Skip to content

Commit

Permalink
CIFS: make mknod() an smb_version_op
Browse files Browse the repository at this point in the history
This cleanup removes cifs specific code from SMB2/SMB3 code paths
which is cleaner and easier to maintain as the code to handle
special files is improved.  Below is an example creating special files
using 'sfu' mount option over SMB3 to Windows (with this patch)
(Note that to Samba server, support for saving dos attributes
has to be enabled for the SFU mount option to work).

In the future this will also make implementation of creating
special files as reparse points easier (as Windows NFS server does
for example).

   root@smf-Thinkpad-P51:~# stat -c "%F" /mnt2/char
   character special file

   root@smf-Thinkpad-P51:~# stat -c "%F" /mnt2/block
   block special file

Signed-off-by: Aurelien Aptel <[email protected]>
Signed-off-by: Steve French <[email protected]>
Reviewed-by: Ronnie Sahlberg <[email protected]>
  • Loading branch information
aaptel authored and Steve French committed Mar 15, 2019
1 parent 6552580 commit c847dcc
Show file tree
Hide file tree
Showing 4 changed files with 239 additions and 104 deletions.
8 changes: 8 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,14 @@ struct smb_version_operations {
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 {
Expand Down
107 changes: 3 additions & 104 deletions fs/cifs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -621,20 +621,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
{
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;
Expand All @@ -654,103 +644,12 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,
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;
Expand Down
126 changes: 126 additions & 0 deletions fs/cifs/smb1ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1027,6 +1027,131 @@ cifs_can_echo(struct TCP_Server_Info *server)
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,
Expand Down Expand Up @@ -1110,6 +1235,7 @@ struct smb_version_operations smb1_operations = {
.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 = {
Expand Down
Loading

0 comments on commit c847dcc

Please sign in to comment.