CIFS: Reconnect durable handles for SMB2
authorPavel Shilovsky <pshilovsky@samba.org>
Tue, 9 Jul 2013 14:40:58 +0000 (18:40 +0400)
committerSteve French <smfrench@gmail.com>
Wed, 10 Jul 2013 18:08:40 +0000 (13:08 -0500)
On reconnects, we need to reopen file and then obtain all byte-range
locks held by the client. SMB2 protocol provides feature to make
this process atomic by reconnecting to the same file handle
with all it's byte-range locks. This patch adds this capability
for SMB2 shares.

Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
Signed-off-by: Steven French <steven@steven-GA-970A-DS3.(none)>
fs/cifs/cifsglob.h
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/smb2file.c
fs/cifs/smb2inode.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c
fs/cifs/smb2pdu.h

index 982fdf92c791021e34f107dcc95c3b67f2b007aa..1fdc370410576e1a3f5a36a133245468160ae4b4 100644 (file)
@@ -920,6 +920,7 @@ struct cifs_open_parms {
        int create_options;
        const char *path;
        struct cifs_fid *fid;
+       bool reconnect:1;
 };
 
 struct cifs_fid {
index c27c242ac5ba3f9fc7f532e61ce6371edd1be002..d62ce0d4814173645f9bbd2da0b0553b527a98b2 100644 (file)
@@ -327,6 +327,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
        oparms.disposition = disposition;
        oparms.path = full_path;
        oparms.fid = fid;
+       oparms.reconnect = false;
 
        rc = server->ops->open(xid, &oparms, oplock, buf);
        if (rc) {
index f36f9a7893da02ae6d38d4642a5563c8ada6ce5c..ba7eed2ee6627d24aec664b8ee07c10ef2f89176 100644 (file)
@@ -232,6 +232,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
        oparms.disposition = disposition;
        oparms.path = full_path;
        oparms.fid = fid;
+       oparms.reconnect = false;
 
        rc = server->ops->open(xid, &oparms, oplock, buf);
 
@@ -594,7 +595,6 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
        int desired_access;
        int disposition = FILE_OPEN;
        int create_options = CREATE_NOT_DIR;
-       struct cifs_fid fid;
        struct cifs_open_parms oparms;
 
        xid = get_xid();
@@ -645,7 +645,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
 
                rc = cifs_posix_open(full_path, NULL, inode->i_sb,
                                     cifs_sb->mnt_file_mode /* ignored */,
-                                    oflags, &oplock, &fid.netfid, xid);
+                                    oflags, &oplock, &cfile->fid.netfid, xid);
                if (rc == 0) {
                        cifs_dbg(FYI, "posix reopen succeeded\n");
                        goto reopen_success;
@@ -662,7 +662,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
                create_options |= CREATE_OPEN_BACKUP_INTENT;
 
        if (server->ops->get_lease_key)
-               server->ops->get_lease_key(inode, &fid);
+               server->ops->get_lease_key(inode, &cfile->fid);
 
        oparms.tcon = tcon;
        oparms.cifs_sb = cifs_sb;
@@ -670,7 +670,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
        oparms.create_options = create_options;
        oparms.disposition = disposition;
        oparms.path = full_path;
-       oparms.fid = &fid;
+       oparms.fid = &cfile->fid;
+       oparms.reconnect = true;
 
        /*
         * Can not refresh inode by passing in file_info buf to be returned by
@@ -710,8 +711,9 @@ reopen_success:
         * to the server to get the new inode info.
         */
 
-       server->ops->set_fid(cfile, &fid, oplock);
-       cifs_relock_file(cfile);
+       server->ops->set_fid(cfile, &cfile->fid, oplock);
+       if (oparms.reconnect)
+               cifs_relock_file(cfile);
 
 reopen_error_exit:
        kfree(full_path);
@@ -1508,6 +1510,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
                if (!rc)
                        goto out;
 
+
                /*
                 * Windows 7 server can delay breaking lease from read to None
                 * if we set a byte-range lock on a file - break it explicitly
index 398992962cc6bec794a67a5580a8bab704755b90..04a81a4142c3235f1bb1693b9be4c73580023c2b 100644 (file)
@@ -40,7 +40,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
        oplock &= 0xFF;
        if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
                return;
-       if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+       if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
+           oplock == SMB2_OPLOCK_LEVEL_BATCH) {
                cinode->clientCanCacheAll = true;
                cinode->clientCanCacheRead = true;
                cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
index 9841df771f08eda368027b1737783e15f677a77c..c6ec1633309abad6464995e4aad8352129f5fed6 100644 (file)
@@ -58,6 +58,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.disposition = create_disposition;
        oparms.create_options = create_options;
        oparms.fid = &fid;
+       oparms.reconnect = false;
 
        rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
        if (rc) {
index 1bc7d7e64cf53a75a65c2820a88362c61fb8b452..f259e6cc835791f20e34acce582f83ca1b78102c 100644 (file)
@@ -227,6 +227,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.disposition = FILE_OPEN;
        oparms.create_options = 0;
        oparms.fid = &fid;
+       oparms.reconnect = false;
 
        rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
        if (rc) {
@@ -460,6 +461,7 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.disposition = FILE_OPEN;
        oparms.create_options = 0;
        oparms.fid = fid;
+       oparms.reconnect = false;
 
        rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
        kfree(utf16_path);
@@ -546,6 +548,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
        oparms.disposition = FILE_OPEN;
        oparms.create_options = 0;
        oparms.fid = &fid;
+       oparms.reconnect = false;
 
        rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL);
        if (rc)
index 9d7341d696fc7aee5a3448344af1ef88deec10f0..c7ad06fc9d63313ad9f8fafe3a776d48a09ba663 100644 (file)
@@ -857,7 +857,7 @@ create_durable_buf(void)
                return NULL;
 
        buf->ccontext.DataOffset = cpu_to_le16(offsetof
-                                       (struct create_durable, Reserved));
+                                       (struct create_durable, Data));
        buf->ccontext.DataLength = cpu_to_le32(16);
        buf->ccontext.NameOffset = cpu_to_le16(offsetof
                                (struct create_durable, Name));
@@ -869,6 +869,30 @@ create_durable_buf(void)
        return buf;
 }
 
+static struct create_durable *
+create_reconnect_durable_buf(struct cifs_fid *fid)
+{
+       struct create_durable *buf;
+
+       buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
+       if (!buf)
+               return NULL;
+
+       buf->ccontext.DataOffset = cpu_to_le16(offsetof
+                                       (struct create_durable, Data));
+       buf->ccontext.DataLength = cpu_to_le32(16);
+       buf->ccontext.NameOffset = cpu_to_le16(offsetof
+                               (struct create_durable, Name));
+       buf->ccontext.NameLength = cpu_to_le16(4);
+       buf->Data.Fid.PersistentFileId = fid->persistent_fid;
+       buf->Data.Fid.VolatileFileId = fid->volatile_fid;
+       buf->Name[0] = 'D';
+       buf->Name[1] = 'H';
+       buf->Name[2] = 'n';
+       buf->Name[3] = 'C';
+       return buf;
+}
+
 static __u8
 parse_lease_state(struct smb2_create_rsp *rsp)
 {
@@ -924,12 +948,18 @@ add_lease_context(struct kvec *iov, unsigned int *num_iovec, __u8 *oplock)
 }
 
 static int
-add_durable_context(struct kvec *iov, unsigned int *num_iovec)
+add_durable_context(struct kvec *iov, unsigned int *num_iovec,
+                   struct cifs_open_parms *oparms)
 {
        struct smb2_create_req *req = iov[0].iov_base;
        unsigned int num = *num_iovec;
 
-       iov[num].iov_base = create_durable_buf();
+       if (oparms->reconnect) {
+               iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
+               /* indicate that we don't need to relock the file */
+               oparms->reconnect = false;
+       } else
+               iov[num].iov_base = create_durable_buf();
        if (iov[num].iov_base == NULL)
                return -ENOMEM;
        iov[num].iov_len = sizeof(struct create_durable);
@@ -1037,7 +1067,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
                            (struct create_context *)iov[num_iovecs-1].iov_base;
                        ccontext->Next = sizeof(struct create_lease);
                }
-               rc = add_durable_context(iov, &num_iovecs);
+               rc = add_durable_context(iov, &num_iovecs, oparms);
                if (rc) {
                        cifs_small_buf_release(req);
                        kfree(copy_path);
index 3e30f0ad57499f4da7cecf5158607a453f70b711..36b0d37ea69b8d9f7bab7d1e1e3fcd167afe34e3 100644 (file)
@@ -488,7 +488,13 @@ struct create_lease {
 struct create_durable {
        struct create_context ccontext;
        __u8   Name[8];
-       __u8   Reserved[16];
+       union {
+               __u8  Reserved[16];
+               struct {
+                       __u64 PersistentFileId;
+                       __u64 VolatileFileId;
+               } Fid;
+       } Data;
 } __packed;
 
 /* this goes in the ioctl buffer when doing a copychunk request */