smb3: do not error on fsync when readonly
authorSteve French <stfrench@microsoft.com>
Wed, 10 Nov 2021 07:47:48 +0000 (01:47 -0600)
committerSteve French <stfrench@microsoft.com>
Wed, 10 Nov 2021 22:28:27 +0000 (16:28 -0600)
Linux allows doing a flush/fsync on a file open for read-only,
but the protocol does not allow that.  If the file passed in
on the flush is read-only try to find a writeable handle for
the same inode, if that is not possible skip sending the
fsync call to the server to avoid breaking the apps.

Reported-by: Julian Sikorski <belegdol@gmail.com>
Tested-by: Julian Sikorski <belegdol@gmail.com>
Suggested-by: Jeremy Allison <jra@samba.org>
Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/file.c

index 1b855fc..9fee3af 100644 (file)
@@ -2692,12 +2692,23 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
        tcon = tlink_tcon(smbfile->tlink);
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
                server = tcon->ses->server;
-               if (server->ops->flush)
-                       rc = server->ops->flush(xid, tcon, &smbfile->fid);
-               else
+               if (server->ops->flush == NULL) {
                        rc = -ENOSYS;
+                       goto strict_fsync_exit;
+               }
+
+               if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
+                       smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
+                       if (smbfile) {
+                               rc = server->ops->flush(xid, tcon, &smbfile->fid);
+                               cifsFileInfo_put(smbfile);
+                       } else
+                               cifs_dbg(FYI, "ignore fsync for file not open for write\n");
+               } else
+                       rc = server->ops->flush(xid, tcon, &smbfile->fid);
        }
 
+strict_fsync_exit:
        free_xid(xid);
        return rc;
 }
@@ -2709,6 +2720,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        struct cifs_tcon *tcon;
        struct TCP_Server_Info *server;
        struct cifsFileInfo *smbfile = file->private_data;
+       struct inode *inode = file_inode(file);
        struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
 
        rc = file_write_and_wait_range(file, start, end);
@@ -2725,12 +2737,23 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
        tcon = tlink_tcon(smbfile->tlink);
        if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
                server = tcon->ses->server;
-               if (server->ops->flush)
-                       rc = server->ops->flush(xid, tcon, &smbfile->fid);
-               else
+               if (server->ops->flush == NULL) {
                        rc = -ENOSYS;
+                       goto fsync_exit;
+               }
+
+               if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) {
+                       smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY);
+                       if (smbfile) {
+                               rc = server->ops->flush(xid, tcon, &smbfile->fid);
+                               cifsFileInfo_put(smbfile);
+                       } else
+                               cifs_dbg(FYI, "ignore fsync for file not open for write\n");
+               } else
+                       rc = server->ops->flush(xid, tcon, &smbfile->fid);
        }
 
+fsync_exit:
        free_xid(xid);
        return rc;
 }