struct cifs_tcon *tcon,
                                __le16 *path, int is_dir,
                                unsigned long p);
+       /* make unix special files (block, char, fifo, socket) */
+       int (*make_node)(unsigned int xid,
+                        struct inode *inode,
+                        struct dentry *dentry,
+                        struct cifs_tcon *tcon,
+                        char *full_path,
+                        umode_t mode,
+                        dev_t device_number);
 };
 
 struct smb_version_values {
 
 {
        int rc = -EPERM;
        unsigned int xid;
-       int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
        struct cifs_sb_info *cifs_sb;
        struct tcon_link *tlink;
        struct cifs_tcon *tcon;
-       struct cifs_io_parms io_parms;
        char *full_path = NULL;
-       struct inode *newinode = NULL;
-       __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;
                goto mknod_out;
        }
 
-       if (tcon->unix_ext) {
-               struct cifs_unix_set_info_args args = {
-                       .mode   = mode & ~current_umask(),
-                       .ctime  = NO_CHANGE_64,
-                       .atime  = NO_CHANGE_64,
-                       .mtime  = NO_CHANGE_64,
-                       .device = device_number,
-               };
-               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
-                       args.uid = current_fsuid();
-                       args.gid = current_fsgid();
-               } else {
-                       args.uid = INVALID_UID; /* no change */
-                       args.gid = INVALID_GID; /* no change */
-               }
-               rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
-                                           cifs_sb->local_nls,
-                                           cifs_remap(cifs_sb));
-               if (rc)
-                       goto mknod_out;
-
-               rc = cifs_get_inode_info_unix(&newinode, full_path,
-                                               inode->i_sb, xid);
-
-               if (rc == 0)
-                       d_instantiate(direntry, newinode);
-               goto mknod_out;
-       }
-
-       if (!S_ISCHR(mode) && !S_ISBLK(mode))
-               goto mknod_out;
-
-       if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
-               goto mknod_out;
-
-
-       cifs_dbg(FYI, "sfu compat create special file\n");
-
-       buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
-       if (buf == NULL) {
-               rc = -ENOMEM;
-               goto mknod_out;
-       }
-
-       if (backup_cred(cifs_sb))
-               create_options |= CREATE_OPEN_BACKUP_INTENT;
-
-       oparms.tcon = tcon;
-       oparms.cifs_sb = cifs_sb;
-       oparms.desired_access = GENERIC_WRITE;
-       oparms.create_options = create_options;
-       oparms.disposition = FILE_CREATE;
-       oparms.path = full_path;
-       oparms.fid = &fid;
-       oparms.reconnect = false;
-
-       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;
-
-       /*
-        * BB Do not bother to decode buf since no local inode yet to put
-        * timestamps in, but we can reuse it safely.
-        */
-
-       pdev = (struct win_dev *)buf;
-       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 = 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 = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
-                                                       &bytes_written, iov, 1);
-       }
-       tcon->ses->server->ops->close(xid, tcon, &fid);
-       d_drop(direntry);
-
-       /* FIXME: add code here to set EAs */
+       rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
+                                              full_path, mode,
+                                              device_number);
 
 mknod_out:
        kfree(full_path);
-       kfree(buf);
        free_xid(xid);
        cifs_put_tlink(tlink);
        return rc;
 
        return false;
 }
 
+static int
+cifs_make_node(unsigned int xid, struct inode *inode,
+              struct dentry *dentry, struct cifs_tcon *tcon,
+              char *full_path, umode_t mode, dev_t dev)
+{
+       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+       struct inode *newinode = NULL;
+       int rc = -EPERM;
+       int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
+       FILE_ALL_INFO *buf = NULL;
+       struct cifs_io_parms io_parms;
+       __u32 oplock = 0;
+       struct cifs_fid fid;
+       struct cifs_open_parms oparms;
+       unsigned int bytes_written;
+       struct win_dev *pdev;
+       struct kvec iov[2];
+
+       if (tcon->unix_ext) {
+               /*
+                * SMB1 Unix Extensions: requires server support but
+                * works with all special files
+                */
+               struct cifs_unix_set_info_args args = {
+                       .mode   = mode & ~current_umask(),
+                       .ctime  = NO_CHANGE_64,
+                       .atime  = NO_CHANGE_64,
+                       .mtime  = NO_CHANGE_64,
+                       .device = dev,
+               };
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+                       args.uid = current_fsuid();
+                       args.gid = current_fsgid();
+               } else {
+                       args.uid = INVALID_UID; /* no change */
+                       args.gid = INVALID_GID; /* no change */
+               }
+               rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
+                                           cifs_sb->local_nls,
+                                           cifs_remap(cifs_sb));
+               if (rc)
+                       goto out;
+
+               rc = cifs_get_inode_info_unix(&newinode, full_path,
+                                             inode->i_sb, xid);
+
+               if (rc == 0)
+                       d_instantiate(dentry, newinode);
+               goto out;
+       }
+
+       /*
+        * SMB1 SFU emulation: should work with all servers, but only
+        * support block and char device (no socket & fifo)
+        */
+       if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+               goto out;
+
+       if (!S_ISCHR(mode) && !S_ISBLK(mode))
+               goto out;
+
+       cifs_dbg(FYI, "sfu compat create special file\n");
+
+       buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+       if (buf == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+       oparms.tcon = tcon;
+       oparms.cifs_sb = cifs_sb;
+       oparms.desired_access = GENERIC_WRITE;
+       oparms.create_options = create_options;
+       oparms.disposition = FILE_CREATE;
+       oparms.path = full_path;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
+       if (rc)
+               goto out;
+
+       /*
+        * BB Do not bother to decode buf since no local inode yet to put
+        * timestamps in, but we can reuse it safely.
+        */
+
+       pdev = (struct win_dev *)buf;
+       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(dev));
+               pdev->minor = cpu_to_le64(MINOR(dev));
+               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(dev));
+               pdev->minor = cpu_to_le64(MINOR(dev));
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
+       }
+       tcon->ses->server->ops->close(xid, tcon, &fid);
+       d_drop(dentry);
+
+       /* FIXME: add code here to set EAs */
+out:
+       kfree(buf);
+       return rc;
+}
+
+
+
 struct smb_version_operations smb1_operations = {
        .send_cancel = send_nt_cancel,
        .compare_fids = cifs_compare_fids,
        .get_acl_by_fid = get_cifs_acl_by_fid,
        .set_acl = set_cifs_acl,
 #endif /* CIFS_ACL */
+       .make_node = cifs_make_node,
 };
 
 struct smb_version_values smb1_values = {
 
        return le32_to_cpu(hdr->NextCommand);
 }
 
+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)
+{
+       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+       int rc = -EPERM;
+       int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;
+       FILE_ALL_INFO *buf = NULL;
+       struct cifs_io_parms io_parms;
+       __u32 oplock = 0;
+       struct cifs_fid fid;
+       struct cifs_open_parms oparms;
+       unsigned int bytes_written;
+       struct win_dev *pdev;
+       struct kvec iov[2];
+
+       /*
+        * Check if mounted with mount parm 'sfu' mount parm.
+        * SFU emulation should work with all servers, but only
+        * supports block and char device (no socket & fifo),
+        * and was used by default in earlier versions of Windows
+        */
+       if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
+               goto out;
+
+       /*
+        * TODO: Add ability to create instead via reparse point. Windows (e.g.
+        * their current NFS server) uses this approach to expose special files
+        * over SMB2/SMB3 and Samba will do this with SMB3.1.1 POSIX Extensions
+        */
+
+       if (!S_ISCHR(mode) && !S_ISBLK(mode))
+               goto out;
+
+       cifs_dbg(FYI, "sfu compat create special file\n");
+
+       buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+       if (buf == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       if (backup_cred(cifs_sb))
+               create_options |= CREATE_OPEN_BACKUP_INTENT;
+
+       oparms.tcon = tcon;
+       oparms.cifs_sb = cifs_sb;
+       oparms.desired_access = GENERIC_WRITE;
+       oparms.create_options = create_options;
+       oparms.disposition = FILE_CREATE;
+       oparms.path = full_path;
+       oparms.fid = &fid;
+       oparms.reconnect = false;
+
+       if (tcon->ses->server->oplocks)
+               oplock = REQ_OPLOCK;
+       else
+               oplock = 0;
+       rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, buf);
+       if (rc)
+               goto out;
+
+       /*
+        * BB Do not bother to decode buf since no local inode yet to put
+        * timestamps in, but we can reuse it safely.
+        */
+
+       pdev = (struct win_dev *)buf;
+       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(dev));
+               pdev->minor = cpu_to_le64(MINOR(dev));
+               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(dev));
+               pdev->minor = cpu_to_le64(MINOR(dev));
+               rc = tcon->ses->server->ops->sync_write(xid, &fid, &io_parms,
+                                                       &bytes_written, iov, 1);
+       }
+       tcon->ses->server->ops->close(xid, tcon, &fid);
+       d_drop(dentry);
+
+       /* FIXME: add code here to set EAs */
+out:
+       kfree(buf);
+       return rc;
+}
+
+
 struct smb_version_operations smb20_operations = {
        .compare_fids = smb2_compare_fids,
        .setup_request = smb2_setup_request,
 #endif /* CIFS_ACL */
        .next_header = smb2_next_header,
        .ioctl_query_info = smb2_ioctl_query_info,
+       .make_node = smb2_make_node,
 };
 
 struct smb_version_operations smb21_operations = {
 #endif /* CIFS_ACL */
        .next_header = smb2_next_header,
        .ioctl_query_info = smb2_ioctl_query_info,
+       .make_node = smb2_make_node,
 };
 
 struct smb_version_operations smb30_operations = {
 #endif /* CIFS_ACL */
        .next_header = smb2_next_header,
        .ioctl_query_info = smb2_ioctl_query_info,
+       .make_node = smb2_make_node,
 };
 
 struct smb_version_operations smb311_operations = {
 #endif /* CIFS_ACL */
        .next_header = smb2_next_header,
        .ioctl_query_info = smb2_ioctl_query_info,
+       .make_node = smb2_make_node,
 };
 
 struct smb_version_values smb20_values = {