Enable previous version support
authorSteve French <smfrench@gmail.com>
Sat, 1 Oct 2016 02:14:26 +0000 (21:14 -0500)
committerSteve French <smfrench@gmail.com>
Fri, 14 Oct 2016 00:48:11 +0000 (19:48 -0500)
Add ioctl to query previous versions of file

Allows listing snapshots on files on SMB3 mounts.

Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifs_ioctl.h
fs/cifs/cifsglob.h
fs/cifs/ioctl.c
fs/cifs/smb2ops.c

index 0065256..57ff075 100644 (file)
@@ -36,7 +36,15 @@ struct smb_mnt_fs_info {
        __u64   cifs_posix_caps;
 } __packed;
 
+struct smb_snapshot_array {
+       __u32   number_of_snapshots;
+       __u32   number_of_snapshots_returned;
+       __u32   snapshot_array_size;
+       /*      snapshots[]; */
+} __packed;
+
 #define CIFS_IOCTL_MAGIC       0xCF
 #define CIFS_IOC_COPYCHUNK_FILE        _IOW(CIFS_IOCTL_MAGIC, 3, int)
 #define CIFS_IOC_SET_INTEGRITY  _IO(CIFS_IOCTL_MAGIC, 4)
 #define CIFS_IOC_GET_MNT_INFO _IOR(CIFS_IOCTL_MAGIC, 5, struct smb_mnt_fs_info)
+#define CIFS_ENUMERATE_SNAPSHOTS _IOR(CIFS_IOCTL_MAGIC, 6, struct smb_snapshot_array)
index cd80594..d9a1728 100644 (file)
@@ -388,6 +388,8 @@ struct smb_version_operations {
        int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *);
        int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
                             struct cifsFileInfo *src_file);
+       int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
+                            struct cifsFileInfo *src_file, void __user *);
        int (*query_mf_symlink)(unsigned int, struct cifs_tcon *,
                                struct cifs_sb_info *, const unsigned char *,
                                char *, unsigned int *);
index 7a3b84e..9f51b81 100644 (file)
@@ -189,7 +189,7 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
        xid = get_xid();
 
        cifs_sb = CIFS_SB(inode->i_sb);
-
+       cifs_dbg(VFS, "cifs ioctl 0x%x\n", command);
        switch (command) {
                case FS_IOC_GETFLAGS:
                        if (pSMBFile == NULL)
@@ -267,11 +267,23 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
                        tcon = tlink_tcon(pSMBFile->tlink);
                        rc = smb_mnt_get_fsinfo(xid, tcon, (void __user *)arg);
                        break;
+               case CIFS_ENUMERATE_SNAPSHOTS:
+                       if (arg == 0) {
+                               rc = -EINVAL;
+                               goto cifs_ioc_exit;
+                       }
+                       tcon = tlink_tcon(pSMBFile->tlink);
+                       if (tcon->ses->server->ops->enum_snapshots)
+                               rc = tcon->ses->server->ops->enum_snapshots(xid, tcon,
+                                               pSMBFile, (void __user *)arg);
+                       else
+                               rc = -EOPNOTSUPP;
+                       break;
                default:
                        cifs_dbg(FYI, "unsupported ioctl\n");
                        break;
        }
-
+cifs_ioc_exit:
        free_xid(xid);
        return rc;
 }
index 378260c..f0872d2 100644 (file)
@@ -28,6 +28,7 @@
 #include "cifs_unicode.h"
 #include "smb2status.h"
 #include "smb2glob.h"
+#include "cifs_ioctl.h"
 
 static int
 change_conf(struct TCP_Server_Info *server)
@@ -894,6 +895,50 @@ smb3_set_integrity(const unsigned int xid, struct cifs_tcon *tcon,
 
 }
 
+static int
+smb3_enum_snapshots(const unsigned int xid, struct cifs_tcon *tcon,
+                  struct cifsFileInfo *cfile, void __user *ioc_buf)
+{
+       char *retbuf = NULL;
+       unsigned int ret_data_len = 0;
+       int rc;
+       struct smb_snapshot_array snapshot_in;
+
+       rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid,
+                       cfile->fid.volatile_fid,
+                       FSCTL_SRV_ENUMERATE_SNAPSHOTS,
+                       true /* is_fsctl */, NULL, 0 /* no input data */,
+                       (char **)&retbuf,
+                       &ret_data_len);
+       cifs_dbg(FYI, "enum snaphots ioctl returned %d and ret buflen is %d\n",
+                       rc, ret_data_len);
+       if (rc)
+               return rc;
+
+       if (ret_data_len && (ioc_buf != NULL) && (retbuf != NULL)) {
+               /* Fixup buffer */
+               if (copy_from_user(&snapshot_in, ioc_buf,
+                   sizeof(struct smb_snapshot_array))) {
+                       rc = -EFAULT;
+                       kfree(retbuf);
+                       return rc;
+               }
+               if (snapshot_in.snapshot_array_size < sizeof(struct smb_snapshot_array)) {
+                       rc = -ERANGE;
+                       return rc;
+               }
+
+               if (ret_data_len > snapshot_in.snapshot_array_size)
+                       ret_data_len = snapshot_in.snapshot_array_size;
+
+               if (copy_to_user(ioc_buf, retbuf, ret_data_len))
+                       rc = -EFAULT;
+       }
+
+       kfree(retbuf);
+       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,
@@ -1659,6 +1704,7 @@ struct smb_version_operations smb21_operations = {
        .clone_range = smb2_clone_range,
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 struct smb_version_operations smb30_operations = {
@@ -1745,6 +1791,7 @@ struct smb_version_operations smb30_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 
 #ifdef CONFIG_CIFS_SMB311
@@ -1832,6 +1879,7 @@ struct smb_version_operations smb311_operations = {
        .wp_retry_size = smb2_wp_retry_size,
        .dir_needs_close = smb2_dir_needs_close,
        .fallocate = smb3_fallocate,
+       .enum_snapshots = smb3_enum_snapshots,
 };
 #endif /* CIFS_SMB311 */