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:
Родитель
b42bf88828
Коммит
d244bf2dfb
|
@ -1495,11 +1495,12 @@ struct reparse_data {
|
||||||
__u32 ReparseTag;
|
__u32 ReparseTag;
|
||||||
__u16 ReparseDataLength;
|
__u16 ReparseDataLength;
|
||||||
__u16 Reserved;
|
__u16 Reserved;
|
||||||
__u16 AltNameOffset;
|
__u16 SubstituteNameOffset;
|
||||||
__u16 AltNameLen;
|
__u16 SubstituteNameLength;
|
||||||
__u16 TargetNameOffset;
|
__u16 PrintNameOffset;
|
||||||
__u16 TargetNameLen;
|
__u16 PrintNameLength;
|
||||||
char LinkNamesBuf[1];
|
__u32 Flags;
|
||||||
|
char PathBuffer[0];
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct cifs_quota_data {
|
struct cifs_quota_data {
|
||||||
|
|
|
@ -357,13 +357,9 @@ 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);
|
const struct nls_table *nls_codepage);
|
||||||
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
|
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
extern int CIFSSMBQueryReparseLinkInfo(const unsigned int xid,
|
__u16 fid, char **symlinkinfo,
|
||||||
struct cifs_tcon *tcon,
|
const struct nls_table *nls_codepage);
|
||||||
const unsigned char *searchName,
|
|
||||||
char *symlinkinfo, const int buflen, __u16 fid,
|
|
||||||
const struct nls_table *nls_codepage);
|
|
||||||
#endif /* temporarily unused until cifs_symlink fixed */
|
|
||||||
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
|
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const char *fileName, const int disposition,
|
const char *fileName, const int disposition,
|
||||||
const int access_flags, const int omode,
|
const int access_flags, const int omode,
|
||||||
|
|
|
@ -3067,7 +3067,6 @@ querySymLinkRetry:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
|
|
||||||
/*
|
/*
|
||||||
* Recent Windows versions now create symlinks more frequently
|
* Recent Windows versions now create symlinks more frequently
|
||||||
* and they use the "reparse point" mechanism below. We can of course
|
* 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.
|
* it is not compiled in by default until callers fixed up and more tested.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
const unsigned char *searchName,
|
__u16 fid, char **symlinkinfo,
|
||||||
char *symlinkinfo, const int buflen, __u16 fid,
|
const struct nls_table *nls_codepage)
|
||||||
const struct nls_table *nls_codepage)
|
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int bytes_returned;
|
int bytes_returned;
|
||||||
struct smb_com_transaction_ioctl_req *pSMB;
|
struct smb_com_transaction_ioctl_req *pSMB;
|
||||||
struct smb_com_transaction_ioctl_rsp *pSMBr;
|
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",
|
cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid);
|
||||||
searchName);
|
|
||||||
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
|
rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB,
|
||||||
(void **) &pSMBr);
|
(void **) &pSMBr);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -3119,66 +3122,55 @@ CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
|
cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc);
|
||||||
} else { /* decode response */
|
goto qreparse_out;
|
||||||
__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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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:
|
qreparse_out:
|
||||||
cifs_buf_release(pSMB);
|
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;
|
return rc;
|
||||||
}
|
}
|
||||||
#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */
|
|
||||||
|
|
||||||
#ifdef CONFIG_CIFS_POSIX
|
#ifdef CONFIG_CIFS_POSIX
|
||||||
|
|
||||||
|
|
|
@ -881,6 +881,37 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
||||||
(__u8)type, wait, 0);
|
(__u8)type, wait, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||||
|
const char *full_path, char **target_path,
|
||||||
|
struct cifs_sb_info *cifs_sb)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int oplock = 0;
|
||||||
|
__u16 netfid;
|
||||||
|
|
||||||
|
cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path);
|
||||||
|
|
||||||
|
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
|
||||||
|
FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid,
|
||||||
|
&oplock, NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path,
|
||||||
|
cifs_sb->local_nls);
|
||||||
|
if (rc) {
|
||||||
|
CIFSSMBClose(xid, tcon, netfid);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
convert_delimiter(*target_path, '/');
|
||||||
|
CIFSSMBClose(xid, tcon, netfid);
|
||||||
|
cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);
|
||||||
|
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,
|
||||||
|
@ -927,6 +958,7 @@ struct smb_version_operations smb1_operations = {
|
||||||
.rename_pending_delete = cifs_rename_pending_delete,
|
.rename_pending_delete = cifs_rename_pending_delete,
|
||||||
.rename = CIFSSMBRename,
|
.rename = CIFSSMBRename,
|
||||||
.create_hardlink = CIFSCreateHardLink,
|
.create_hardlink = CIFSCreateHardLink,
|
||||||
|
.query_symlink = cifs_query_symlink,
|
||||||
.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,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче