diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-09-11 04:34:46 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2016-09-11 04:34:46 -0300 |
commit | 863981e96738983919de841ec669e157e6bdaeb0 (patch) | |
tree | d6d89a12e7eb8017837c057935a2271290907f76 /fs/cifs | |
parent | 8dec7c70575785729a6a9e6719a955e9c545bcab (diff) |
Linux-libre 4.7.1-gnupck-4.7.1-gnu
Diffstat (limited to 'fs/cifs')
-rw-r--r-- | fs/cifs/Makefile | 3 | ||||
-rw-r--r-- | fs/cifs/cifs_dfs_ref.c | 8 | ||||
-rw-r--r-- | fs/cifs/cifs_spnego.c | 67 | ||||
-rw-r--r-- | fs/cifs/cifsacl.c | 2 | ||||
-rw-r--r-- | fs/cifs/cifsencrypt.c | 97 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 42 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 14 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 3 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 14 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 15 | ||||
-rw-r--r-- | fs/cifs/connect.c | 139 | ||||
-rw-r--r-- | fs/cifs/dir.c | 2 | ||||
-rw-r--r-- | fs/cifs/file.c | 80 | ||||
-rw-r--r-- | fs/cifs/inode.c | 3 | ||||
-rw-r--r-- | fs/cifs/readdir.c | 61 | ||||
-rw-r--r-- | fs/cifs/sess.c | 4 | ||||
-rw-r--r-- | fs/cifs/smb2transport.c | 107 | ||||
-rw-r--r-- | fs/cifs/transport.c | 141 | ||||
-rw-r--r-- | fs/cifs/xattr.c | 387 |
19 files changed, 484 insertions, 705 deletions
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 1964d212a..eed7eb09f 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -5,9 +5,10 @@ obj-$(CONFIG_CIFS) += cifs.o cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ link.o misc.o netmisc.o smbencrypt.o transport.o asn1.o \ - cifs_unicode.o nterr.o xattr.o cifsencrypt.o \ + cifs_unicode.o nterr.o cifsencrypt.o \ readdir.o ioctl.o sess.o export.o smb1ops.o winucase.o +cifs-$(CONFIG_CIFS_XATTR) += xattr.o cifs-$(CONFIG_CIFS_ACL) += cifsacl.o cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index e956cba94..ec9dbbcca 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -151,8 +151,12 @@ char *cifs_compose_mount_options(const char *sb_mountdata, if (sb_mountdata == NULL) return ERR_PTR(-EINVAL); - if (strlen(fullpath) - ref->path_consumed) + if (strlen(fullpath) - ref->path_consumed) { prepath = fullpath + ref->path_consumed; + /* skip initial delimiter */ + if (*prepath == '/' || *prepath == '\\') + prepath++; + } *devname = cifs_build_devname(ref->node_name, prepath); if (IS_ERR(*devname)) { @@ -302,7 +306,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) if (full_path == NULL) goto cdda_exit; - cifs_sb = CIFS_SB(d_inode(mntpt)->i_sb); + cifs_sb = CIFS_SB(mntpt->d_sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { mnt = ERR_CAST(tlink); diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 6908080e9..b611fc2e8 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -24,10 +24,13 @@ #include <linux/string.h> #include <keys/user-type.h> #include <linux/key-type.h> +#include <linux/keyctl.h> #include <linux/inet.h> #include "cifsglob.h" #include "cifs_spnego.h" #include "cifs_debug.h" +#include "cifsproto.h" +static const struct cred *spnego_cred; /* create a new cifs key */ static int @@ -102,6 +105,7 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) size_t desc_len; struct key *spnego_key; const char *hostname = server->hostname; + const struct cred *saved_cred; /* length of fields (with semicolons): ver=0xyz ip4=ipaddress host=hostname sec=mechanism uid=0xFF user=username */ @@ -163,7 +167,9 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) sprintf(dp, ";pid=0x%x", current->pid); cifs_dbg(FYI, "key description = %s\n", description); + saved_cred = override_creds(spnego_cred); spnego_key = request_key(&cifs_spnego_key_type, description, ""); + revert_creds(saved_cred); #ifdef CONFIG_CIFS_DEBUG2 if (cifsFYI && !IS_ERR(spnego_key)) { @@ -177,3 +183,64 @@ out: kfree(description); return spnego_key; } + +int +init_cifs_spnego(void) +{ + struct cred *cred; + struct key *keyring; + int ret; + + cifs_dbg(FYI, "Registering the %s key type\n", + cifs_spnego_key_type.name); + + /* + * Create an override credential set with special thread keyring for + * spnego upcalls. + */ + + cred = prepare_kernel_cred(NULL); + if (!cred) + return -ENOMEM; + + keyring = keyring_alloc(".cifs_spnego", + GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); + if (IS_ERR(keyring)) { + ret = PTR_ERR(keyring); + goto failed_put_cred; + } + + ret = register_key_type(&cifs_spnego_key_type); + if (ret < 0) + goto failed_put_key; + + /* + * instruct request_key() to use this special keyring as a cache for + * the results it looks up + */ + set_bit(KEY_FLAG_ROOT_CAN_CLEAR, &keyring->flags); + cred->thread_keyring = keyring; + cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; + spnego_cred = cred; + + cifs_dbg(FYI, "cifs spnego keyring: %d\n", key_serial(keyring)); + return 0; + +failed_put_key: + key_put(keyring); +failed_put_cred: + put_cred(cred); + return ret; +} + +void +exit_cifs_spnego(void) +{ + key_revoke(spnego_cred->thread_keyring); + unregister_key_type(&cifs_spnego_key_type); + put_cred(spnego_cred); + cifs_dbg(FYI, "Unregistered %s key type\n", cifs_spnego_key_type.name); +} diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 3f9312591..71e8a56e9 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -360,7 +360,7 @@ init_cifs_idmap(void) GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, cred, (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ, - KEY_ALLOC_NOT_IN_QUOTA, NULL); + KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL); if (IS_ERR(keyring)) { ret = PTR_ERR(keyring); goto failed_put_cred; diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 4897dacf8..6aeb8d461 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -66,45 +66,15 @@ cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server) return 0; } -/* - * Calculate and return the CIFS signature based on the mac key and SMB PDU. - * The 16 byte signature must be allocated by the caller. Note we only use the - * 1st eight bytes and that the smb header signature field on input contains - * the sequence number before this function is called. Also, this function - * should be called with the server->srv_mutex held. - */ -static int cifs_calc_signature(struct smb_rqst *rqst, - struct TCP_Server_Info *server, char *signature) +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash) { int i; int rc; struct kvec *iov = rqst->rq_iov; int n_vec = rqst->rq_nvec; - if (iov == NULL || signature == NULL || server == NULL) - return -EINVAL; - - if (!server->secmech.sdescmd5) { - rc = cifs_crypto_shash_md5_allocate(server); - if (rc) { - cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__); - return -1; - } - } - - rc = crypto_shash_init(&server->secmech.sdescmd5->shash); - if (rc) { - cifs_dbg(VFS, "%s: Could not init md5\n", __func__); - return rc; - } - - rc = crypto_shash_update(&server->secmech.sdescmd5->shash, - server->session_key.response, server->session_key.len); - if (rc) { - cifs_dbg(VFS, "%s: Could not update with response\n", __func__); - return rc; - } - for (i = 0; i < n_vec; i++) { if (iov[i].iov_len == 0) continue; @@ -117,12 +87,10 @@ static int cifs_calc_signature(struct smb_rqst *rqst, if (i == 0) { if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ - rc = - crypto_shash_update(&server->secmech.sdescmd5->shash, + rc = crypto_shash_update(shash, iov[i].iov_base + 4, iov[i].iov_len - 4); } else { - rc = - crypto_shash_update(&server->secmech.sdescmd5->shash, + rc = crypto_shash_update(shash, iov[i].iov_base, iov[i].iov_len); } if (rc) { @@ -134,21 +102,64 @@ static int cifs_calc_signature(struct smb_rqst *rqst, /* now hash over the rq_pages array */ for (i = 0; i < rqst->rq_npages; i++) { - struct kvec p_iov; + void *kaddr = kmap(rqst->rq_pages[i]); + size_t len = rqst->rq_pagesz; + + if (i == rqst->rq_npages - 1) + len = rqst->rq_tailsz; + + crypto_shash_update(shash, kaddr, len); - cifs_rqst_page_to_kvec(rqst, i, &p_iov); - crypto_shash_update(&server->secmech.sdescmd5->shash, - p_iov.iov_base, p_iov.iov_len); kunmap(rqst->rq_pages[i]); } - rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature); + rc = crypto_shash_final(shash, signature); if (rc) - cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); + cifs_dbg(VFS, "%s: Could not generate hash\n", __func__); return rc; } +/* + * Calculate and return the CIFS signature based on the mac key and SMB PDU. + * The 16 byte signature must be allocated by the caller. Note we only use the + * 1st eight bytes and that the smb header signature field on input contains + * the sequence number before this function is called. Also, this function + * should be called with the server->srv_mutex held. + */ +static int cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature) +{ + int rc; + + if (!rqst->rq_iov || !signature || !server) + return -EINVAL; + + if (!server->secmech.sdescmd5) { + rc = cifs_crypto_shash_md5_allocate(server); + if (rc) { + cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__); + return -1; + } + } + + rc = crypto_shash_init(&server->secmech.sdescmd5->shash); + if (rc) { + cifs_dbg(VFS, "%s: Could not init md5\n", __func__); + return rc; + } + + rc = crypto_shash_update(&server->secmech.sdescmd5->shash, + server->session_key.response, server->session_key.len); + if (rc) { + cifs_dbg(VFS, "%s: Could not update with response\n", __func__); + return rc; + } + + return __cifs_calc_signature(rqst, server, signature, + &server->secmech.sdescmd5->shash); +} + /* must be called with server->srv_mutex held */ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, __u32 *pexpected_response_sequence_number) diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 89201564c..5d841f39c 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -37,6 +37,7 @@ #include <linux/freezer.h> #include <linux/namei.h> #include <linux/random.h> +#include <linux/xattr.h> #include <net/ipv6.h> #include "cifsfs.h" #include "cifspdu.h" @@ -86,6 +87,7 @@ extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_mid_poolp; struct workqueue_struct *cifsiod_wq; +__u32 cifs_lock_secret; /* * Bumps refcount for cifs super block. @@ -135,6 +137,7 @@ cifs_read_super(struct super_block *sb) sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_op = &cifs_super_ops; + sb->s_xattr = cifs_xattr_handlers; sb->s_bdi = &cifs_sb->bdi; sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ @@ -888,44 +891,33 @@ const struct inode_operations cifs_dir_inode_ops = { .rmdir = cifs_rmdir, .rename2 = cifs_rename2, .permission = cifs_permission, -/* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, .symlink = cifs_symlink, .mknod = cifs_mknod, -#ifdef CONFIG_CIFS_XATTR - .setxattr = cifs_setxattr, - .getxattr = cifs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = cifs_listxattr, - .removexattr = cifs_removexattr, -#endif + .removexattr = generic_removexattr, }; const struct inode_operations cifs_file_inode_ops = { -/* revalidate:cifs_revalidate, */ .setattr = cifs_setattr, - .getattr = cifs_getattr, /* do we need this anymore? */ + .getattr = cifs_getattr, .permission = cifs_permission, -#ifdef CONFIG_CIFS_XATTR - .setxattr = cifs_setxattr, - .getxattr = cifs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = cifs_listxattr, - .removexattr = cifs_removexattr, -#endif + .removexattr = generic_removexattr, }; const struct inode_operations cifs_symlink_inode_ops = { .readlink = generic_readlink, .get_link = cifs_get_link, .permission = cifs_permission, - /* BB add the following two eventually */ - /* revalidate: cifs_revalidate, - setattr: cifs_notify_change, *//* BB do we need notify change */ -#ifdef CONFIG_CIFS_XATTR - .setxattr = cifs_setxattr, - .getxattr = cifs_getxattr, + .setxattr = generic_setxattr, + .getxattr = generic_getxattr, .listxattr = cifs_listxattr, - .removexattr = cifs_removexattr, -#endif + .removexattr = generic_removexattr, }; static int cifs_clone_file_range(struct file *src_file, loff_t off, @@ -1083,7 +1075,7 @@ const struct file_operations cifs_file_direct_nobrl_ops = { }; const struct file_operations cifs_dir_ops = { - .iterate = cifs_readdir, + .iterate_shared = cifs_readdir, .release = cifs_closedir, .read = generic_read_dir, .unlocked_ioctl = cifs_ioctl, @@ -1275,6 +1267,8 @@ init_cifs(void) spin_lock_init(&cifs_file_list_lock); spin_lock_init(&GlobalMid_Lock); + get_random_bytes(&cifs_lock_secret, sizeof(cifs_lock_secret)); + if (cifs_max_pending < 2) { cifs_max_pending = 2; cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); @@ -1307,7 +1301,7 @@ init_cifs(void) goto out_destroy_mids; #ifdef CONFIG_CIFS_UPCALL - rc = register_key_type(&cifs_spnego_key_type); + rc = init_cifs_spnego(); if (rc) goto out_destroy_request_bufs; #endif /* CONFIG_CIFS_UPCALL */ @@ -1330,7 +1324,7 @@ out_init_cifs_idmap: out_register_key_type: #endif #ifdef CONFIG_CIFS_UPCALL - unregister_key_type(&cifs_spnego_key_type); + exit_cifs_spnego(); out_destroy_request_bufs: #endif cifs_destroy_request_bufs(); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 83aac8ba5..9dcf974ac 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -120,15 +120,19 @@ extern const char *cifs_get_link(struct dentry *, struct inode *, struct delayed_call *); extern int cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname); -extern int cifs_removexattr(struct dentry *, const char *); -extern int cifs_setxattr(struct dentry *, const char *, const void *, - size_t, int); -extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t); + +#ifdef CONFIG_CIFS_XATTR +extern const struct xattr_handler *cifs_xattr_handlers[]; extern ssize_t cifs_listxattr(struct dentry *, char *, size_t); +#else +# define cifs_xattr_handlers NULL +# define cifs_listxattr NULL +#endif + extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); #ifdef CONFIG_CIFS_NFSD_EXPORT extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.08" +#define CIFS_VERSION "2.09" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index f2cc0b3d1..8f1d8c1e7 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -615,8 +615,6 @@ struct TCP_Server_Info { bool sec_mskerberos; /* supports legacy MS Kerberos */ bool large_buf; /* is current buffer large? */ struct delayed_work echo; /* echo ping workqueue job */ - struct kvec *iov; /* reusable kvec array for receives */ - unsigned int nr_iov; /* number of kvecs in array */ char *smallbuf; /* pointer to current "small" buffer */ char *bigbuf; /* pointer to current "big" buffer */ unsigned int total_read; /* total amount of data read in this pass */ @@ -1621,6 +1619,7 @@ void cifs_oplock_break(struct work_struct *work); extern const struct slow_work_ops cifs_oplock_break_ops; extern struct workqueue_struct *cifsiod_wq; +extern __u32 cifs_lock_secret; extern mempool_t *cifs_mid_poolp; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index eed7ff50f..1243bd326 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -37,8 +37,6 @@ extern void cifs_buf_release(void *); extern struct smb_hdr *cifs_small_buf_get(void); extern void cifs_small_buf_release(void *); extern void free_rsp_buf(int, void *); -extern void cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx, - struct kvec *iov); extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *, unsigned int /* length */); extern unsigned int _get_xid(void); @@ -60,6 +58,8 @@ do { \ } while (0) extern int init_cifs_idmap(void); extern void exit_cifs_idmap(void); +extern int init_cifs_spnego(void); +extern void exit_cifs_spnego(void); extern char *build_path_from_dentry(struct dentry *); extern char *cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb, @@ -181,10 +181,9 @@ extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *, extern void dequeue_mid(struct mid_q_entry *mid, bool malformed); extern int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, - unsigned int to_read); -extern int cifs_readv_from_socket(struct TCP_Server_Info *server, - struct kvec *iov_orig, unsigned int nr_segs, - unsigned int to_read); + unsigned int to_read); +extern int cifs_read_page_from_socket(struct TCP_Server_Info *server, + struct page *page, unsigned int to_read); extern void cifs_setup_cifs_sb(struct smb_vol *pvolume_info, struct cifs_sb_info *cifs_sb); extern int cifs_match_super(struct super_block *, void *); @@ -512,4 +511,7 @@ int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const unsigned char *path, char *pbuf, unsigned int *pbytes_written); +int __cifs_calc_signature(struct smb_rqst *rqst, + struct TCP_Server_Info *server, char *signature, + struct shash_desc *shash); #endif /* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a894bf809..d47197ea4 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -1447,10 +1447,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) len = min_t(unsigned int, buflen, server->vals->read_rsp_size) - HEADER_SIZE(server) + 1; - rdata->iov.iov_base = buf + HEADER_SIZE(server) - 1; - rdata->iov.iov_len = len; - - length = cifs_readv_from_socket(server, &rdata->iov, 1, len); + length = cifs_read_from_socket(server, + buf + HEADER_SIZE(server) - 1, len); if (length < 0) return length; server->total_read += length; @@ -1502,9 +1500,8 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) len = data_offset - server->total_read; if (len > 0) { /* read any junk before data into the rest of smallbuf */ - rdata->iov.iov_base = buf + server->total_read; - rdata->iov.iov_len = len; - length = cifs_readv_from_socket(server, &rdata->iov, 1, len); + length = cifs_read_from_socket(server, + buf + server->total_read, len); if (length < 0) return length; server->total_read += length; @@ -3366,7 +3363,7 @@ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, if (le16_to_cpu(cifs_acl->version) != CIFS_ACL_VERSION) return -EOPNOTSUPP; - if (acl_type & ACL_TYPE_ACCESS) { + if (acl_type == ACL_TYPE_ACCESS) { count = le16_to_cpu(cifs_acl->access_entry_count); pACE = &cifs_acl->ace_array[0]; size = sizeof(struct cifs_posix_acl); @@ -3377,7 +3374,7 @@ static int cifs_copy_posix_acl(char *trgt, char *src, const int buflen, size_of_data_area, size); return -EINVAL; } - } else if (acl_type & ACL_TYPE_DEFAULT) { + } else if (acl_type == ACL_TYPE_DEFAULT) { count = le16_to_cpu(cifs_acl->access_entry_count); size = sizeof(struct cifs_posix_acl); size += sizeof(struct cifs_posix_ace) * count; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 34cbc582e..7d2b15c06 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -503,99 +503,34 @@ server_unresponsive(struct TCP_Server_Info *server) return false; } -/* - * kvec_array_init - clone a kvec array, and advance into it - * @new: pointer to memory for cloned array - * @iov: pointer to original array - * @nr_segs: number of members in original array - * @bytes: number of bytes to advance into the cloned array - * - * This function will copy the array provided in iov to a section of memory - * and advance the specified number of bytes into the new array. It returns - * the number of segments in the new array. "new" must be at least as big as - * the original iov array. - */ -static unsigned int -kvec_array_init(struct kvec *new, struct kvec *iov, unsigned int nr_segs, - size_t bytes) -{ - size_t base = 0; - - while (bytes || !iov->iov_len) { - int copy = min(bytes, iov->iov_len); - - bytes -= copy; - base += copy; - if (iov->iov_len == base) { - iov++; - nr_segs--; - base = 0; - } - } - memcpy(new, iov, sizeof(*iov) * nr_segs); - new->iov_base += base; - new->iov_len -= base; - return nr_segs; -} - -static struct kvec * -get_server_iovec(struct TCP_Server_Info *server, unsigned int nr_segs) -{ - struct kvec *new_iov; - - if (server->iov && nr_segs <= server->nr_iov) - return server->iov; - - /* not big enough -- allocate a new one and release the old */ - new_iov = kmalloc(sizeof(*new_iov) * nr_segs, GFP_NOFS); - if (new_iov) { - kfree(server->iov); - server->iov = new_iov; - server->nr_iov = nr_segs; - } - return new_iov; -} - -int -cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig, - unsigned int nr_segs, unsigned int to_read) +static int +cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) { int length = 0; int total_read; - unsigned int segs; - struct msghdr smb_msg; - struct kvec *iov; - iov = get_server_iovec(server, nr_segs); - if (!iov) - return -ENOMEM; - - smb_msg.msg_control = NULL; - smb_msg.msg_controllen = 0; + smb_msg->msg_control = NULL; + smb_msg->msg_controllen = 0; - for (total_read = 0; to_read; total_read += length, to_read -= length) { + for (total_read = 0; msg_data_left(smb_msg); total_read += length) { try_to_freeze(); - if (server_unresponsive(server)) { - total_read = -ECONNABORTED; - break; - } + if (server_unresponsive(server)) + return -ECONNABORTED; - segs = kvec_array_init(iov, iov_orig, nr_segs, total_read); + length = sock_recvmsg(server->ssocket, smb_msg, 0); - length = kernel_recvmsg(server->ssocket, &smb_msg, - iov, segs, to_read, 0); + if (server->tcpStatus == CifsExiting) + return -ESHUTDOWN; - if (server->tcpStatus == CifsExiting) { - total_read = -ESHUTDOWN; - break; - } else if (server->tcpStatus == CifsNeedReconnect) { + if (server->tcpStatus == CifsNeedReconnect) { cifs_reconnect(server); - total_read = -ECONNABORTED; - break; - } else if (length == -ERESTARTSYS || - length == -EAGAIN || - length == -EINTR) { + return -ECONNABORTED; + } + + if (length == -ERESTARTSYS || + length == -EAGAIN || + length == -EINTR) { /* * Minimum sleep to prevent looping, allowing socket * to clear and app threads to set tcpStatus @@ -604,12 +539,12 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct kvec *iov_orig, usleep_range(1000, 2000); length = 0; continue; - } else if (length <= 0) { - cifs_dbg(FYI, "Received no data or error: expecting %d\n" - "got %d", to_read, length); + } + + if (length <= 0) { + cifs_dbg(FYI, "Received no data or error: %d\n", length); cifs_reconnect(server); - total_read = -ECONNABORTED; - break; + return -ECONNABORTED; } } return total_read; @@ -619,12 +554,21 @@ int cifs_read_from_socket(struct TCP_Server_Info *server, char *buf, unsigned int to_read) { - struct kvec iov; + struct msghdr smb_msg; + struct kvec iov = {.iov_base = buf, .iov_len = to_read}; + iov_iter_kvec(&smb_msg.msg_iter, READ | ITER_KVEC, &iov, 1, to_read); - iov.iov_base = buf; - iov.iov_len = to_read; + return cifs_readv_from_socket(server, &smb_msg); +} - return cifs_readv_from_socket(server, &iov, 1, to_read); +int +cifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page, + unsigned int to_read) +{ + struct msghdr smb_msg; + struct bio_vec bv = {.bv_page = page, .bv_len = to_read}; + iov_iter_bvec(&smb_msg.msg_iter, READ | ITER_BVEC, &bv, 1, to_read); + return cifs_readv_from_socket(server, &smb_msg); } static bool @@ -785,7 +729,6 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) } kfree(server->hostname); - kfree(server->iov); kfree(server); length = atomic_dec_return(&tcpSesAllocCount); @@ -1198,8 +1141,12 @@ cifs_parse_devname(const char *devname, struct smb_vol *vol) convert_delimiter(vol->UNC, '\\'); - /* If pos is NULL, or is a bogus trailing delimiter then no prepath */ - if (!*pos++ || !*pos) + /* skip any delimiter */ + if (*pos == '/' || *pos == '\\') + pos++; + + /* If pos is NULL then no prepath */ + if (!*pos) return 0; vol->prepath = kstrdup(pos, GFP_KERNEL); @@ -2920,7 +2867,7 @@ static inline void cifs_reclassify_socket4(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); + BUG_ON(!sock_allow_reclassification(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS", &cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]); } @@ -2929,7 +2876,7 @@ static inline void cifs_reclassify_socket6(struct socket *sock) { struct sock *sk = sock->sk; - BUG_ON(sock_owned_by_user(sk)); + BUG_ON(!sock_allow_reclassification(sk)); sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS", &cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]); } diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index c3eb998a9..fb0903fff 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -445,7 +445,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry, * Check for hashed negative dentry. We have already revalidated * the dentry and it is fine. No need to perform another lookup. */ - if (!d_unhashed(direntry)) + if (!d_in_lookup(direntry)) return -ENOENT; res = cifs_lookup(inode, direntry, 0); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c03d07446..d4890b6dc 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -271,7 +271,7 @@ struct cifsFileInfo * cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, struct tcon_link *tlink, __u32 oplock) { - struct dentry *dentry = file->f_path.dentry; + struct dentry *dentry = file_dentry(file); struct inode *inode = d_inode(dentry); struct cifsInodeInfo *cinode = CIFS_I(inode); struct cifsFileInfo *cfile; @@ -461,7 +461,7 @@ int cifs_open(struct inode *inode, struct file *file) tcon = tlink_tcon(tlink); server = tcon->ses->server; - full_path = build_path_from_dentry(file->f_path.dentry); + full_path = build_path_from_dentry(file_dentry(file)); if (full_path == NULL) { rc = -ENOMEM; goto out; @@ -1112,6 +1112,12 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile) return rc; } +static __u32 +hash_lockowner(fl_owner_t owner) +{ + return cifs_lock_secret ^ hash32_ptr((const void *)owner); +} + struct lock_to_push { struct list_head llist; __u64 offset; @@ -1178,7 +1184,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile) else type = CIFS_WRLCK; lck = list_entry(el, struct lock_to_push, llist); - lck->pid = flock->fl_pid; + lck->pid = hash_lockowner(flock->fl_owner); lck->netfid = cfile->fid.netfid; lck->length = length; lck->type = type; @@ -1305,7 +1311,8 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type, posix_lock_type = CIFS_RDLCK; else posix_lock_type = CIFS_WRLCK; - rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid, + rc = CIFSSMBPosixLock(xid, tcon, netfid, + hash_lockowner(flock->fl_owner), flock->fl_start, length, flock, posix_lock_type, wait_flag); return rc; @@ -1505,7 +1512,8 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, posix_lock_type = CIFS_UNLCK; rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid, - current->tgid, flock->fl_start, length, + hash_lockowner(flock->fl_owner), + flock->fl_start, length, NULL, posix_lock_type, wait_flag); goto out; } @@ -2687,11 +2695,8 @@ cifs_writev(struct kiocb *iocb, struct iov_iter *from) out: inode_unlock(inode); - if (rc > 0) { - ssize_t err = generic_write_sync(file, iocb->ki_pos - rc, rc); - if (err < 0) - rc = err; - } + if (rc > 0) + rc = generic_write_sync(iocb, rc); up_read(&cinode->lock_sem); return rc; } @@ -2855,39 +2860,31 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server, int result = 0; unsigned int i; unsigned int nr_pages = rdata->nr_pages; - struct kvec iov; rdata->got_bytes = 0; rdata->tailsz = PAGE_SIZE; for (i = 0; i < nr_pages; i++) { struct page *page = rdata->pages[i]; + size_t n; - if (len >= PAGE_SIZE) { - /* enough data to fill the page */ - iov.iov_base = kmap(page); - iov.iov_len = PAGE_SIZE; - cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n", - i, iov.iov_base, iov.iov_len); - len -= PAGE_SIZE; - } else if (len > 0) { - /* enough for partial page, fill and zero the rest */ - iov.iov_base = kmap(page); - iov.iov_len = len; - cifs_dbg(FYI, "%u: iov_base=%p iov_len=%zu\n", - i, iov.iov_base, iov.iov_len); - memset(iov.iov_base + len, '\0', PAGE_SIZE - len); - rdata->tailsz = len; - len = 0; - } else { + if (len <= 0) { /* no need to hold page hostage */ rdata->pages[i] = NULL; rdata->nr_pages--; put_page(page); continue; } - - result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len); - kunmap(page); + n = len; + if (len >= PAGE_SIZE) { + /* enough data to fill the page */ + n = PAGE_SIZE; + len -= n; + } else { + zero_user(page, len, PAGE_SIZE - len); + rdata->tailsz = len; + len = 0; + } + result = cifs_read_page_from_socket(server, page, n); if (result < 0) break; @@ -3303,7 +3300,6 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server, u64 eof; pgoff_t eof_index; unsigned int nr_pages = rdata->nr_pages; - struct kvec iov; /* determine the eof that the server (probably) has */ eof = CIFS_I(rdata->mapping->host)->server_eof; @@ -3314,23 +3310,14 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server, rdata->tailsz = PAGE_SIZE; for (i = 0; i < nr_pages; i++) { struct page *page = rdata->pages[i]; + size_t n = PAGE_SIZE; if (len >= PAGE_SIZE) { - /* enough data to fill the page */ - iov.iov_base = kmap(page); - iov.iov_len = PAGE_SIZE; - cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n", - i, page->index, iov.iov_base, iov.iov_len); len -= PAGE_SIZE; } else if (len > 0) { /* enough for partial page, fill and zero the rest */ - iov.iov_base = kmap(page); - iov.iov_len = len; - cifs_dbg(FYI, "%u: idx=%lu iov_base=%p iov_len=%zu\n", - i, page->index, iov.iov_base, iov.iov_len); - memset(iov.iov_base + len, - '\0', PAGE_SIZE - len); - rdata->tailsz = len; + zero_user(page, len, PAGE_SIZE - len); + n = rdata->tailsz = len; len = 0; } else if (page->index > eof_index) { /* @@ -3360,8 +3347,7 @@ cifs_readpages_read_into_pages(struct TCP_Server_Info *server, continue; } - result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len); - kunmap(page); + result = cifs_read_page_from_socket(server, page, n); if (result < 0) break; @@ -3854,7 +3840,7 @@ void cifs_oplock_break(struct work_struct *work) * Direct IO is not yet supported in the cached mode. */ static ssize_t -cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter, loff_t pos) +cifs_direct_io(struct kiocb *iocb, struct iov_iter *iter) { /* * FIXME diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5f9ad5c42..514dadb05 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2418,8 +2418,7 @@ cifs_setattr_exit: int cifs_setattr(struct dentry *direntry, struct iattr *attrs) { - struct inode *inode = d_inode(direntry); - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb); if (pTcon->unix_ext) diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index b30a4a6d9..65cf85dcd 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -78,20 +78,34 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, { struct dentry *dentry, *alias; struct inode *inode; - struct super_block *sb = d_inode(parent)->i_sb; + struct super_block *sb = parent->d_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); cifs_dbg(FYI, "%s: for %s\n", __func__, name->name); dentry = d_hash_and_lookup(parent, name); + if (!dentry) { + /* + * If we know that the inode will need to be revalidated + * immediately, then don't create a new dentry for it. + * We'll end up doing an on the wire call either way and + * this spares us an invalidation. + */ + if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) + return; +retry: + dentry = d_alloc_parallel(parent, name, &wq); + } if (IS_ERR(dentry)) return; - - if (dentry) { + if (!d_in_lookup(dentry)) { inode = d_inode(dentry); if (inode) { - if (d_mountpoint(dentry)) - goto out; + if (d_mountpoint(dentry)) { + dput(dentry); + return; + } /* * If we're generating inode numbers, then we don't * want to clobber the existing one with the one that @@ -106,33 +120,22 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, (inode->i_mode & S_IFMT) == (fattr->cf_mode & S_IFMT)) { cifs_fattr_to_inode(inode, fattr); - goto out; + dput(dentry); + return; } } d_invalidate(dentry); dput(dentry); + goto retry; + } else { + inode = cifs_iget(sb, fattr); + if (!inode) + inode = ERR_PTR(-ENOMEM); + alias = d_splice_alias(inode, dentry); + d_lookup_done(dentry); + if (alias && !IS_ERR(alias)) + dput(alias); } - - /* - * If we know that the inode will need to be revalidated immediately, - * then don't create a new dentry for it. We'll end up doing an on - * the wire call either way and this spares us an invalidation. - */ - if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL) - return; - - dentry = d_alloc(parent, name); - if (!dentry) - return; - - inode = cifs_iget(sb, fattr); - if (!inode) - goto out; - - alias = d_splice_alias(inode, dentry); - if (alias && !IS_ERR(alias)) - dput(alias); -out: dput(dentry); } @@ -300,7 +303,7 @@ initiate_cifs_search(const unsigned int xid, struct file *file) cifsFile->invalidHandle = true; cifsFile->srch_inf.endOfSearch = false; - full_path = build_path_from_dentry(file->f_path.dentry); + full_path = build_path_from_dentry(file_dentry(file)); if (full_path == NULL) { rc = -ENOMEM; goto error_exit; @@ -759,7 +762,7 @@ static int cifs_filldir(char *find_entry, struct file *file, */ fattr.cf_flags |= CIFS_FATTR_NEED_REVAL; - cifs_prime_dcache(file->f_path.dentry, &name, &fattr); + cifs_prime_dcache(file_dentry(file), &name, &fattr); ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype); diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index e88ffe1da..538d9b556 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -450,7 +450,7 @@ int build_ntlmssp_auth_blob(unsigned char **pbuffer, } else { int len; len = cifs_strtoUTF16((__le16 *)tmp, ses->domainName, - CIFS_MAX_USERNAME_LEN, nls_cp); + CIFS_MAX_DOMAINNAME_LEN, nls_cp); len *= 2; /* unicode is 2 bytes each */ sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - *pbuffer); sec_blob->DomainName.Length = cpu_to_le16(len); @@ -710,6 +710,8 @@ sess_auth_lanman(struct sess_data *sess_data) rc = calc_lanman_hash(ses->password, ses->server->cryptkey, ses->server->sec_mode & SECMODE_PW_ENCRYPT ? true : false, lnm_session_key); + if (rc) + goto out; memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); bcc_ptr += CIFS_AUTH_RESP_SIZE; diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 8732a43b1..bc9a7b634 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -135,11 +135,10 @@ smb2_find_smb_ses(struct smb2_hdr *smb2hdr, struct TCP_Server_Info *server) int smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { - int i, rc; + int rc; unsigned char smb2_signature[SMB2_HMACSHA256_SIZE]; unsigned char *sigptr = smb2_signature; struct kvec *iov = rqst->rq_iov; - int n_vec = rqst->rq_nvec; struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; struct cifs_ses *ses; @@ -171,53 +170,11 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) return rc; } - for (i = 0; i < n_vec; i++) { - if (iov[i].iov_len == 0) - continue; - if (iov[i].iov_base == NULL) { - cifs_dbg(VFS, "null iovec entry\n"); - return -EIO; - } - /* - * The first entry includes a length field (which does not get - * signed that occupies the first 4 bytes before the header). - */ - if (i == 0) { - if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ - break; /* nothing to sign or corrupt header */ - rc = - crypto_shash_update( - &server->secmech.sdeschmacsha256->shash, - iov[i].iov_base + 4, iov[i].iov_len - 4); - } else { - rc = - crypto_shash_update( - &server->secmech.sdeschmacsha256->shash, - iov[i].iov_base, iov[i].iov_len); - } - if (rc) { - cifs_dbg(VFS, "%s: Could not update with payload\n", - __func__); - return rc; - } - } - - /* now hash over the rq_pages array */ - for (i = 0; i < rqst->rq_npages; i++) { - struct kvec p_iov; - - cifs_rqst_page_to_kvec(rqst, i, &p_iov); - crypto_shash_update(&server->secmech.sdeschmacsha256->shash, - p_iov.iov_base, p_iov.iov_len); - kunmap(rqst->rq_pages[i]); - } - - rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash, - sigptr); - if (rc) - cifs_dbg(VFS, "%s: Could not generate sha256 hash\n", __func__); + rc = __cifs_calc_signature(rqst, server, sigptr, + &server->secmech.sdeschmacsha256->shash); - memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + if (!rc) + memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); return rc; } @@ -395,12 +352,10 @@ generate_smb311signingkey(struct cifs_ses *ses) int smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) { - int i; int rc = 0; unsigned char smb3_signature[SMB2_CMACAES_SIZE]; unsigned char *sigptr = smb3_signature; struct kvec *iov = rqst->rq_iov; - int n_vec = rqst->rq_nvec; struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base; struct cifs_ses *ses; @@ -431,54 +386,12 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); return rc; } + + rc = __cifs_calc_signature(rqst, server, sigptr, + &server->secmech.sdesccmacaes->shash); - for (i = 0; i < n_vec; i++) { - if (iov[i].iov_len == 0) - continue; - if (iov[i].iov_base == NULL) { - cifs_dbg(VFS, "null iovec entry"); - return -EIO; - } - /* - * The first entry includes a length field (which does not get - * signed that occupies the first 4 bytes before the header). - */ - if (i == 0) { - if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ - break; /* nothing to sign or corrupt header */ - rc = - crypto_shash_update( - &server->secmech.sdesccmacaes->shash, - iov[i].iov_base + 4, iov[i].iov_len - 4); - } else { - rc = - crypto_shash_update( - &server->secmech.sdesccmacaes->shash, - iov[i].iov_base, iov[i].iov_len); - } - if (rc) { - cifs_dbg(VFS, "%s: Couldn't update cmac aes with payload\n", - __func__); - return rc; - } - } - - /* now hash over the rq_pages array */ - for (i = 0; i < rqst->rq_npages; i++) { - struct kvec p_iov; - - cifs_rqst_page_to_kvec(rqst, i, &p_iov); - crypto_shash_update(&server->secmech.sdesccmacaes->shash, - p_iov.iov_base, p_iov.iov_len); - kunmap(rqst->rq_pages[i]); - } - - rc = crypto_shash_final(&server->secmech.sdesccmacaes->shash, - sigptr); - if (rc) - cifs_dbg(VFS, "%s: Could not generate cmac aes\n", __func__); - - memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); + if (!rc) + memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE); return rc; } diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 87abe8ed0..206a597b2 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -124,41 +124,32 @@ cifs_delete_mid(struct mid_q_entry *mid) /* * smb_send_kvec - send an array of kvecs to the server * @server: Server to send the data to - * @iov: Pointer to array of kvecs - * @n_vec: length of kvec array + * @smb_msg: Message to send * @sent: amount of data sent on socket is stored here * * Our basic "send data to server" function. Should be called with srv_mutex * held. The caller is responsible for handling the results. */ static int -smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec, - size_t *sent) +smb_send_kvec(struct TCP_Server_Info *server, struct msghdr *smb_msg, + size_t *sent) { int rc = 0; - int i = 0; - struct msghdr smb_msg; - unsigned int remaining; - size_t first_vec = 0; + int retries = 0; struct socket *ssocket = server->ssocket; *sent = 0; - smb_msg.msg_name = (struct sockaddr *) &server->dstaddr; - smb_msg.msg_namelen = sizeof(struct sockaddr); - smb_msg.msg_control = NULL; - smb_msg.msg_controllen = 0; + smb_msg->msg_name = (struct sockaddr *) &server->dstaddr; + smb_msg->msg_namelen = sizeof(struct sockaddr); + smb_msg->msg_control = NULL; + smb_msg->msg_controllen = 0; if (server->noblocksnd) - smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; + smb_msg->msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; else - smb_msg.msg_flags = MSG_NOSIGNAL; - - remaining = 0; - for (i = 0; i < n_vec; i++) - remaining += iov[i].iov_len; + smb_msg->msg_flags = MSG_NOSIGNAL; - i = 0; - while (remaining) { + while (msg_data_left(smb_msg)) { /* * If blocking send, we try 3 times, since each can block * for 5 seconds. For nonblocking we have to try more @@ -177,35 +168,21 @@ smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec, * after the retries we will kill the socket and * reconnect which may clear the network problem. */ - rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec], - n_vec - first_vec, remaining); + rc = sock_sendmsg(ssocket, smb_msg); if (rc == -EAGAIN) { - i++; - if (i >= 14 || (!server->noblocksnd && (i > 2))) { + retries++; + if (retries >= 14 || + (!server->noblocksnd && (retries > 2))) { cifs_dbg(VFS, "sends on sock %p stuck for 15 seconds\n", ssocket); - rc = -EAGAIN; - break; + return -EAGAIN; } - msleep(1 << i); + msleep(1 << retries); continue; } if (rc < 0) - break; - - /* send was at least partially successful */ - *sent += rc; - - if (rc == remaining) { - remaining = 0; - break; - } - - if (rc > remaining) { - cifs_dbg(VFS, "sent %d requested %d\n", rc, remaining); - break; - } + return rc; if (rc == 0) { /* should never happen, letting socket clear before @@ -215,59 +192,11 @@ smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec, continue; } - remaining -= rc; - - /* the line below resets i */ - for (i = first_vec; i < n_vec; i++) { - if (iov[i].iov_len) { - if (rc > iov[i].iov_len) { - rc -= iov[i].iov_len; - iov[i].iov_len = 0; - } else { - iov[i].iov_base += rc; - iov[i].iov_len -= rc; - first_vec = i; - break; - } - } - } - - i = 0; /* in case we get ENOSPC on the next send */ - rc = 0; + /* send was at least partially successful */ + *sent += rc; + retries = 0; /* in case we get ENOSPC on the next send */ } - return rc; -} - -/** - * rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec - * @rqst: pointer to smb_rqst - * @idx: index into the array of the page - * @iov: pointer to struct kvec that will hold the result - * - * Helper function to convert a slot in the rqst->rq_pages array into a kvec. - * The page will be kmapped and the address placed into iov_base. The length - * will then be adjusted according to the ptailoff. - */ -void -cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx, - struct kvec *iov) -{ - /* - * FIXME: We could avoid this kmap altogether if we used - * kernel_sendpage instead of kernel_sendmsg. That will only - * work if signing is disabled though as sendpage inlines the - * page directly into the fraglist. If userspace modifies the - * page after we calculate the signature, then the server will - * reject it and may break the connection. kernel_sendmsg does - * an extra copy of the data and avoids that issue. - */ - iov->iov_base = kmap(rqst->rq_pages[idx]); - - /* if last page, don't send beyond this offset into page */ - if (idx == (rqst->rq_npages - 1)) - iov->iov_len = rqst->rq_tailsz; - else - iov->iov_len = rqst->rq_pagesz; + return 0; } static unsigned long @@ -299,8 +228,9 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); unsigned long send_length; unsigned int i; - size_t total_len = 0, sent; + size_t total_len = 0, sent, size; struct socket *ssocket = server->ssocket; + struct msghdr smb_msg; int val = 1; if (ssocket == NULL) @@ -321,7 +251,13 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK, (char *)&val, sizeof(val)); - rc = smb_send_kvec(server, iov, n_vec, &sent); + size = 0; + for (i = 0; i < n_vec; i++) + size += iov[i].iov_len; + + iov_iter_kvec(&smb_msg.msg_iter, WRITE | ITER_KVEC, iov, n_vec, size); + + rc = smb_send_kvec(server, &smb_msg, &sent); if (rc < 0) goto uncork; @@ -329,11 +265,16 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst) /* now walk the page array and send each page in it */ for (i = 0; i < rqst->rq_npages; i++) { - struct kvec p_iov; - - cifs_rqst_page_to_kvec(rqst, i, &p_iov); - rc = smb_send_kvec(server, &p_iov, 1, &sent); - kunmap(rqst->rq_pages[i]); + size_t len = i == rqst->rq_npages - 1 + ? rqst->rq_tailsz + : rqst->rq_pagesz; + struct bio_vec bvec = { + .bv_page = rqst->rq_pages[i], + .bv_len = len + }; + iov_iter_bvec(&smb_msg.msg_iter, WRITE | ITER_BVEC, + &bvec, 1, len); + rc = smb_send_kvec(server, &smb_msg, &sent); if (rc < 0) break; diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index f5dc2f0df..5e23f64c0 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -32,92 +32,25 @@ #include "cifs_unicode.h" #define MAX_EA_VALUE_SIZE 65535 -#define CIFS_XATTR_DOS_ATTRIB "user.DosAttrib" #define CIFS_XATTR_CIFS_ACL "system.cifs_acl" /* BB need to add server (Samba e.g) support for security and trusted prefix */ -int cifs_removexattr(struct dentry *direntry, const char *ea_name) -{ - int rc = -EOPNOTSUPP; -#ifdef CONFIG_CIFS_XATTR - unsigned int xid; - struct cifs_sb_info *cifs_sb; - struct tcon_link *tlink; - struct cifs_tcon *pTcon; - struct super_block *sb; - char *full_path = NULL; - - if (direntry == NULL) - return -EIO; - if (d_really_is_negative(direntry)) - return -EIO; - sb = d_inode(direntry)->i_sb; - if (sb == NULL) - return -EIO; - - cifs_sb = CIFS_SB(sb); - tlink = cifs_sb_tlink(cifs_sb); - if (IS_ERR(tlink)) - return PTR_ERR(tlink); - pTcon = tlink_tcon(tlink); - - xid = get_xid(); - - full_path = build_path_from_dentry(direntry); - if (full_path == NULL) { - rc = -ENOMEM; - goto remove_ea_exit; - } - if (ea_name == NULL) { - cifs_dbg(FYI, "Null xattr names not supported\n"); - } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) - && (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN))) { - cifs_dbg(FYI, - "illegal xattr request %s (only user namespace supported)\n", - ea_name); - /* BB what if no namespace prefix? */ - /* Should we just pass them to server, except for - system and perhaps security prefixes? */ - } else { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto remove_ea_exit; +enum { XATTR_USER, XATTR_CIFS_ACL, XATTR_ACL_ACCESS, XATTR_ACL_DEFAULT }; - ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ - if (pTcon->ses->server->ops->set_EA) - rc = pTcon->ses->server->ops->set_EA(xid, pTcon, - full_path, ea_name, NULL, (__u16)0, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - } -remove_ea_exit: - kfree(full_path); - free_xid(xid); - cifs_put_tlink(tlink); -#endif - return rc; -} - -int cifs_setxattr(struct dentry *direntry, const char *ea_name, - const void *ea_value, size_t value_size, int flags) +static int cifs_xattr_set(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, const void *value, + size_t size, int flags) { int rc = -EOPNOTSUPP; -#ifdef CONFIG_CIFS_XATTR unsigned int xid; - struct cifs_sb_info *cifs_sb; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct tcon_link *tlink; struct cifs_tcon *pTcon; - struct super_block *sb; char *full_path; - if (direntry == NULL) - return -EIO; - if (d_really_is_negative(direntry)) - return -EIO; - sb = d_inode(direntry)->i_sb; - if (sb == NULL) - return -EIO; - - cifs_sb = CIFS_SB(sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); @@ -125,10 +58,10 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, xid = get_xid(); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - goto set_ea_exit; + goto out; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ @@ -136,123 +69,93 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, /* if proc/fs/cifs/streamstoxattr is set then search server for EAs or streams to returns as xattrs */ - if (value_size > MAX_EA_VALUE_SIZE) { + if (size > MAX_EA_VALUE_SIZE) { cifs_dbg(FYI, "size of EA value too large\n"); rc = -EOPNOTSUPP; - goto set_ea_exit; + goto out; } - if (ea_name == NULL) { - cifs_dbg(FYI, "Null xattr names not supported\n"); - } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) - == 0) { + switch (handler->flags) { + case XATTR_USER: if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto set_ea_exit; - if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) - cifs_dbg(FYI, "attempt to set cifs inode metadata\n"); + goto out; - ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ if (pTcon->ses->server->ops->set_EA) rc = pTcon->ses->server->ops->set_EA(xid, pTcon, - full_path, ea_name, ea_value, (__u16)value_size, + full_path, name, value, (__u16)size, cifs_sb->local_nls, cifs_remap(cifs_sb)); - } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) - == 0) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto set_ea_exit; + break; - ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ - if (pTcon->ses->server->ops->set_EA) - rc = pTcon->ses->server->ops->set_EA(xid, pTcon, - full_path, ea_name, ea_value, (__u16)value_size, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - } else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL, - strlen(CIFS_XATTR_CIFS_ACL)) == 0) { + case XATTR_CIFS_ACL: { #ifdef CONFIG_CIFS_ACL struct cifs_ntsd *pacl; - pacl = kmalloc(value_size, GFP_KERNEL); + + if (!value) + goto out; + pacl = kmalloc(size, GFP_KERNEL); if (!pacl) { rc = -ENOMEM; } else { - memcpy(pacl, ea_value, value_size); - if (pTcon->ses->server->ops->set_acl) + memcpy(pacl, value, size); + if (value && + pTcon->ses->server->ops->set_acl) rc = pTcon->ses->server->ops->set_acl(pacl, - value_size, d_inode(direntry), + size, inode, full_path, CIFS_ACL_DACL); else rc = -EOPNOTSUPP; if (rc == 0) /* force revalidate of the inode */ - CIFS_I(d_inode(direntry))->time = 0; + CIFS_I(inode)->time = 0; kfree(pacl); } -#else - cifs_dbg(FYI, "Set CIFS ACL not supported yet\n"); #endif /* CONFIG_CIFS_ACL */ - } else { - int temp; - temp = strncmp(ea_name, XATTR_NAME_POSIX_ACL_ACCESS, - strlen(XATTR_NAME_POSIX_ACL_ACCESS)); - if (temp == 0) { + break; + } + + case XATTR_ACL_ACCESS: #ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & MS_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - ea_value, (const int)value_size, - ACL_TYPE_ACCESS, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_dbg(FYI, "set POSIX ACL rc %d\n", rc); -#else - cifs_dbg(FYI, "set POSIX ACL not supported\n"); -#endif - } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_DEFAULT, - strlen(XATTR_NAME_POSIX_ACL_DEFAULT)) == 0) { + if (!value) + goto out; + if (sb->s_flags & MS_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_ACCESS, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: #ifdef CONFIG_CIFS_POSIX - if (sb->s_flags & MS_POSIXACL) - rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, - ea_value, (const int)value_size, - ACL_TYPE_DEFAULT, cifs_sb->local_nls, - cifs_remap(cifs_sb)); - cifs_dbg(FYI, "set POSIX default ACL rc %d\n", rc); -#else - cifs_dbg(FYI, "set default POSIX ACL not supported\n"); -#endif - } else { - cifs_dbg(FYI, "illegal xattr request %s (only user namespace supported)\n", - ea_name); - /* BB what if no namespace prefix? */ - /* Should we just pass them to server, except for - system and perhaps security prefixes? */ - } + if (!value) + goto out; + if (sb->s_flags & MS_POSIXACL) + rc = CIFSSMBSetPosixACL(xid, pTcon, full_path, + value, (const int)size, + ACL_TYPE_DEFAULT, cifs_sb->local_nls, + cifs_remap(cifs_sb)); +#endif /* CONFIG_CIFS_POSIX */ + break; } -set_ea_exit: +out: kfree(full_path); free_xid(xid); cifs_put_tlink(tlink); -#endif return rc; } -ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, - void *ea_value, size_t buf_size) +static int cifs_xattr_get(const struct xattr_handler *handler, + struct dentry *dentry, struct inode *inode, + const char *name, void *value, size_t size) { ssize_t rc = -EOPNOTSUPP; -#ifdef CONFIG_CIFS_XATTR unsigned int xid; - struct cifs_sb_info *cifs_sb; + struct super_block *sb = dentry->d_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct tcon_link *tlink; struct cifs_tcon *pTcon; - struct super_block *sb; char *full_path; - if (direntry == NULL) - return -EIO; - if (d_really_is_negative(direntry)) - return -EIO; - sb = d_inode(direntry)->i_sb; - if (sb == NULL) - return -EIO; - - cifs_sb = CIFS_SB(sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); @@ -260,98 +163,72 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, xid = get_xid(); - full_path = build_path_from_dentry(direntry); + full_path = build_path_from_dentry(dentry); if (full_path == NULL) { rc = -ENOMEM; - goto get_ea_exit; + goto out; } /* return dos attributes as pseudo xattr */ /* return alt name if available as pseudo attr */ - if (ea_name == NULL) { - cifs_dbg(FYI, "Null xattr names not supported\n"); - } else if (strncmp(ea_name, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN) - == 0) { + switch (handler->flags) { + case XATTR_USER: if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto get_ea_exit; + goto out; - if (strncmp(ea_name, CIFS_XATTR_DOS_ATTRIB, 14) == 0) { - cifs_dbg(FYI, "attempt to query cifs inode metadata\n"); - /* revalidate/getattr then populate from inode */ - } /* BB add else when above is implemented */ - ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ if (pTcon->ses->server->ops->query_all_EAs) rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, ea_name, ea_value, buf_size, + full_path, name, value, size, cifs_sb->local_nls, cifs_remap(cifs_sb)); - } else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) { - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) - goto get_ea_exit; + break; - ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ - if (pTcon->ses->server->ops->query_all_EAs) - rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, - full_path, ea_name, ea_value, buf_size, - cifs_sb->local_nls, cifs_remap(cifs_sb)); - } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_ACCESS, - strlen(XATTR_NAME_POSIX_ACL_ACCESS)) == 0) { + case XATTR_CIFS_ACL: { +#ifdef CONFIG_CIFS_ACL + u32 acllen; + struct cifs_ntsd *pacl; + + if (pTcon->ses->server->ops->get_acl == NULL) + goto out; /* rc already EOPNOTSUPP */ + + pacl = pTcon->ses->server->ops->get_acl(cifs_sb, + inode, full_path, &acllen); + if (IS_ERR(pacl)) { + rc = PTR_ERR(pacl); + cifs_dbg(VFS, "%s: error %zd getting sec desc\n", + __func__, rc); + } else { + if (value) { + if (acllen > size) + acllen = -ERANGE; + else + memcpy(value, pacl, acllen); + } + rc = acllen; + kfree(pacl); + } +#endif /* CONFIG_CIFS_ACL */ + break; + } + + case XATTR_ACL_ACCESS: #ifdef CONFIG_CIFS_POSIX if (sb->s_flags & MS_POSIXACL) rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - ea_value, buf_size, ACL_TYPE_ACCESS, + value, size, ACL_TYPE_ACCESS, cifs_sb->local_nls, cifs_remap(cifs_sb)); -#else - cifs_dbg(FYI, "Query POSIX ACL not supported yet\n"); -#endif /* CONFIG_CIFS_POSIX */ - } else if (strncmp(ea_name, XATTR_NAME_POSIX_ACL_DEFAULT, - strlen(XATTR_NAME_POSIX_ACL_DEFAULT)) == 0) { +#endif /* CONFIG_CIFS_POSIX */ + break; + + case XATTR_ACL_DEFAULT: #ifdef CONFIG_CIFS_POSIX if (sb->s_flags & MS_POSIXACL) rc = CIFSSMBGetPosixACL(xid, pTcon, full_path, - ea_value, buf_size, ACL_TYPE_DEFAULT, + value, size, ACL_TYPE_DEFAULT, cifs_sb->local_nls, cifs_remap(cifs_sb)); -#else - cifs_dbg(FYI, "Query POSIX default ACL not supported yet\n"); -#endif /* CONFIG_CIFS_POSIX */ - } else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL, - strlen(CIFS_XATTR_CIFS_ACL)) == 0) { -#ifdef CONFIG_CIFS_ACL - u32 acllen; - struct cifs_ntsd *pacl; - - if (pTcon->ses->server->ops->get_acl == NULL) - goto get_ea_exit; /* rc already EOPNOTSUPP */ - - pacl = pTcon->ses->server->ops->get_acl(cifs_sb, - d_inode(direntry), full_path, &acllen); - if (IS_ERR(pacl)) { - rc = PTR_ERR(pacl); - cifs_dbg(VFS, "%s: error %zd getting sec desc\n", - __func__, rc); - } else { - if (ea_value) { - if (acllen > buf_size) - acllen = -ERANGE; - else - memcpy(ea_value, pacl, acllen); - } - rc = acllen; - kfree(pacl); - } -#else - cifs_dbg(FYI, "Query CIFS ACL not supported yet\n"); -#endif /* CONFIG_CIFS_ACL */ - } else if (strncmp(ea_name, - XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN) == 0) { - cifs_dbg(FYI, "Trusted xattr namespace not supported yet\n"); - } else if (strncmp(ea_name, - XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) == 0) { - cifs_dbg(FYI, "Security xattr namespace not supported yet\n"); - } else - cifs_dbg(FYI, - "illegal xattr request %s (only user namespace supported)\n", - ea_name); +#endif /* CONFIG_CIFS_POSIX */ + break; + } /* We could add an additional check for streams ie if proc/fs/cifs/streamstoxattr is set then @@ -361,34 +238,22 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, if (rc == -EINVAL) rc = -EOPNOTSUPP; -get_ea_exit: +out: kfree(full_path); free_xid(xid); cifs_put_tlink(tlink); -#endif return rc; } ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) { ssize_t rc = -EOPNOTSUPP; -#ifdef CONFIG_CIFS_XATTR unsigned int xid; - struct cifs_sb_info *cifs_sb; + struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb); struct tcon_link *tlink; struct cifs_tcon *pTcon; - struct super_block *sb; char *full_path; - if (direntry == NULL) - return -EIO; - if (d_really_is_negative(direntry)) - return -EIO; - sb = d_inode(direntry)->i_sb; - if (sb == NULL) - return -EIO; - - cifs_sb = CIFS_SB(sb); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) return -EOPNOTSUPP; @@ -419,6 +284,50 @@ list_ea_exit: kfree(full_path); free_xid(xid); cifs_put_tlink(tlink); -#endif return rc; } + +static const struct xattr_handler cifs_user_xattr_handler = { + .prefix = XATTR_USER_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +/* os2.* attributes are treated like user.* attributes */ +static const struct xattr_handler cifs_os2_xattr_handler = { + .prefix = XATTR_OS2_PREFIX, + .flags = XATTR_USER, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_cifs_acl_xattr_handler = { + .name = CIFS_XATTR_CIFS_ACL, + .flags = XATTR_CIFS_ACL, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_posix_acl_access_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_ACCESS, + .flags = XATTR_ACL_ACCESS, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +static const struct xattr_handler cifs_posix_acl_default_xattr_handler = { + .name = XATTR_NAME_POSIX_ACL_DEFAULT, + .flags = XATTR_ACL_DEFAULT, + .get = cifs_xattr_get, + .set = cifs_xattr_set, +}; + +const struct xattr_handler *cifs_xattr_handlers[] = { + &cifs_user_xattr_handler, + &cifs_os2_xattr_handler, + &cifs_cifs_acl_xattr_handler, + &cifs_posix_acl_access_xattr_handler, + &cifs_posix_acl_default_xattr_handler, + NULL +}; |