]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/cifs/smb2ops.c
Merge tag 'ras-urgent-2020-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / fs / cifs / smb2ops.c
index 6787fce26f2080d1a8284b3a3bd0a00c20bfa0fa..e47190cae1639a9793f9724f7cf19ba4fab1fed3 100644 (file)
@@ -655,7 +655,8 @@ smb2_cached_lease_break(struct work_struct *work)
 /*
  * Open the directory at the root of a share
  */
-int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
+int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
+               struct cifs_sb_info *cifs_sb, struct cifs_fid *pfid)
 {
        struct cifs_ses *ses = tcon->ses;
        struct TCP_Server_Info *server = ses->server;
@@ -702,7 +703,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
        rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
 
        oparms.tcon = tcon;
-       oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
        oparms.fid = pfid;
@@ -818,7 +819,8 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
 }
 
 static void
-smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
+smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
+             struct cifs_sb_info *cifs_sb)
 {
        int rc;
        __le16 srch_path = 0; /* Null - open root of share */
@@ -830,7 +832,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
-       oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -838,7 +840,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
                rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
                               NULL);
        else
-               rc = open_shroot(xid, tcon, &fid);
+               rc = open_shroot(xid, tcon, cifs_sb, &fid);
 
        if (rc)
                return;
@@ -860,7 +862,8 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
 }
 
 static void
-smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
+smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
+             struct cifs_sb_info *cifs_sb)
 {
        int rc;
        __le16 srch_path = 0; /* Null - open root of share */
@@ -871,7 +874,7 @@ smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
-       oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -906,10 +909,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -1116,7 +1116,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
        void *data[1];
        struct smb2_file_full_ea_info *ea = NULL;
        struct kvec close_iov[1];
-       int rc;
+       struct smb2_query_info_rsp *rsp;
+       int rc, used_len = 0;
 
        if (smb3_encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
@@ -1139,6 +1140,38 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
                                                             cifs_sb);
                        if (rc == -ENODATA)
                                goto sea_exit;
+               } else {
+                       /* If we are adding a attribute we should first check
+                        * if there will be enough space available to store
+                        * the new EA. If not we should not add it since we
+                        * would not be able to even read the EAs back.
+                        */
+                       rc = smb2_query_info_compound(xid, tcon, utf16_path,
+                                     FILE_READ_EA,
+                                     FILE_FULL_EA_INFORMATION,
+                                     SMB2_O_INFO_FILE,
+                                     CIFSMaxBufSize -
+                                     MAX_SMB2_CREATE_RESPONSE_SIZE -
+                                     MAX_SMB2_CLOSE_RESPONSE_SIZE,
+                                     &rsp_iov[1], &resp_buftype[1], cifs_sb);
+                       if (rc == 0) {
+                               rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
+                               used_len = le32_to_cpu(rsp->OutputBufferLength);
+                       }
+                       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+                       resp_buftype[1] = CIFS_NO_BUFFER;
+                       memset(&rsp_iov[1], 0, sizeof(rsp_iov[1]));
+                       rc = 0;
+
+                       /* Use a fudge factor of 256 bytes in case we collide
+                        * with a different set_EAs command.
+                        */
+                       if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE -
+                          MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 <
+                          used_len + ea_name_len + ea_value_len + 1) {
+                               rc = -ENOSPC;
+                               goto sea_exit;
+                       }
                }
        }
 
@@ -1151,10 +1184,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.tcon = tcon;
        oparms.desired_access = FILE_WRITE_EA;
        oparms.disposition = FILE_OPEN;
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -1422,6 +1452,7 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
 static int
 smb2_ioctl_query_info(const unsigned int xid,
                      struct cifs_tcon *tcon,
+                     struct cifs_sb_info *cifs_sb,
                      __le16 *path, int is_dir,
                      unsigned long p)
 {
@@ -1447,6 +1478,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        struct kvec close_iov[1];
        unsigned int size[2];
        void *data[2];
+       int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
 
        memset(rqst, 0, sizeof(rqst));
        resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
@@ -1477,10 +1509,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        memset(&oparms, 0, sizeof(oparms));
        oparms.tcon = tcon;
        oparms.disposition = FILE_OPEN;
-       if (is_dir)
-               oparms.create_options = CREATE_NOT_FILE;
-       else
-               oparms.create_options = CREATE_NOT_DIR;
+       oparms.create_options = cifs_create_options(cifs_sb, create_options);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -2049,6 +2078,66 @@ smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
        return rc;
 }
 
+
+
+static int
+smb3_notify(const unsigned int xid, struct file *pfile,
+           void __user *ioc_buf)
+{
+       struct smb3_notify notify;
+       struct dentry *dentry = pfile->f_path.dentry;
+       struct inode *inode = file_inode(pfile);
+       struct cifs_sb_info *cifs_sb;
+       struct cifs_open_parms oparms;
+       struct cifs_fid fid;
+       struct cifs_tcon *tcon;
+       unsigned char *path = NULL;
+       __le16 *utf16_path = NULL;
+       u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+       int rc = 0;
+
+       path = build_path_from_dentry(dentry);
+       if (path == NULL)
+               return -ENOMEM;
+
+       cifs_sb = CIFS_SB(inode->i_sb);
+
+       utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
+       if (utf16_path == NULL) {
+               rc = -ENOMEM;
+               goto notify_exit;
+       }
+
+       if (copy_from_user(&notify, ioc_buf, sizeof(struct smb3_notify))) {
+               rc = -EFAULT;
+               goto notify_exit;
+       }
+
+       tcon = cifs_sb_master_tcon(cifs_sb);
+       oparms.tcon = tcon;
+       oparms.desired_access = FILE_READ_ATTRIBUTES;
+       oparms.disposition = FILE_OPEN;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL);
+       if (rc)
+               goto notify_exit;
+
+       rc = SMB2_change_notify(xid, tcon, fid.persistent_fid, fid.volatile_fid,
+                               notify.watch_tree, notify.completion_filter);
+
+       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+
+       cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
+
+notify_exit:
+       kfree(path);
+       kfree(utf16_path);
+       return rc;
+}
+
 static int
 smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
                     const char *path, struct cifs_sb_info *cifs_sb,
@@ -2086,10 +2175,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
        oparms.disposition = FILE_OPEN;
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = fid;
        oparms.reconnect = false;
 
@@ -2343,10 +2429,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.tcon = tcon;
        oparms.desired_access = desired_access;
        oparms.disposition = FILE_OPEN;
-       if (cifs_sb && backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -2402,7 +2485,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 
 static int
 smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
-            struct kstatfs *buf)
+            struct cifs_sb_info *cifs_sb, struct kstatfs *buf)
 {
        struct smb2_query_info_rsp *rsp;
        struct smb2_fs_full_size_info *info = NULL;
@@ -2417,7 +2500,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
                                      FS_FULL_SIZE_INFORMATION,
                                      SMB2_O_INFO_FILESYSTEM,
                                      sizeof(struct smb2_fs_full_size_info),
-                                     &rsp_iov, &buftype, NULL);
+                                     &rsp_iov, &buftype, cifs_sb);
        if (rc)
                goto qfs_exit;
 
@@ -2439,7 +2522,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
 
 static int
 smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
-            struct kstatfs *buf)
+              struct cifs_sb_info *cifs_sb, struct kstatfs *buf)
 {
        int rc;
        __le16 srch_path = 0; /* Null - open root of share */
@@ -2448,12 +2531,12 @@ smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
        struct cifs_fid fid;
 
        if (!tcon->posix_extensions)
-               return smb2_queryfs(xid, tcon, buf);
+               return smb2_queryfs(xid, tcon, cifs_sb, buf);
 
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
-       oparms.create_options = 0;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -2722,6 +2805,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
        struct smb2_create_rsp *create_rsp;
        struct smb2_ioctl_rsp *ioctl_rsp;
        struct reparse_data_buffer *reparse_buf;
+       int create_options = is_reparse_point ? OPEN_REPARSE_POINT : 0;
        u32 plen;
 
        cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
@@ -2748,14 +2832,7 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.tcon = tcon;
        oparms.desired_access = FILE_READ_ATTRIBUTES;
        oparms.disposition = FILE_OPEN;
-
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
-       if (is_reparse_point)
-               oparms.create_options = OPEN_REPARSE_POINT;
-
+       oparms.create_options = cifs_create_options(cifs_sb, create_options);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -2934,11 +3011,6 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
        tcon = tlink_tcon(tlink);
        xid = get_xid();
 
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
-
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path) {
                rc = -ENOMEM;
@@ -2949,6 +3021,7 @@ get_smb2_acl_by_path(struct cifs_sb_info *cifs_sb,
        oparms.tcon = tcon;
        oparms.desired_access = READ_CONTROL;
        oparms.disposition = FILE_OPEN;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.fid = &fid;
        oparms.reconnect = false;
 
@@ -2990,11 +3063,6 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
        tcon = tlink_tcon(tlink);
        xid = get_xid();
 
-       if (backup_cred(cifs_sb))
-               oparms.create_options = CREATE_OPEN_BACKUP_INTENT;
-       else
-               oparms.create_options = 0;
-
        if (aclflag == CIFS_ACL_OWNER || aclflag == CIFS_ACL_GROUP)
                access_flags = WRITE_OWNER;
        else
@@ -3009,6 +3077,7 @@ set_smb2_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
 
        oparms.tcon = tcon;
        oparms.desired_access = access_flags;
+       oparms.create_options = cifs_create_options(cifs_sb, 0);
        oparms.disposition = FILE_OPEN;
        oparms.path = path;
        oparms.fid = &fid;
@@ -4491,7 +4560,6 @@ smb2_make_node(unsigned int xid, struct inode *inode,
 {
        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
        int rc = -EPERM;
-       int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
        FILE_ALL_INFO *buf = NULL;
        struct cifs_io_parms io_parms;
        __u32 oplock = 0;
@@ -4527,13 +4595,11 @@ smb2_make_node(unsigned int xid, struct inode *inode,
                goto out;
        }
 
-       if (backup_cred(cifs_sb))
-               create_options |= CREATE_OPEN_BACKUP_INTENT;
-
        oparms.tcon = tcon;
        oparms.cifs_sb = cifs_sb;
        oparms.desired_access = GENERIC_WRITE;
-       oparms.create_options = create_options;
+       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR |
+                                                   CREATE_OPTION_SPECIAL);
        oparms.disposition = FILE_CREATE;
        oparms.path = full_path;
        oparms.fid = &fid;
@@ -4762,6 +4828,7 @@ struct smb_version_operations smb21_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .enum_snapshots = smb3_enum_snapshots,
+       .notify = smb3_notify,
        .get_dfs_refer = smb2_get_dfs_refer,
        .select_sectype = smb2_select_sectype,
 #ifdef CONFIG_CIFS_XATTR
@@ -4868,6 +4935,7 @@ struct smb_version_operations smb30_operations = {
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
        .enum_snapshots = smb3_enum_snapshots,
+       .notify = smb3_notify,
        .init_transform_rq = smb3_init_transform_rq,
        .is_transform_hdr = smb3_is_transform_hdr,
        .receive_transform = smb3_receive_transform,
@@ -4978,6 +5046,7 @@ struct smb_version_operations smb311_operations = {
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
        .enum_snapshots = smb3_enum_snapshots,
+       .notify = smb3_notify,
        .init_transform_rq = smb3_init_transform_rq,
        .is_transform_hdr = smb3_is_transform_hdr,
        .receive_transform = smb3_receive_transform,