Allow mknod and mkfifo on SMB2/SMB3 mounts
authorSteve French <smfrench@gmail.com>
Mon, 22 Sep 2014 10:13:55 +0000 (05:13 -0500)
committerSteve French <smfrench@gmail.com>
Thu, 16 Oct 2014 20:20:19 +0000 (15:20 -0500)
The "sfu" mount option did not work on SMB2/SMB3 mounts.
With these changes when the "sfu" mount option is passed in
on an smb2/smb2.1/smb3 mount the client can emulate (and
recognize) fifo and device (character and device files).

In addition the "sfu" mount option should not conflict
with "mfsymlinks" (symlink emulation) as we will never
create "sfu" style symlinks, but using "sfu" mount option
will allow us to recognize existing symlinks, created with
Microsoft "Services for Unix" (SFU and SUA).

To enable the "sfu" mount option for SMB2/SMB3 the calling
syntax of the generic cifs/smb2/smb3 sync_read and sync_write
protocol dependent function needed to be changed (we
don't have a file struct in all cases), but this actually
ended up simplifying the code a little.

Signed-off-by: Steve French <smfrench@gmail.com>
fs/cifs/cifsglob.h
fs/cifs/connect.c
fs/cifs/dir.c
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/smb1ops.c
fs/cifs/smb2ops.c
fs/cifs/smb2pdu.c

index 25b8392bfdd2a7fd9864af5008e809dc41e3a7df..dae7e3709cc6051a72974c5f0c7d0a80dc94d42a 100644 (file)
@@ -323,11 +323,11 @@ struct smb_version_operations {
        int (*async_writev)(struct cifs_writedata *,
                            void (*release)(struct kref *));
        /* sync read from the server */
-       int (*sync_read)(const unsigned int, struct cifsFileInfo *,
+       int (*sync_read)(const unsigned int, struct cifs_fid *,
                         struct cifs_io_parms *, unsigned int *, char **,
                         int *);
        /* sync write to the server */
-       int (*sync_write)(const unsigned int, struct cifsFileInfo *,
+       int (*sync_write)(const unsigned int, struct cifs_fid *,
                          struct cifs_io_parms *, unsigned int *, struct kvec *,
                          unsigned long);
        /* open dir, start readdir */
index 239e1fb330002f5153cc2bbb1047506254aa5276..d8eb6a74b21178c43cb82ee49211e818bed84d2a 100644 (file)
@@ -3239,10 +3239,20 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
        }
        if (pvolume_info->mfsymlinks) {
                if (pvolume_info->sfu_emul) {
-                       cifs_dbg(VFS, "mount option mfsymlinks ignored if sfu mount option is used\n");
-               } else {
-                       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
+                       /*
+                        * Our SFU ("Services for Unix" emulation does not allow
+                        * creating symlinks but does allow reading existing SFU
+                        * symlinks (it does allow both creating and reading SFU
+                        * style mknod and FIFOs though). When "mfsymlinks" and
+                        * "sfu" are both enabled at the same time, it allows
+                        * reading both types of symlinks, but will only create
+                        * them with mfsymlinks format. This allows better
+                        * Apple compatibility (probably better for Samba too)
+                        * while still recognizing old Windows style symlinks.
+                        */
+                       cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
                }
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
        }
 
        if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
index 073640675a39684cb698ffc46e445bd7710c22c4..b72bc29cba23e7f29b4acfc5f1309150f0711aaf 100644 (file)
@@ -577,12 +577,13 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
        struct cifs_io_parms io_parms;
        char *full_path = NULL;
        struct inode *newinode = NULL;
-       int oplock = 0;
+       __u32 oplock = 0;
        struct cifs_fid fid;
        struct cifs_open_parms oparms;
        FILE_ALL_INFO *buf = NULL;
        unsigned int bytes_written;
        struct win_dev *pdev;
+       struct kvec iov[2];
 
        if (!old_valid_dev(device_number))
                return -EINVAL;
@@ -658,7 +659,11 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = CIFS_open(xid, &oparms, &oplock, buf);
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
        if (rc)
                goto mknod_out;
 
@@ -668,25 +673,26 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
         */
 
        pdev = (struct win_dev *)buf;
-       io_parms.netfid = fid.netfid;
        io_parms.pid = current->tgid;
        io_parms.tcon = tcon;
        io_parms.offset = 0;
        io_parms.length = sizeof(struct win_dev);
+       iov[1].iov_base = buf;
+       iov[1].iov_len = sizeof(struct win_dev);
        if (S_ISCHR(mode)) {
                memcpy(pdev->type, "IntxCHR", 8);
                pdev->major = cpu_to_le64(MAJOR(device_number));
                pdev->minor = cpu_to_le64(MINOR(device_number));
-               rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev,
-                                 NULL, 0);
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
        } else if (S_ISBLK(mode)) {
                memcpy(pdev->type, "IntxBLK", 8);
                pdev->major = cpu_to_le64(MAJOR(device_number));
                pdev->minor = cpu_to_le64(MINOR(device_number));
-               rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev,
-                                 NULL, 0);
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
        } /* else if (S_ISFIFO) */
-       CIFSSMBClose(xid, tcon, fid.netfid);
+       tcon->ses->server->ops->close(xid, tcon, &fid);
        d_drop(direntry);
 
        /* FIXME: add code here to set EAs */
index 8f7b40fd8f3ba2947b1fad3821e18ac02c0349ae..3e4d00a06c446b997750888bd080b9868a8771f9 100644 (file)
@@ -1687,8 +1687,8 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
                        io_parms.tcon = tcon;
                        io_parms.offset = *offset;
                        io_parms.length = len;
-                       rc = server->ops->sync_write(xid, open_file, &io_parms,
-                                                    &bytes_written, iov, 1);
+                       rc = server->ops->sync_write(xid, &open_file->fid,
+                                       &io_parms, &bytes_written, iov, 1);
                }
                if (rc || (bytes_written == 0)) {
                        if (total_written)
@@ -3206,7 +3206,7 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
                        io_parms.tcon = tcon;
                        io_parms.offset = *offset;
                        io_parms.length = current_read_size;
-                       rc = server->ops->sync_read(xid, open_file, &io_parms,
+                       rc = server->ops->sync_read(xid, &open_file->fid, &io_parms,
                                                    &bytes_read, &cur_offset,
                                                    &buf_type);
                } while (rc == -EAGAIN);
index 8fd4ee8e07ff9ea0db8c6e83e26201f4d8134ce2..4ff36ea8c693cd3917ac5d43e819e8c3220deb64 100644 (file)
@@ -412,7 +412,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
              struct cifs_sb_info *cifs_sb, unsigned int xid)
 {
        int rc;
-       int oplock = 0;
+       __u32 oplock;
        struct tcon_link *tlink;
        struct cifs_tcon *tcon;
        struct cifs_fid fid;
@@ -451,8 +451,13 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
        oparms.fid = &fid;
        oparms.reconnect = false;
 
-       rc = CIFS_open(xid, &oparms, &oplock, NULL);
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
        if (rc) {
+               cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc);
                cifs_put_tlink(tlink);
                return rc;
        }
@@ -464,7 +469,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
        io_parms.offset = 0;
        io_parms.length = 24;
 
-       rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type);
+       rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
+                                       &bytes_read, &pbuf, &buf_type);
        if ((rc == 0) && (bytes_read >= 8)) {
                if (memcmp("IntxBLK", pbuf, 8) == 0) {
                        cifs_dbg(FYI, "Block device\n");
@@ -504,7 +510,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const char *path,
                fattr->cf_dtype = DT_REG;
                rc = -EOPNOTSUPP; /* or some unknown SFU type */
        }
-       CIFSSMBClose(xid, tcon, fid.netfid);
+
+       tcon->ses->server->ops->close(xid, tcon, &fid);
        cifs_put_tlink(tlink);
        return rc;
 }
index 52131d8cb4d5397c6c75f0290c0d226d76e6d67c..2aca620193cd2e7bab6f8abbeac25fa46db4484e 100644 (file)
@@ -749,21 +749,21 @@ cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 static int
-cifs_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+cifs_sync_read(const unsigned int xid, struct cifs_fid *pfid,
               struct cifs_io_parms *parms, unsigned int *bytes_read,
               char **buf, int *buf_type)
 {
-       parms->netfid = cfile->fid.netfid;
+       parms->netfid = pfid->netfid;
        return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
 }
 
 static int
-cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+cifs_sync_write(const unsigned int xid, struct cifs_fid *pfid,
                struct cifs_io_parms *parms, unsigned int *written,
                struct kvec *iov, unsigned long nr_segs)
 {
 
-       parms->netfid = cfile->fid.netfid;
+       parms->netfid = pfid->netfid;
        return CIFSSMBWrite2(xid, parms, written, iov, nr_segs);
 }
 
index f522193b7184facd4926cfa3447b7fcbf5268cce..ea158c9dea15987cf5487ccbbc7a6b9943a1fc5a 100644 (file)
@@ -711,23 +711,23 @@ smb2_read_data_length(char *buf)
 
 
 static int
-smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+smb2_sync_read(const unsigned int xid, struct cifs_fid *pfid,
               struct cifs_io_parms *parms, unsigned int *bytes_read,
               char **buf, int *buf_type)
 {
-       parms->persistent_fid = cfile->fid.persistent_fid;
-       parms->volatile_fid = cfile->fid.volatile_fid;
+       parms->persistent_fid = pfid->persistent_fid;
+       parms->volatile_fid = pfid->volatile_fid;
        return SMB2_read(xid, parms, bytes_read, buf, buf_type);
 }
 
 static int
-smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+smb2_sync_write(const unsigned int xid, struct cifs_fid *pfid,
                struct cifs_io_parms *parms, unsigned int *written,
                struct kvec *iov, unsigned long nr_segs)
 {
 
-       parms->persistent_fid = cfile->fid.persistent_fid;
-       parms->volatile_fid = cfile->fid.volatile_fid;
+       parms->persistent_fid = pfid->persistent_fid;
+       parms->volatile_fid = pfid->volatile_fid;
        return SMB2_write(xid, parms, written, iov, nr_segs);
 }
 
index 74b3a6684383c9bfd08f481c8575932cefa06766..8f1672bb82d56202c3cc9d3d5506ab983d0467f6 100644 (file)
@@ -1098,6 +1098,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 
        if (oparms->create_options & CREATE_OPTION_READONLY)
                file_attributes |= ATTR_READONLY;
+       if (oparms->create_options & CREATE_OPTION_SPECIAL)
+               file_attributes |= ATTR_SYSTEM;
 
        req->ImpersonationLevel = IL_IMPERSONATION;
        req->DesiredAccess = cpu_to_le32(oparms->desired_access);