Commit 4515866d authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '6.7-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull smb client fixes from Steve French:

 - use after free fix in releasing multichannel interfaces

 - fixes for special file types (report char, block, FIFOs properly when
   created e.g. by NFS to Windows)

 - fixes for reporting various special file types and symlinks properly
   when using SMB1

* tag '6.7-rc2-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  smb: client: introduce cifs_sfu_make_node()
  smb: client: set correct file type from NFS reparse points
  smb: client: introduce ->parse_reparse_point()
  smb: client: implement ->query_reparse_point() for SMB1
  cifs: fix use after free for iface while disabling secondary channels
parents 090472ed b0348e45
...@@ -191,7 +191,13 @@ struct cifs_open_info_data { ...@@ -191,7 +191,13 @@ struct cifs_open_info_data {
bool reparse_point; bool reparse_point;
bool symlink; bool symlink;
}; };
__u32 reparse_tag; struct {
__u32 tag;
union {
struct reparse_data_buffer *buf;
struct reparse_posix_data *posix;
};
} reparse;
char *symlink_target; char *symlink_target;
union { union {
struct smb2_file_all_info fi; struct smb2_file_all_info fi;
...@@ -395,8 +401,7 @@ struct smb_version_operations { ...@@ -395,8 +401,7 @@ struct smb_version_operations {
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const char *full_path, const char *full_path,
char **target_path, char **target_path);
struct kvec *rsp_iov);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock, int (*open)(const unsigned int xid, struct cifs_open_parms *oparms, __u32 *oplock,
void *buf); void *buf);
...@@ -551,6 +556,9 @@ struct smb_version_operations { ...@@ -551,6 +556,9 @@ struct smb_version_operations {
bool (*is_status_io_timeout)(char *buf); bool (*is_status_io_timeout)(char *buf);
/* Check for STATUS_NETWORK_NAME_DELETED */ /* Check for STATUS_NETWORK_NAME_DELETED */
bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv); bool (*is_network_name_deleted)(char *buf, struct TCP_Server_Info *srv);
int (*parse_reparse_point)(struct cifs_sb_info *cifs_sb,
struct kvec *rsp_iov,
struct cifs_open_info_data *data);
}; };
struct smb_version_values { struct smb_version_values {
......
...@@ -1356,7 +1356,7 @@ typedef struct smb_com_transaction_ioctl_rsp { ...@@ -1356,7 +1356,7 @@ typedef struct smb_com_transaction_ioctl_rsp {
__le32 DataDisplacement; __le32 DataDisplacement;
__u8 SetupCount; /* 1 */ __u8 SetupCount; /* 1 */
__le16 ReturnedDataLen; __le16 ReturnedDataLen;
__u16 ByteCount; __le16 ByteCount;
} __attribute__((packed)) TRANSACT_IOCTL_RSP; } __attribute__((packed)) TRANSACT_IOCTL_RSP;
#define CIFS_ACL_OWNER 1 #define CIFS_ACL_OWNER 1
...@@ -1509,7 +1509,7 @@ struct reparse_posix_data { ...@@ -1509,7 +1509,7 @@ struct reparse_posix_data {
__le16 ReparseDataLength; __le16 ReparseDataLength;
__u16 Reserved; __u16 Reserved;
__le64 InodeType; /* LNK, FIFO, CHR etc. */ __le64 InodeType; /* LNK, FIFO, CHR etc. */
char PathBuffer[]; __u8 DataBuffer[];
} __attribute__((packed)); } __attribute__((packed));
struct cifs_quota_data { struct cifs_quota_data {
......
...@@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -210,7 +210,7 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
const struct cifs_fid *fid); const struct cifs_fid *fid);
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct cifs_fattr *fattr,
u32 tag); struct cifs_open_info_data *data);
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path, extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
struct super_block *sb, unsigned int xid); struct super_block *sb, unsigned int xid);
extern int cifs_get_inode_info_unix(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode,
...@@ -458,6 +458,12 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid, ...@@ -458,6 +458,12 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
const unsigned char *searchName, char **syminfo, const unsigned char *searchName, char **syminfo,
const struct nls_table *nls_codepage, int remap); const struct nls_table *nls_codepage, int remap);
extern int cifs_query_reparse_point(const unsigned int xid,
struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path,
u32 *tag, struct kvec *rsp,
int *rsp_buftype);
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
__u16 fid, char **symlinkinfo, __u16 fid, char **symlinkinfo,
const struct nls_table *nls_codepage); const struct nls_table *nls_codepage);
...@@ -659,6 +665,12 @@ void cifs_put_tcp_super(struct super_block *sb); ...@@ -659,6 +665,12 @@ void cifs_put_tcp_super(struct super_block *sb);
int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
char *extract_hostname(const char *unc); char *extract_hostname(const char *unc);
char *extract_sharename(const char *unc); char *extract_sharename(const char *unc);
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);
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);
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses, static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
......
...@@ -2690,136 +2690,97 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2690,136 +2690,97 @@ CIFSSMBUnixQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
return rc; return rc;
} }
/* int cifs_query_reparse_point(const unsigned int xid,
* Recent Windows versions now create symlinks more frequently struct cifs_tcon *tcon,
* and they use the "reparse point" mechanism below. We can of course struct cifs_sb_info *cifs_sb,
* do symlinks nicely to Samba and other servers which support the const char *full_path,
* CIFS Unix Extensions and we can also do SFU symlinks and "client only" u32 *tag, struct kvec *rsp,
* "MF" symlinks optionally, but for recent Windows we really need to int *rsp_buftype)
* reenable the code below and fix the cifs_symlink callers to handle this.
* In the interim this code has been moved to its own config option so
* it is not compiled in by default until callers fixed up and more tested.
*/
int
CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
__u16 fid, char **symlinkinfo,
const struct nls_table *nls_codepage)
{ {
int rc = 0; struct cifs_open_parms oparms;
int bytes_returned; TRANSACT_IOCTL_REQ *io_req = NULL;
struct smb_com_transaction_ioctl_req *pSMB; TRANSACT_IOCTL_RSP *io_rsp = NULL;
struct smb_com_transaction_ioctl_rsp *pSMBr; struct cifs_fid fid;
bool is_unicode;
unsigned int sub_len;
char *sub_start;
struct reparse_symlink_data *reparse_buf;
struct reparse_posix_data *posix_buf;
__u32 data_offset, data_count; __u32 data_offset, data_count;
char *end_of_smb; __u8 *start, *end;
int io_rsp_len;
int oplock = 0;
int rc;
cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
(void **) &pSMBr); if (cap_unix(tcon->ses))
return -EOPNOTSUPP;
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
.desired_access = FILE_READ_ATTRIBUTES,
.create_options = cifs_create_options(cifs_sb,
OPEN_REPARSE_POINT),
.disposition = FILE_OPEN,
.path = full_path,
.fid = &fid,
};
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (rc) if (rc)
return rc; return rc;
pSMB->TotalParameterCount = 0 ; rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon,
pSMB->TotalDataCount = 0; (void **)&io_req, (void **)&io_rsp);
pSMB->MaxParameterCount = cpu_to_le32(2); if (rc)
/* BB find exact data count max from sess structure BB */ goto error;
pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
pSMB->MaxSetupCount = 4;
pSMB->Reserved = 0;
pSMB->ParameterOffset = 0;
pSMB->DataCount = 0;
pSMB->DataOffset = 0;
pSMB->SetupCount = 4;
pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
pSMB->ParameterCount = pSMB->TotalParameterCount;
pSMB->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
pSMB->IsFsctl = 1; /* FSCTL */
pSMB->IsRootFlag = 0;
pSMB->Fid = fid; /* file handle always le */
pSMB->ByteCount = 0;
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
if (rc) {
cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
goto qreparse_out;
}
data_offset = le32_to_cpu(pSMBr->DataOffset); io_req->TotalParameterCount = 0;
data_count = le32_to_cpu(pSMBr->DataCount); io_req->TotalDataCount = 0;
if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { io_req->MaxParameterCount = cpu_to_le32(2);
/* BB also check enough total bytes returned */ /* BB find exact data count max from sess structure BB */
rc = -EIO; /* bad smb */ io_req->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00);
goto qreparse_out; io_req->MaxSetupCount = 4;
} io_req->Reserved = 0;
if (!data_count || (data_count > 2048)) { io_req->ParameterOffset = 0;
rc = -EIO; io_req->DataCount = 0;
cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); io_req->DataOffset = 0;
goto qreparse_out; io_req->SetupCount = 4;
} io_req->SubCommand = cpu_to_le16(NT_TRANSACT_IOCTL);
end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; io_req->ParameterCount = io_req->TotalParameterCount;
reparse_buf = (struct reparse_symlink_data *) io_req->FunctionCode = cpu_to_le32(FSCTL_GET_REPARSE_POINT);
((char *)&pSMBr->hdr.Protocol + data_offset); io_req->IsFsctl = 1;
if ((char *)reparse_buf >= end_of_smb) { io_req->IsRootFlag = 0;
rc = -EIO; io_req->Fid = fid.netfid;
goto qreparse_out; io_req->ByteCount = 0;
}
if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)io_req,
cifs_dbg(FYI, "NFS style reparse tag\n"); (struct smb_hdr *)io_rsp, &io_rsp_len, 0);
posix_buf = (struct reparse_posix_data *)reparse_buf; if (rc)
goto error;
if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { data_offset = le32_to_cpu(io_rsp->DataOffset);
cifs_dbg(FYI, "unsupported file type 0x%llx\n", data_count = le32_to_cpu(io_rsp->DataCount);
le64_to_cpu(posix_buf->InodeType)); if (get_bcc(&io_rsp->hdr) < 2 || data_offset > 512 ||
rc = -EOPNOTSUPP; !data_count || data_count > 2048) {
goto qreparse_out;
}
is_unicode = true;
sub_len = le16_to_cpu(reparse_buf->ReparseDataLength);
if (posix_buf->PathBuffer + sub_len > end_of_smb) {
cifs_dbg(FYI, "reparse buf beyond SMB\n");
rc = -EIO; rc = -EIO;
goto qreparse_out; goto error;
}
*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer,
sub_len, is_unicode, nls_codepage);
goto qreparse_out;
} else if (reparse_buf->ReparseTag !=
cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) {
rc = -EOPNOTSUPP;
goto qreparse_out;
} }
/* Reparse tag is NTFS symlink */ end = 2 + get_bcc(&io_rsp->hdr) + (__u8 *)&io_rsp->ByteCount;
sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + start = (__u8 *)&io_rsp->hdr.Protocol + data_offset;
reparse_buf->PathBuffer; if (start >= end) {
sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength);
if (sub_start + sub_len > end_of_smb) {
cifs_dbg(FYI, "reparse buf beyond SMB\n");
rc = -EIO; rc = -EIO;
goto qreparse_out; goto error;
} }
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
is_unicode = true;
else
is_unicode = false;
/* BB FIXME investigate remapping reserved chars here */ *tag = le32_to_cpu(((struct reparse_data_buffer *)start)->ReparseTag);
*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, rsp->iov_base = io_rsp;
nls_codepage); rsp->iov_len = io_rsp_len;
if (!*symlinkinfo) *rsp_buftype = CIFS_LARGE_BUFFER;
rc = -ENOMEM; CIFSSMBClose(xid, tcon, fid.netfid);
qreparse_out: return 0;
cifs_buf_release(pSMB);
/* error:
* Note: On -EAGAIN error only caller can retry on handle based calls cifs_buf_release(io_req);
* since file handle passed in no longer valid. CIFSSMBClose(xid, tcon, fid.netfid);
*/
return rc; return rc;
} }
......
...@@ -459,8 +459,7 @@ static int cifs_get_unix_fattr(const unsigned char *full_path, ...@@ -459,8 +459,7 @@ static int cifs_get_unix_fattr(const unsigned char *full_path,
return -EOPNOTSUPP; return -EOPNOTSUPP;
rc = server->ops->query_symlink(xid, tcon, rc = server->ops->query_symlink(xid, tcon,
cifs_sb, full_path, cifs_sb, full_path,
&fattr->cf_symlink_target, &fattr->cf_symlink_target);
NULL);
cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc); cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
} }
return rc; return rc;
...@@ -722,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, ...@@ -722,10 +721,51 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink); fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
} }
static inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
{
u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
return MKDEV(v >> 32, v & 0xffffffff);
}
bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb, bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct cifs_fattr *fattr,
u32 tag) struct cifs_open_info_data *data)
{ {
struct reparse_posix_data *buf = data->reparse.posix;
u32 tag = data->reparse.tag;
if (tag == IO_REPARSE_TAG_NFS && buf) {
switch (le64_to_cpu(buf->InodeType)) {
case NFS_SPECFILE_CHR:
fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_CHR;
fattr->cf_rdev = nfs_mkdev(buf);
break;
case NFS_SPECFILE_BLK:
fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_BLK;
fattr->cf_rdev = nfs_mkdev(buf);
break;
case NFS_SPECFILE_FIFO:
fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_FIFO;
break;
case NFS_SPECFILE_SOCK:
fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_SOCK;
break;
case NFS_SPECFILE_LNK:
fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
fattr->cf_dtype = DT_LNK;
break;
default:
WARN_ON_ONCE(1);
return false;
}
return true;
}
switch (tag) { switch (tag) {
case IO_REPARSE_TAG_LX_SYMLINK: case IO_REPARSE_TAG_LX_SYMLINK:
fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode; fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
...@@ -791,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, ...@@ -791,7 +831,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (cifs_open_data_reparse(data) && if (cifs_open_data_reparse(data) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag)) cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
goto out_reparse; goto out_reparse;
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
...@@ -856,7 +896,7 @@ cifs_get_file_info(struct file *filp) ...@@ -856,7 +896,7 @@ cifs_get_file_info(struct file *filp)
data.adjust_tz = false; data.adjust_tz = false;
if (data.symlink_target) { if (data.symlink_target) {
data.symlink = true; data.symlink = true;
data.reparse_tag = IO_REPARSE_TAG_SYMLINK; data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
} }
cifs_open_info_to_fattr(&fattr, &data, inode->i_sb); cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
break; break;
...@@ -1025,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, ...@@ -1025,7 +1065,7 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct kvec rsp_iov, *iov = NULL; struct kvec rsp_iov, *iov = NULL;
int rsp_buftype = CIFS_NO_BUFFER; int rsp_buftype = CIFS_NO_BUFFER;
u32 tag = data->reparse_tag; u32 tag = data->reparse.tag;
int rc = 0; int rc = 0;
if (!tag && server->ops->query_reparse_point) { if (!tag && server->ops->query_reparse_point) {
...@@ -1035,22 +1075,28 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data, ...@@ -1035,22 +1075,28 @@ static int reparse_info_to_fattr(struct cifs_open_info_data *data,
if (!rc) if (!rc)
iov = &rsp_iov; iov = &rsp_iov;
} }
switch ((data->reparse_tag = tag)) {
rc = -EOPNOTSUPP;
switch ((data->reparse.tag = tag)) {
case 0: /* SMB1 symlink */ case 0: /* SMB1 symlink */
iov = NULL; if (server->ops->query_symlink) {
fallthrough;
case IO_REPARSE_TAG_NFS:
case IO_REPARSE_TAG_SYMLINK:
if (!data->symlink_target && server->ops->query_symlink) {
rc = server->ops->query_symlink(xid, tcon, rc = server->ops->query_symlink(xid, tcon,
cifs_sb, full_path, cifs_sb, full_path,
&data->symlink_target, &data->symlink_target);
iov);
} }
break; break;
case IO_REPARSE_TAG_MOUNT_POINT: case IO_REPARSE_TAG_MOUNT_POINT:
cifs_create_junction_fattr(fattr, sb); cifs_create_junction_fattr(fattr, sb);
rc = 0;
goto out; goto out;
default:
if (data->symlink_target) {
rc = 0;
} else if (server->ops->parse_reparse_point) {
rc = server->ops->parse_reparse_point(cifs_sb,
iov, data);
}
break;
} }
cifs_open_info_to_fattr(fattr, data, sb); cifs_open_info_to_fattr(fattr, data, sb);
......
...@@ -153,6 +153,10 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) ...@@ -153,6 +153,10 @@ static bool reparse_file_needs_reval(const struct cifs_fattr *fattr)
static void static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{ {
struct cifs_open_info_data data = {
.reparse = { .tag = fattr->cf_cifstag, },
};
fattr->cf_uid = cifs_sb->ctx->linux_uid; fattr->cf_uid = cifs_sb->ctx->linux_uid;
fattr->cf_gid = cifs_sb->ctx->linux_gid; fattr->cf_gid = cifs_sb->ctx->linux_gid;
...@@ -165,7 +169,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) ...@@ -165,7 +169,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
* reasonably map some of them to directories vs. files vs. symlinks * reasonably map some of them to directories vs. files vs. symlinks
*/ */
if ((fattr->cf_cifsattrs & ATTR_REPARSE) && if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
cifs_reparse_point_to_fattr(cifs_sb, fattr, fattr->cf_cifstag)) cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
goto out_reparse; goto out_reparse;
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
......
...@@ -332,10 +332,10 @@ cifs_disable_secondary_channels(struct cifs_ses *ses) ...@@ -332,10 +332,10 @@ cifs_disable_secondary_channels(struct cifs_ses *ses)
if (iface) { if (iface) {
spin_lock(&ses->iface_lock); spin_lock(&ses->iface_lock);
kref_put(&iface->refcount, release_iface);
iface->num_channels--; iface->num_channels--;
if (iface->weight_fulfilled) if (iface->weight_fulfilled)
iface->weight_fulfilled--; iface->weight_fulfilled--;
kref_put(&iface->refcount, release_iface);
spin_unlock(&ses->iface_lock); spin_unlock(&ses->iface_lock);
} }
......
...@@ -976,64 +976,37 @@ static int cifs_query_symlink(const unsigned int xid, ...@@ -976,64 +976,37 @@ static int cifs_query_symlink(const unsigned int xid,
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const char *full_path, const char *full_path,
char **target_path, char **target_path)
struct kvec *rsp_iov)
{ {
int rc; int rc;
int oplock = 0;
bool is_reparse_point = !!rsp_iov;
struct cifs_fid fid;
struct cifs_open_parms oparms;
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); cifs_tcon_dbg(FYI, "%s: path=%s\n", __func__, full_path);
if (is_reparse_point) { if (!cap_unix(tcon->ses))
cifs_dbg(VFS, "reparse points not handled for SMB1 symlinks\n");
return -EOPNOTSUPP; return -EOPNOTSUPP;
}
/* Check for unix extensions */
if (cap_unix(tcon->ses)) {
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path,
cifs_sb->local_nls, cifs_sb->local_nls, cifs_remap(cifs_sb));
cifs_remap(cifs_sb));
if (rc == -EREMOTE) if (rc == -EREMOTE)
rc = cifs_unix_dfs_readlink(xid, tcon, full_path, rc = cifs_unix_dfs_readlink(xid, tcon, full_path,
target_path, target_path, cifs_sb->local_nls);
cifs_sb->local_nls);
goto out;
}
oparms = (struct cifs_open_parms) {
.tcon = tcon,
.cifs_sb = cifs_sb,
.desired_access = FILE_READ_ATTRIBUTES,
.create_options = cifs_create_options(cifs_sb,
OPEN_REPARSE_POINT),
.disposition = FILE_OPEN,
.path = full_path,
.fid = &fid,
};
rc = CIFS_open(xid, &oparms, &oplock, NULL);
if (rc)
goto out;
rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path,
cifs_sb->local_nls);
if (rc)
goto out_close;
convert_delimiter(*target_path, '/');
out_close:
CIFSSMBClose(xid, tcon, fid.netfid);
out:
if (!rc)
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
return rc; return rc;
} }
static int cifs_parse_reparse_point(struct cifs_sb_info *cifs_sb,
struct kvec *rsp_iov,
struct cifs_open_info_data *data)
{
struct reparse_data_buffer *buf;
TRANSACT_IOCTL_RSP *io = rsp_iov->iov_base;
bool unicode = !!(io->hdr.Flags2 & SMBFLG2_UNICODE);
u32 plen = le16_to_cpu(io->ByteCount);
buf = (struct reparse_data_buffer *)((__u8 *)&io->hdr.Protocol +
le32_to_cpu(io->DataOffset));
return parse_reparse_point(buf, plen, cifs_sb, unicode, data);
}
static bool static bool
cifs_is_read_op(__u32 oplock) cifs_is_read_op(__u32 oplock)
{ {
...@@ -1068,15 +1041,7 @@ cifs_make_node(unsigned int xid, struct inode *inode, ...@@ -1068,15 +1041,7 @@ cifs_make_node(unsigned int xid, struct inode *inode,
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct inode *newinode = NULL; struct inode *newinode = NULL;
int rc = -EPERM; int rc;
struct cifs_open_info_data buf = {};
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) { if (tcon->unix_ext) {
/* /*
...@@ -1110,74 +1075,18 @@ cifs_make_node(unsigned int xid, struct inode *inode, ...@@ -1110,74 +1075,18 @@ cifs_make_node(unsigned int xid, struct inode *inode,
d_instantiate(dentry, newinode); d_instantiate(dentry, newinode);
return rc; return rc;
} }
/* /*
* SMB1 SFU emulation: should work with all servers, but only * Check if mounted with mount parm 'sfu' mount parm.
* support block and char device (no socket & fifo) * 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)) if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL))
return rc; return -EPERM;
return cifs_sfu_make_node(xid, inode, dentry, tcon,
if (!S_ISCHR(mode) && !S_ISBLK(mode)) full_path, mode, dev);
return rc;
cifs_dbg(FYI, "sfu compat create special file\n");
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,
};
if (tcon->ses->server->oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, &buf);
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.offset = 0;
io_parms.length = sizeof(struct win_dev);
iov[1].iov_base = &buf.fi;
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 */
cifs_free_open_info(&buf);
return rc;
} }
struct smb_version_operations smb1_operations = { struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel, .send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids, .compare_fids = cifs_compare_fids,
...@@ -1214,6 +1123,7 @@ struct smb_version_operations smb1_operations = { ...@@ -1214,6 +1123,7 @@ struct smb_version_operations smb1_operations = {
.is_path_accessible = cifs_is_path_accessible, .is_path_accessible = cifs_is_path_accessible,
.can_echo = cifs_can_echo, .can_echo = cifs_can_echo,
.query_path_info = cifs_query_path_info, .query_path_info = cifs_query_path_info,
.query_reparse_point = cifs_query_reparse_point,
.query_file_info = cifs_query_file_info, .query_file_info = cifs_query_file_info,
.get_srv_inum = cifs_get_srv_inum, .get_srv_inum = cifs_get_srv_inum,
.set_path_size = CIFSSMBSetEOF, .set_path_size = CIFSSMBSetEOF,
...@@ -1229,6 +1139,7 @@ struct smb_version_operations smb1_operations = { ...@@ -1229,6 +1139,7 @@ struct smb_version_operations smb1_operations = {
.rename = CIFSSMBRename, .rename = CIFSSMBRename,
.create_hardlink = CIFSCreateHardLink, .create_hardlink = CIFSCreateHardLink,
.query_symlink = cifs_query_symlink, .query_symlink = cifs_query_symlink,
.parse_reparse_point = cifs_parse_reparse_point,
.open = cifs_open_file, .open = cifs_open_file,
.set_fid = cifs_set_fid, .set_fid = cifs_set_fid,
.close = cifs_close_file, .close = cifs_close_file,
......
...@@ -555,7 +555,7 @@ static int parse_create_response(struct cifs_open_info_data *data, ...@@ -555,7 +555,7 @@ static int parse_create_response(struct cifs_open_info_data *data,
break; break;
} }
data->reparse_point = reparse_point; data->reparse_point = reparse_point;
data->reparse_tag = tag; data->reparse.tag = tag;
return rc; return rc;
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment