Merge tag 'modules-for-v5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu...
[linux-2.6-microblaze.git] / fs / cifs / smb2ops.c
index f703204..e4c8f60 100644 (file)
@@ -388,7 +388,9 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 {
        int rc;
 
+       spin_lock(&GlobalMid_Lock);
        cifs_ses_server(ses)->CurrentMid = 0;
+       spin_unlock(&GlobalMid_Lock);
        rc = SMB2_negotiate(xid, ses);
        /* BB we probably don't need to retry with modern servers */
        if (rc == -EAGAIN)
@@ -690,17 +692,21 @@ smb2_close_cached_fid(struct kref *ref)
                cfid->is_valid = false;
                cfid->file_all_info_is_valid = false;
                cfid->has_lease = false;
+               if (cfid->dentry) {
+                       dput(cfid->dentry);
+                       cfid->dentry = NULL;
+               }
        }
 }
 
-void close_shroot(struct cached_fid *cfid)
+void close_cached_dir(struct cached_fid *cfid)
 {
        mutex_lock(&cfid->fid_mutex);
        kref_put(&cfid->refcount, smb2_close_cached_fid);
        mutex_unlock(&cfid->fid_mutex);
 }
 
-void close_shroot_lease_locked(struct cached_fid *cfid)
+void close_cached_dir_lease_locked(struct cached_fid *cfid)
 {
        if (cfid->has_lease) {
                cfid->has_lease = false;
@@ -708,10 +714,10 @@ void close_shroot_lease_locked(struct cached_fid *cfid)
        }
 }
 
-void close_shroot_lease(struct cached_fid *cfid)
+void close_cached_dir_lease(struct cached_fid *cfid)
 {
        mutex_lock(&cfid->fid_mutex);
-       close_shroot_lease_locked(cfid);
+       close_cached_dir_lease_locked(cfid);
        mutex_unlock(&cfid->fid_mutex);
 }
 
@@ -721,13 +727,15 @@ smb2_cached_lease_break(struct work_struct *work)
        struct cached_fid *cfid = container_of(work,
                                struct cached_fid, lease_break);
 
-       close_shroot_lease(cfid);
+       close_cached_dir_lease(cfid);
 }
 
 /*
- * Open the directory at the root of a share
+ * Open the and cache a directory handle.
+ * Only supported for the root handle.
  */
-int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
+int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+               const char *path,
                struct cifs_sb_info *cifs_sb,
                struct cached_fid **cfid)
 {
@@ -745,6 +753,18 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
        __le16 utf16_path = 0; /* Null - since an open of top of share */
        u8 oplock = SMB2_OPLOCK_LEVEL_II;
        struct cifs_fid *pfid;
+       struct dentry *dentry;
+
+       if (tcon->nohandlecache)
+               return -ENOTSUPP;
+
+       if (cifs_sb->root == NULL)
+               return -ENOENT;
+
+       if (strlen(path))
+               return -ENOENT;
+
+       dentry = cifs_sb->root;
 
        mutex_lock(&tcon->crfid.fid_mutex);
        if (tcon->crfid.is_valid) {
@@ -830,11 +850,9 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
                };
 
                /*
-                * caller expects this func to set pfid to a valid
-                * cached root, so we copy the existing one and get a
-                * reference.
+                * caller expects this func to set the fid in crfid to valid
+                * cached root, so increment the refcount.
                 */
-               memcpy(pfid, tcon->crfid.fid, sizeof(*pfid));
                kref_get(&tcon->crfid.refcount);
 
                mutex_unlock(&tcon->crfid.fid_mutex);
@@ -867,13 +885,18 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
        oparms.fid->mid = le64_to_cpu(o_rsp->sync_hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
 
-       memcpy(tcon->crfid.fid, pfid, sizeof(struct cifs_fid));
        tcon->crfid.tcon = tcon;
        tcon->crfid.is_valid = true;
+       tcon->crfid.dentry = dentry;
+       dget(dentry);
        kref_init(&tcon->crfid.refcount);
 
        /* BB TBD check to see if oplock level check can be removed below */
        if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
+               /*
+                * See commit 2f94a3125b87. Increment the refcount when we
+                * get a lease for root, release it if lease break occurs
+                */
                kref_get(&tcon->crfid.refcount);
                tcon->crfid.has_lease = true;
                smb2_parse_contexts(server, o_rsp,
@@ -892,6 +915,8 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
                                &rsp_iov[1], sizeof(struct smb2_file_all_info),
                                (char *)&tcon->crfid.file_all_info))
                tcon->crfid.file_all_info_is_valid = true;
+       tcon->crfid.time = jiffies;
+
 
 oshr_exit:
        mutex_unlock(&tcon->crfid.fid_mutex);
@@ -905,6 +930,22 @@ oshr_free:
        return rc;
 }
 
+int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
+                             struct dentry *dentry,
+                             struct cached_fid **cfid)
+{
+       mutex_lock(&tcon->crfid.fid_mutex);
+       if (tcon->crfid.dentry == dentry) {
+               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
+               *cfid = &tcon->crfid;
+               kref_get(&tcon->crfid.refcount);
+               mutex_unlock(&tcon->crfid.fid_mutex);
+               return 0;
+       }
+       mutex_unlock(&tcon->crfid.fid_mutex);
+       return -ENOENT;
+}
+
 static void
 smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
              struct cifs_sb_info *cifs_sb)
@@ -914,7 +955,6 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
        u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
-       bool no_cached_open = tcon->nohandlecache;
        struct cached_fid *cfid = NULL;
 
        oparms.tcon = tcon;
@@ -924,14 +964,12 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       if (no_cached_open) {
+       rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
+       if (rc == 0)
+               memcpy(&fid, cfid->fid, sizeof(struct cifs_fid));
+       else
                rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
                               NULL, NULL);
-       } else {
-               rc = open_shroot(xid, tcon, cifs_sb, &cfid);
-               if (rc == 0)
-                       memcpy(&fid, cfid->fid, sizeof(struct cifs_fid));
-       }
        if (rc)
                return;
 
@@ -945,10 +983,10 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
                        FS_VOLUME_INFORMATION);
        SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid,
                        FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */
-       if (no_cached_open)
+       if (cfid == NULL)
                SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
        else
-               close_shroot(cfid);
+               close_cached_dir(cfid);
 }
 
 static void
@@ -1531,7 +1569,10 @@ SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon,
                        NULL, 0 /* no input */, CIFSMaxBufSize,
                        (char **)&res_key, &ret_data_len);
 
-       if (rc) {
+       if (rc == -EOPNOTSUPP) {
+               pr_warn_once("Server share %s does not support copy range\n", tcon->treeName);
+               goto req_res_key_exit;
+       } else if (rc) {
                cifs_tcon_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc);
                goto req_res_key_exit;
        }
@@ -1763,18 +1804,14 @@ smb2_ioctl_query_info(const unsigned int xid,
        }
 
  iqinf_exit:
-       kfree(vars);
-       kfree(buffer);
-       SMB2_open_free(&rqst[0]);
-       if (qi.flags & PASSTHRU_FSCTL)
-               SMB2_ioctl_free(&rqst[1]);
-       else
-               SMB2_query_info_free(&rqst[1]);
-
-       SMB2_close_free(&rqst[2]);
+       cifs_small_buf_release(rqst[0].rq_iov[0].iov_base);
+       cifs_small_buf_release(rqst[1].rq_iov[0].iov_base);
+       cifs_small_buf_release(rqst[2].rq_iov[0].iov_base);
        free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
        free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
        free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
+       kfree(vars);
+       kfree(buffer);
        return rc;
 
 e_fault:
@@ -1826,6 +1863,8 @@ smb2_copychunk_range(const unsigned int xid,
                        cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk));
 
                /* Request server copy to target from src identified by key */
+               kfree(retbuf);
+               retbuf = NULL;
                rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid,
                        trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
                        true /* is_fsctl */, (char *)pcchunk,
@@ -2217,22 +2256,23 @@ smb3_notify(const unsigned int xid, struct file *pfile,
        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_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
        struct cifs_open_parms oparms;
        struct cifs_fid fid;
        struct cifs_tcon *tcon;
-       unsigned char *path = NULL;
+       const unsigned char *path;
+       void *page = alloc_dentry_path();
        __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);
+       path = build_path_from_dentry(dentry, page);
+       if (IS_ERR(path)) {
+               rc = PTR_ERR(path);
+               goto notify_exit;
+       }
 
-       utf16_path = cifs_convert_path_to_utf16(path + 1, cifs_sb);
+       utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (utf16_path == NULL) {
                rc = -ENOMEM;
                goto notify_exit;
@@ -2264,7 +2304,7 @@ smb3_notify(const unsigned int xid, struct file *pfile,
        cifs_dbg(FYI, "change notify for path %s rc %d\n", path, rc);
 
 notify_exit:
-       kfree(path);
+       free_dentry_path(page);
        kfree(utf16_path);
        return rc;
 }
@@ -2287,6 +2327,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
        struct smb2_query_directory_rsp *qd_rsp = NULL;
        struct smb2_create_rsp *op_rsp = NULL;
        struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+       int retry_count = 0;
 
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path)
@@ -2334,10 +2375,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 
        smb2_set_related(&rqst[1]);
 
+again:
        rc = compound_send_recv(xid, tcon->ses, server,
                                flags, 2, rqst,
                                resp_buftype, rsp_iov);
 
+       if (rc == -EAGAIN && retry_count++ < 10)
+               goto again;
+
        /* If the open failed there is nothing to do */
        op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
        if (op_rsp == NULL || op_rsp->sync_hdr.Status != STATUS_SUCCESS) {
@@ -3563,6 +3608,119 @@ static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon,
        return rc;
 }
 
+static int smb3_simple_fallocate_write_range(unsigned int xid,
+                                            struct cifs_tcon *tcon,
+                                            struct cifsFileInfo *cfile,
+                                            loff_t off, loff_t len,
+                                            char *buf)
+{
+       struct cifs_io_parms io_parms = {0};
+       int nbytes;
+       struct kvec iov[2];
+
+       io_parms.netfid = cfile->fid.netfid;
+       io_parms.pid = current->tgid;
+       io_parms.tcon = tcon;
+       io_parms.persistent_fid = cfile->fid.persistent_fid;
+       io_parms.volatile_fid = cfile->fid.volatile_fid;
+       io_parms.offset = off;
+       io_parms.length = len;
+
+       /* iov[0] is reserved for smb header */
+       iov[1].iov_base = buf;
+       iov[1].iov_len = io_parms.length;
+       return SMB2_write(xid, &io_parms, &nbytes, iov, 1);
+}
+
+static int smb3_simple_fallocate_range(unsigned int xid,
+                                      struct cifs_tcon *tcon,
+                                      struct cifsFileInfo *cfile,
+                                      loff_t off, loff_t len)
+{
+       struct file_allocated_range_buffer in_data, *out_data = NULL, *tmp_data;
+       u32 out_data_len;
+       char *buf = NULL;
+       loff_t l;
+       int rc;
+
+       in_data.file_offset = cpu_to_le64(off);
+       in_data.length = cpu_to_le64(len);
+       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                       cfile->fid.volatile_fid,
+                       FSCTL_QUERY_ALLOCATED_RANGES, true,
+                       (char *)&in_data, sizeof(in_data),
+                       1024 * sizeof(struct file_allocated_range_buffer),
+                       (char **)&out_data, &out_data_len);
+       if (rc)
+               goto out;
+       /*
+        * It is already all allocated
+        */
+       if (out_data_len == 0)
+               goto out;
+
+       buf = kzalloc(1024 * 1024, GFP_KERNEL);
+       if (buf == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       tmp_data = out_data;
+       while (len) {
+               /*
+                * The rest of the region is unmapped so write it all.
+                */
+               if (out_data_len == 0) {
+                       rc = smb3_simple_fallocate_write_range(xid, tcon,
+                                              cfile, off, len, buf);
+                       goto out;
+               }
+
+               if (out_data_len < sizeof(struct file_allocated_range_buffer)) {
+                       rc = -EINVAL;
+                       goto out;
+               }
+
+               if (off < le64_to_cpu(tmp_data->file_offset)) {
+                       /*
+                        * We are at a hole. Write until the end of the region
+                        * or until the next allocated data,
+                        * whichever comes next.
+                        */
+                       l = le64_to_cpu(tmp_data->file_offset) - off;
+                       if (len < l)
+                               l = len;
+                       rc = smb3_simple_fallocate_write_range(xid, tcon,
+                                              cfile, off, l, buf);
+                       if (rc)
+                               goto out;
+                       off = off + l;
+                       len = len - l;
+                       if (len == 0)
+                               goto out;
+               }
+               /*
+                * We are at a section of allocated data, just skip forward
+                * until the end of the data or the end of the region
+                * we are supposed to fallocate, whichever comes first.
+                */
+               l = le64_to_cpu(tmp_data->length);
+               if (len < l)
+                       l = len;
+               off += l;
+               len -= l;
+
+               tmp_data = &tmp_data[1];
+               out_data_len -= sizeof(struct file_allocated_range_buffer);
+       }
+
+ out:
+       kfree(out_data);
+       kfree(buf);
+       return rc;
+}
+
+
 static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
                            loff_t off, loff_t len, bool keep_size)
 {
@@ -3623,6 +3781,26 @@ static long smb3_simple_falloc(struct file *file, struct cifs_tcon *tcon,
        }
 
        if ((keep_size == true) || (i_size_read(inode) >= off + len)) {
+               /*
+                * At this point, we are trying to fallocate an internal
+                * regions of a sparse file. Since smb2 does not have a
+                * fallocate command we have two otions on how to emulate this.
+                * We can either turn the entire file to become non-sparse
+                * which we only do if the fallocate is for virtually
+                * the whole file,  or we can overwrite the region with zeroes
+                * using SMB2_write, which could be prohibitevly expensive
+                * if len is large.
+                */
+               /*
+                * We are only trying to fallocate a small region so
+                * just write it with zero.
+                */
+               if (len <= 1024 * 1024) {
+                       rc = smb3_simple_fallocate_range(xid, tcon, cfile,
+                                                        off, len);
+                       goto out;
+               }
+
                /*
                 * Check if falloc starts within first few pages of file
                 * and ends within a few pages of the end of file to
@@ -3652,6 +3830,77 @@ out:
        return rc;
 }
 
+static long smb3_collapse_range(struct file *file, struct cifs_tcon *tcon,
+                           loff_t off, loff_t len)
+{
+       int rc;
+       unsigned int xid;
+       struct cifsFileInfo *cfile = file->private_data;
+       __le64 eof;
+
+       xid = get_xid();
+
+       if (off >= i_size_read(file->f_inode) ||
+           off + len >= i_size_read(file->f_inode)) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       rc = smb2_copychunk_range(xid, cfile, cfile, off + len,
+                                 i_size_read(file->f_inode) - off - len, off);
+       if (rc < 0)
+               goto out;
+
+       eof = cpu_to_le64(i_size_read(file->f_inode) - len);
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, cfile->pid, &eof);
+       if (rc < 0)
+               goto out;
+
+       rc = 0;
+ out:
+       free_xid(xid);
+       return rc;
+}
+
+static long smb3_insert_range(struct file *file, struct cifs_tcon *tcon,
+                             loff_t off, loff_t len)
+{
+       int rc;
+       unsigned int xid;
+       struct cifsFileInfo *cfile = file->private_data;
+       __le64 eof;
+       __u64  count;
+
+       xid = get_xid();
+
+       if (off >= i_size_read(file->f_inode)) {
+               rc = -EINVAL;
+               goto out;
+       }
+
+       count = i_size_read(file->f_inode) - off;
+       eof = cpu_to_le64(i_size_read(file->f_inode) + len);
+
+       rc = SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+                         cfile->fid.volatile_fid, cfile->pid, &eof);
+       if (rc < 0)
+               goto out;
+
+       rc = smb2_copychunk_range(xid, cfile, cfile, off, count, off + len);
+       if (rc < 0)
+               goto out;
+
+       rc = smb3_zero_range(file, tcon, off, len, 1);
+       if (rc < 0)
+               goto out;
+
+       rc = 0;
+ out:
+       free_xid(xid);
+       return rc;
+}
+
 static loff_t smb3_llseek(struct file *file, struct cifs_tcon *tcon, loff_t offset, int whence)
 {
        struct cifsFileInfo *wrcfile, *cfile = file->private_data;
@@ -3823,6 +4072,10 @@ static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode,
                return smb3_zero_range(file, tcon, off, len, false);
        } else if (mode == FALLOC_FL_KEEP_SIZE)
                return smb3_simple_falloc(file, tcon, off, len, true);
+       else if (mode == FALLOC_FL_COLLAPSE_RANGE)
+               return smb3_collapse_range(file, tcon, off, len);
+       else if (mode == FALLOC_FL_INSERT_RANGE)
+               return smb3_insert_range(file, tcon, off, len);
        else if (mode == 0)
                return smb3_simple_falloc(file, tcon, off, len, false);
 
@@ -3870,6 +4123,7 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
                      unsigned int epoch, bool *purge_cache)
 {
        oplock &= 0xFF;
+       cinode->lease_granted = false;
        if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
                return;
        if (oplock == SMB2_OPLOCK_LEVEL_BATCH) {
@@ -3896,6 +4150,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,
        unsigned int new_oplock = 0;
 
        oplock &= 0xFF;
+       cinode->lease_granted = true;
        if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
                return;
 
@@ -4178,7 +4433,7 @@ smb2_get_enc_key(struct TCP_Server_Info *server, __u64 ses_id, int enc, u8 *key)
        }
        spin_unlock(&cifs_tcp_ses_lock);
 
-       return 1;
+       return -EAGAIN;
 }
 /*
  * Encrypt or decrypt @rqst message. @rqst[0] has the following format:
@@ -4968,7 +5223,7 @@ smb2_next_header(char *buf)
 static int
 smb2_make_node(unsigned int xid, struct inode *inode,
               struct dentry *dentry, struct cifs_tcon *tcon,
-              char *full_path, umode_t mode, dev_t dev)
+              const char *full_path, umode_t mode, dev_t dev)
 {
        struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
        int rc = -EPERM;