signal: Implement SIL_FAULT_TRAPNO
[linux-2.6-microblaze.git] / fs / cifs / smb2ops.c
index f703204..dd0eb66 100644 (file)
@@ -690,17 +690,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 +712,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 +725,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 +751,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 +848,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 +883,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 +913,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 +928,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 +953,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 +962,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 +981,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 +1567,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 +1802,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:
@@ -2217,22 +2252,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 +2300,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;
 }
@@ -3652,6 +3688,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 +3930,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);
 
@@ -4178,7 +4289,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 +5079,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;