CIFS: Implement follow_link for nounix CIFS mounts
by using a query reparse ioctl request. Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org> Signed-off-by: Steve French <smfrench@gmail.com>
This commit is contained in:

committed by
Steve French

parent
b42bf88828
commit
d244bf2dfb
@@ -3067,7 +3067,6 @@ querySymLinkRetry:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
|
||||
/*
|
||||
* Recent Windows versions now create symlinks more frequently
|
||||
* and they use the "reparse point" mechanism below. We can of course
|
||||
@@ -3079,18 +3078,22 @@ querySymLinkRetry:
|
||||
* it is not compiled in by default until callers fixed up and more tested.
|
||||
*/
|
||||
int
|
||||
CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const unsigned char *searchName,
|
||||
char *symlinkinfo, const int buflen, __u16 fid,
|
||||
const struct nls_table *nls_codepage)
|
||||
CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid, char **symlinkinfo,
|
||||
const struct nls_table *nls_codepage)
|
||||
{
|
||||
int rc = 0;
|
||||
int bytes_returned;
|
||||
struct smb_com_transaction_ioctl_req *pSMB;
|
||||
struct smb_com_transaction_ioctl_rsp *pSMBr;
|
||||
bool is_unicode;
|
||||
unsigned int sub_len;
|
||||
char *sub_start;
|
||||
struct reparse_data *reparse_buf;
|
||||
__u32 data_offset, data_count;
|
||||
char *end_of_smb;
|
||||
|
||||
cifs_dbg(FYI, "In Windows reparse style QueryLink for path %s\n",
|
||||
searchName);
|
||||
cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
|
||||
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
|
||||
(void **) &pSMBr);
|
||||
if (rc)
|
||||
@@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
|
||||
} else { /* decode response */
|
||||
__u32 data_offset = le32_to_cpu(pSMBr->DataOffset);
|
||||
__u32 data_count = le32_to_cpu(pSMBr->DataCount);
|
||||
if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
|
||||
/* BB also check enough total bytes returned */
|
||||
rc = -EIO; /* bad smb */
|
||||
goto qreparse_out;
|
||||
}
|
||||
if (data_count && (data_count < 2048)) {
|
||||
char *end_of_smb = 2 /* sizeof byte count */ +
|
||||
get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
|
||||
|
||||
struct reparse_data *reparse_buf =
|
||||
(struct reparse_data *)
|
||||
((char *)&pSMBr->hdr.Protocol
|
||||
+ data_offset);
|
||||
if ((char *)reparse_buf >= end_of_smb) {
|
||||
rc = -EIO;
|
||||
goto qreparse_out;
|
||||
}
|
||||
if ((reparse_buf->LinkNamesBuf +
|
||||
reparse_buf->TargetNameOffset +
|
||||
reparse_buf->TargetNameLen) > end_of_smb) {
|
||||
cifs_dbg(FYI, "reparse buf beyond SMB\n");
|
||||
rc = -EIO;
|
||||
goto qreparse_out;
|
||||
}
|
||||
|
||||
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) {
|
||||
cifs_from_ucs2(symlinkinfo, (__le16 *)
|
||||
(reparse_buf->LinkNamesBuf +
|
||||
reparse_buf->TargetNameOffset),
|
||||
buflen,
|
||||
reparse_buf->TargetNameLen,
|
||||
nls_codepage, 0);
|
||||
} else { /* ASCII names */
|
||||
strncpy(symlinkinfo,
|
||||
reparse_buf->LinkNamesBuf +
|
||||
reparse_buf->TargetNameOffset,
|
||||
min_t(const int, buflen,
|
||||
reparse_buf->TargetNameLen));
|
||||
}
|
||||
} else {
|
||||
rc = -EIO;
|
||||
cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
|
||||
}
|
||||
symlinkinfo[buflen] = 0; /* just in case so the caller
|
||||
does not go off the end of the buffer */
|
||||
cifs_dbg(FYI, "readlink result - %s\n", symlinkinfo);
|
||||
goto qreparse_out;
|
||||
}
|
||||
|
||||
data_offset = le32_to_cpu(pSMBr->DataOffset);
|
||||
data_count = le32_to_cpu(pSMBr->DataCount);
|
||||
if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) {
|
||||
/* BB also check enough total bytes returned */
|
||||
rc = -EIO; /* bad smb */
|
||||
goto qreparse_out;
|
||||
}
|
||||
if (!data_count || (data_count > 2048)) {
|
||||
rc = -EIO;
|
||||
cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n");
|
||||
goto qreparse_out;
|
||||
}
|
||||
end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount;
|
||||
reparse_buf = (struct reparse_data *)
|
||||
((char *)&pSMBr->hdr.Protocol + data_offset);
|
||||
if ((char *)reparse_buf >= end_of_smb) {
|
||||
rc = -EIO;
|
||||
goto qreparse_out;
|
||||
}
|
||||
if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset +
|
||||
reparse_buf->PrintNameLength) > end_of_smb) {
|
||||
cifs_dbg(FYI, "reparse buf beyond SMB\n");
|
||||
rc = -EIO;
|
||||
goto qreparse_out;
|
||||
}
|
||||
sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer;
|
||||
sub_len = reparse_buf->SubstituteNameLength;
|
||||
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
||||
is_unicode = true;
|
||||
else
|
||||
is_unicode = false;
|
||||
|
||||
/* BB FIXME investigate remapping reserved chars here */
|
||||
*symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode,
|
||||
nls_codepage);
|
||||
if (!*symlinkinfo)
|
||||
rc = -ENOMEM;
|
||||
qreparse_out:
|
||||
cifs_buf_release(pSMB);
|
||||
|
||||
/* Note: On -EAGAIN error only caller can retry on handle based calls
|
||||
since file handle passed in no longer valid */
|
||||
|
||||
/*
|
||||
* Note: On -EAGAIN error only caller can retry on handle based calls
|
||||
* since file handle passed in no longer valid.
|
||||
*/
|
||||
return rc;
|
||||
}
|
||||
#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */
|
||||
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
|
||||
|
Reference in New Issue
Block a user