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;
|
||||
__u16 ReparseDataLength;
|
||||
__u16 Reserved;
|
||||
__u16 AltNameOffset;
|
||||
__u16 AltNameLen;
|
||||
__u16 TargetNameOffset;
|
||||
__u16 TargetNameLen;
|
||||
char LinkNamesBuf[1];
|
||||
__u16 SubstituteNameOffset;
|
||||
__u16 SubstituteNameLength;
|
||||
__u16 PrintNameOffset;
|
||||
__u16 PrintNameLength;
|
||||
__u32 Flags;
|
||||
char PathBuffer[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct cifs_quota_data {
|
||||
|
|
|
@ -357,13 +357,9 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,
|
|||
struct cifs_tcon *tcon,
|
||||
const unsigned char *searchName, char **syminfo,
|
||||
const struct nls_table *nls_codepage);
|
||||
#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL
|
||||
extern 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);
|
||||
#endif /* temporarily unused until cifs_symlink fixed */
|
||||
extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
__u16 fid, char **symlinkinfo,
|
||||
const struct nls_table *nls_codepage);
|
||||
extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon,
|
||||
const char *fileName, const int disposition,
|
||||
const int access_flags, const int omode,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -881,6 +881,37 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
|
|||
(__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 = {
|
||||
.send_cancel = send_nt_cancel,
|
||||
.compare_fids = cifs_compare_fids,
|
||||
|
@ -927,6 +958,7 @@ struct smb_version_operations smb1_operations = {
|
|||
.rename_pending_delete = cifs_rename_pending_delete,
|
||||
.rename = CIFSSMBRename,
|
||||
.create_hardlink = CIFSCreateHardLink,
|
||||
.query_symlink = cifs_query_symlink,
|
||||
.open = cifs_open_file,
|
||||
.set_fid = cifs_set_fid,
|
||||
.close = cifs_close_file,
|
||||
|
|
Загрузка…
Ссылка в новой задаче