diff options
Diffstat (limited to 'fs/smb/client/smb2ops.c')
-rw-r--r-- | fs/smb/client/smb2ops.c | 335 |
1 files changed, 54 insertions, 281 deletions
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index ab77ecb2d4..28f0b7d19d 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -28,6 +28,7 @@ #include "fscache.h" #include "fs_context.h" #include "cached_dir.h" +#include "reparse.h" /* Change credits for different ops and return the total number of credits */ static int @@ -2914,8 +2915,11 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, tcon = list_first_entry_or_null(&ses->tcon_list, struct cifs_tcon, tcon_list); - if (tcon) + if (tcon) { tcon->tc_count++; + trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_get_dfs_refer); + } spin_unlock(&cifs_tcp_ses_lock); } @@ -2979,6 +2983,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, /* ipc tcons are not refcounted */ spin_lock(&cifs_tcp_ses_lock); tcon->tc_count--; + trace_smb3_tcon_ref(tcon->debug_id, tcon->tc_count, + netfs_trace_tcon_ref_dec_dfs_refer); /* tc_count can never go negative */ WARN_ON(tcon->tc_count < 0); spin_unlock(&cifs_tcp_ses_lock); @@ -2989,109 +2995,6 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, return rc; } -/* See MS-FSCC 2.1.2.6 for the 'NFS' style reparse tags */ -static int parse_reparse_posix(struct reparse_posix_data *buf, - struct cifs_sb_info *cifs_sb, - struct cifs_open_info_data *data) -{ - unsigned int len; - u64 type; - - switch ((type = le64_to_cpu(buf->InodeType))) { - case NFS_SPECFILE_LNK: - len = le16_to_cpu(buf->ReparseDataLength); - data->symlink_target = cifs_strndup_from_utf16(buf->DataBuffer, - len, true, - cifs_sb->local_nls); - if (!data->symlink_target) - return -ENOMEM; - convert_delimiter(data->symlink_target, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", - __func__, data->symlink_target); - break; - case NFS_SPECFILE_CHR: - case NFS_SPECFILE_BLK: - case NFS_SPECFILE_FIFO: - case NFS_SPECFILE_SOCK: - break; - default: - cifs_dbg(VFS, "%s: unhandled inode type: 0x%llx\n", - __func__, type); - return -EOPNOTSUPP; - } - return 0; -} - -static int parse_reparse_symlink(struct reparse_symlink_data_buffer *sym, - u32 plen, bool unicode, - struct cifs_sb_info *cifs_sb, - struct cifs_open_info_data *data) -{ - unsigned int len; - unsigned int offs; - - /* We handle Symbolic Link reparse tag here. See: MS-FSCC 2.1.2.4 */ - - offs = le16_to_cpu(sym->SubstituteNameOffset); - len = le16_to_cpu(sym->SubstituteNameLength); - if (offs + 20 > plen || offs + len + 20 > plen) { - cifs_dbg(VFS, "srv returned malformed symlink buffer\n"); - return -EIO; - } - - data->symlink_target = cifs_strndup_from_utf16(sym->PathBuffer + offs, - len, unicode, - cifs_sb->local_nls); - if (!data->symlink_target) - return -ENOMEM; - - convert_delimiter(data->symlink_target, '/'); - cifs_dbg(FYI, "%s: target path: %s\n", __func__, data->symlink_target); - - return 0; -} - -int parse_reparse_point(struct reparse_data_buffer *buf, - u32 plen, struct cifs_sb_info *cifs_sb, - bool unicode, struct cifs_open_info_data *data) -{ - data->reparse.buf = buf; - - /* See MS-FSCC 2.1.2 */ - switch (le32_to_cpu(buf->ReparseTag)) { - case IO_REPARSE_TAG_NFS: - return parse_reparse_posix((struct reparse_posix_data *)buf, - cifs_sb, data); - case IO_REPARSE_TAG_SYMLINK: - return parse_reparse_symlink( - (struct reparse_symlink_data_buffer *)buf, - plen, unicode, cifs_sb, data); - case IO_REPARSE_TAG_LX_SYMLINK: - case IO_REPARSE_TAG_AF_UNIX: - case IO_REPARSE_TAG_LX_FIFO: - case IO_REPARSE_TAG_LX_CHR: - case IO_REPARSE_TAG_LX_BLK: - return 0; - default: - cifs_dbg(VFS, "%s: unhandled reparse tag: 0x%08x\n", - __func__, le32_to_cpu(buf->ReparseTag)); - return -EOPNOTSUPP; - } -} - -static int smb2_parse_reparse_point(struct cifs_sb_info *cifs_sb, - struct kvec *rsp_iov, - struct cifs_open_info_data *data) -{ - struct reparse_data_buffer *buf; - struct smb2_ioctl_rsp *io = rsp_iov->iov_base; - u32 plen = le32_to_cpu(io->OutputCount); - - buf = (struct reparse_data_buffer *)((u8 *)io + - le32_to_cpu(io->OutputOffset)); - return parse_reparse_point(buf, plen, cifs_sb, true, data); -} - static struct cifs_ntsd * get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb, const struct cifs_fid *cifsfid, u32 *pacllen, u32 info) @@ -4018,7 +3921,7 @@ smb21_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock, strcat(message, "W"); } if (!new_oplock) - strncpy(message, "None", sizeof(message)); + strscpy(message, "None"); cinode->oplock = new_oplock; cifs_dbg(FYI, "%s Lease granted on inode %p\n", message, @@ -5066,214 +4969,84 @@ static int smb2_next_header(struct TCP_Server_Info *server, char *buf, return 0; } -int cifs_sfu_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) +static int __cifs_sfu_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) { - struct cifs_open_info_data buf = {}; struct TCP_Server_Info *server = tcon->ses->server; struct cifs_open_parms oparms; struct cifs_io_parms io_parms = {}; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_fid fid; unsigned int bytes_written; - struct win_dev *pdev; + struct win_dev pdev = {}; struct kvec iov[2]; __u32 oplock = server->oplocks ? REQ_OPLOCK : 0; int rc; - if (!S_ISCHR(mode) && !S_ISBLK(mode) && !S_ISFIFO(mode)) + switch (mode & S_IFMT) { + case S_IFCHR: + strscpy(pdev.type, "IntxCHR"); + pdev.major = cpu_to_le64(MAJOR(dev)); + pdev.minor = cpu_to_le64(MINOR(dev)); + break; + case S_IFBLK: + strscpy(pdev.type, "IntxBLK"); + pdev.major = cpu_to_le64(MAJOR(dev)); + pdev.minor = cpu_to_le64(MINOR(dev)); + break; + case S_IFIFO: + strscpy(pdev.type, "LnxFIFO"); + break; + default: return -EPERM; + } - oparms = (struct cifs_open_parms) { - .tcon = tcon, - .cifs_sb = cifs_sb, - .desired_access = GENERIC_WRITE, - .create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR | - CREATE_OPTION_SPECIAL), - .disposition = FILE_CREATE, - .path = full_path, - .fid = &fid, - }; + oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, GENERIC_WRITE, + FILE_CREATE, CREATE_NOT_DIR | + CREATE_OPTION_SPECIAL, ACL_NO_MODE); + oparms.fid = &fid; - rc = server->ops->open(xid, &oparms, &oplock, &buf); + rc = server->ops->open(xid, &oparms, &oplock, NULL); if (rc) return rc; - /* - * 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.fi; io_parms.pid = current->tgid; io_parms.tcon = tcon; - io_parms.length = sizeof(*pdev); - iov[1].iov_base = pdev; - iov[1].iov_len = sizeof(*pdev); - if (S_ISCHR(mode)) { - memcpy(pdev->type, "IntxCHR", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - } else if (S_ISBLK(mode)) { - memcpy(pdev->type, "IntxBLK", 8); - pdev->major = cpu_to_le64(MAJOR(dev)); - pdev->minor = cpu_to_le64(MINOR(dev)); - } else if (S_ISFIFO(mode)) { - memcpy(pdev->type, "LnxFIFO", 8); - } + io_parms.length = sizeof(pdev); + iov[1].iov_base = &pdev; + iov[1].iov_len = sizeof(pdev); rc = server->ops->sync_write(xid, &fid, &io_parms, &bytes_written, iov, 1); server->ops->close(xid, tcon, &fid); - d_drop(dentry); - /* FIXME: add code here to set EAs */ - cifs_free_open_info(&buf); return rc; } -static inline u64 mode_nfs_type(mode_t mode) -{ - switch (mode & S_IFMT) { - case S_IFBLK: return NFS_SPECFILE_BLK; - case S_IFCHR: return NFS_SPECFILE_CHR; - case S_IFIFO: return NFS_SPECFILE_FIFO; - case S_IFSOCK: return NFS_SPECFILE_SOCK; - } - return 0; -} - -static int nfs_set_reparse_buf(struct reparse_posix_data *buf, - mode_t mode, dev_t dev, - struct kvec *iov) -{ - u64 type; - u16 len, dlen; - - len = sizeof(*buf); - - switch ((type = mode_nfs_type(mode))) { - case NFS_SPECFILE_BLK: - case NFS_SPECFILE_CHR: - dlen = sizeof(__le64); - break; - case NFS_SPECFILE_FIFO: - case NFS_SPECFILE_SOCK: - dlen = 0; - break; - default: - return -EOPNOTSUPP; - } - - buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_NFS); - buf->Reserved = 0; - buf->InodeType = cpu_to_le64(type); - buf->ReparseDataLength = cpu_to_le16(len + dlen - - sizeof(struct reparse_data_buffer)); - *(__le64 *)buf->DataBuffer = cpu_to_le64(((u64)MAJOR(dev) << 32) | - MINOR(dev)); - iov->iov_base = buf; - iov->iov_len = len + dlen; - return 0; -} - -static int nfs_make_node(unsigned int xid, struct inode *inode, - struct dentry *dentry, struct cifs_tcon *tcon, - const char *full_path, umode_t mode, dev_t dev) +int cifs_sfu_make_node(unsigned int xid, struct inode *inode, + struct dentry *dentry, struct cifs_tcon *tcon, + const char *full_path, umode_t mode, dev_t dev) { - struct cifs_open_info_data data; - struct reparse_posix_data *p; - struct inode *new; - struct kvec iov; - __u8 buf[sizeof(*p) + sizeof(__le64)]; + struct inode *new = NULL; int rc; - p = (struct reparse_posix_data *)buf; - rc = nfs_set_reparse_buf(p, mode, dev, &iov); + rc = __cifs_sfu_make_node(xid, inode, dentry, tcon, + full_path, mode, dev); if (rc) return rc; - data = (struct cifs_open_info_data) { - .reparse_point = true, - .reparse = { .tag = IO_REPARSE_TAG_NFS, .posix = p, }, - }; - - new = smb2_get_reparse_inode(&data, inode->i_sb, xid, - tcon, full_path, &iov); - if (!IS_ERR(new)) - d_instantiate(dentry, new); - else - rc = PTR_ERR(new); - cifs_free_open_info(&data); - return rc; -} - -static int smb2_create_reparse_symlink(const unsigned int xid, - struct inode *inode, - struct dentry *dentry, - struct cifs_tcon *tcon, - const char *full_path, - const char *symname) -{ - struct reparse_symlink_data_buffer *buf = NULL; - struct cifs_open_info_data data; - struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); - struct inode *new; - struct kvec iov; - __le16 *path; - char *sym, sep = CIFS_DIR_SEP(cifs_sb); - u16 len, plen; - int rc = 0; - - sym = kstrdup(symname, GFP_KERNEL); - if (!sym) - return -ENOMEM; - - data = (struct cifs_open_info_data) { - .reparse_point = true, - .reparse = { .tag = IO_REPARSE_TAG_SYMLINK, }, - .symlink_target = sym, - }; - - convert_delimiter(sym, sep); - path = cifs_convert_path_to_utf16(sym, cifs_sb); - if (!path) { - rc = -ENOMEM; - goto out; - } - - plen = 2 * UniStrnlen((wchar_t *)path, PATH_MAX); - len = sizeof(*buf) + plen * 2; - buf = kzalloc(len, GFP_KERNEL); - if (!buf) { - rc = -ENOMEM; - goto out; + if (tcon->posix_extensions) { + rc = smb311_posix_get_inode_info(&new, full_path, NULL, + inode->i_sb, xid); + } else if (tcon->unix_ext) { + rc = cifs_get_inode_info_unix(&new, full_path, + inode->i_sb, xid); + } else { + rc = cifs_get_inode_info(&new, full_path, NULL, + inode->i_sb, xid, NULL); } - - buf->ReparseTag = cpu_to_le32(IO_REPARSE_TAG_SYMLINK); - buf->ReparseDataLength = cpu_to_le16(len - sizeof(struct reparse_data_buffer)); - buf->SubstituteNameOffset = cpu_to_le16(plen); - buf->SubstituteNameLength = cpu_to_le16(plen); - memcpy(&buf->PathBuffer[plen], path, plen); - buf->PrintNameOffset = 0; - buf->PrintNameLength = cpu_to_le16(plen); - memcpy(buf->PathBuffer, path, plen); - buf->Flags = cpu_to_le32(*symname != '/' ? SYMLINK_FLAG_RELATIVE : 0); - if (*sym != sep) - buf->Flags = cpu_to_le32(SYMLINK_FLAG_RELATIVE); - - convert_delimiter(sym, '/'); - iov.iov_base = buf; - iov.iov_len = len; - new = smb2_get_reparse_inode(&data, inode->i_sb, xid, - tcon, full_path, &iov); - if (!IS_ERR(new)) + if (!rc) d_instantiate(dentry, new); - else - rc = PTR_ERR(new); -out: - kfree(path); - cifs_free_open_info(&data); - kfree(buf); return rc; } @@ -5294,8 +5067,8 @@ static int smb2_make_node(unsigned int xid, struct inode *inode, rc = cifs_sfu_make_node(xid, inode, dentry, tcon, full_path, mode, dev); } else { - rc = nfs_make_node(xid, inode, dentry, tcon, - full_path, mode, dev); + rc = smb2_mknod_reparse(xid, inode, dentry, tcon, + full_path, mode, dev); } return rc; } |