diff mbox

[2/2] CIFS: Implement follow_link for nounix CIFS mounts

Message ID 1376053042-8608-2-git-send-email-pshilovsky@samba.org (mailing list archive)
State New, archived
Headers show

Commit Message

Pavel Shilovsky Aug. 9, 2013, 12:57 p.m. UTC
by using a query reparse ioctl request.

Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
---
 fs/cifs/cifspdu.h   |   11 +++---
 fs/cifs/cifsproto.h |   10 ++---
 fs/cifs/cifssmb.c   |  110 ++++++++++++++++++++++++---------------------------
 fs/cifs/smb1ops.c   |   32 +++++++++++++++
 4 files changed, 92 insertions(+), 71 deletions(-)

Comments

Jeff Layton Aug. 12, 2013, 1:59 p.m. UTC | #1
On Fri,  9 Aug 2013 16:57:22 +0400
Pavel Shilovsky <pshilovsky@samba.org> wrote:

> by using a query reparse ioctl request.
> 
> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
> ---
>  fs/cifs/cifspdu.h   |   11 +++---
>  fs/cifs/cifsproto.h |   10 ++---
>  fs/cifs/cifssmb.c   |  110 ++++++++++++++++++++++++---------------------------
>  fs/cifs/smb1ops.c   |   32 +++++++++++++++
>  4 files changed, 92 insertions(+), 71 deletions(-)
> 
> diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
> index 7e8523c..9d40a14 100644
> --- a/fs/cifs/cifspdu.h
> +++ b/fs/cifs/cifspdu.h
> @@ -1490,11 +1490,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 {
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index a82b3c0..63c19ed 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -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,
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index a89c4cb..a3d74fe 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -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;
> -			}
> +		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:
>  	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;
>  }

Took me a few minutes to understand that you were just refactoring the
code in the above function, without really changing much. Might be nice
to do that in a separate patch instead.

> -#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */
>  
>  #ifdef CONFIG_CIFS_POSIX
>  
> diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
> index 6457690..0d525c3 100644
> --- a/fs/cifs/smb1ops.c
> +++ b/fs/cifs/smb1ops.c
> @@ -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,

Other than the nit above...

Acked-by: Jeff Layton <jlayton@redhat.com>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pavel Shilovsky Aug. 14, 2013, 2:50 p.m. UTC | #2
2013/8/12 Jeff Layton <jlayton@redhat.com>:
> On Fri,  9 Aug 2013 16:57:22 +0400
> Pavel Shilovsky <pshilovsky@samba.org> wrote:
>
>> by using a query reparse ioctl request.
>>
>> Signed-off-by: Pavel Shilovsky <pshilovsky@samba.org>
>> ---
>>  fs/cifs/cifspdu.h   |   11 +++---
>>  fs/cifs/cifsproto.h |   10 ++---
>>  fs/cifs/cifssmb.c   |  110 ++++++++++++++++++++++++---------------------------
>>  fs/cifs/smb1ops.c   |   32 +++++++++++++++
>>  4 files changed, 92 insertions(+), 71 deletions(-)
>>
>> diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
>> index 7e8523c..9d40a14 100644
>> --- a/fs/cifs/cifspdu.h
>> +++ b/fs/cifs/cifspdu.h
>> @@ -1490,11 +1490,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 {
>> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
>> index a82b3c0..63c19ed 100644
>> --- a/fs/cifs/cifsproto.h
>> +++ b/fs/cifs/cifsproto.h
>> @@ -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,
>> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
>> index a89c4cb..a3d74fe 100644
>> --- a/fs/cifs/cifssmb.c
>> +++ b/fs/cifs/cifssmb.c
>> @@ -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;
>> -                     }
>> +             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:
>>       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;
>>  }
>
> Took me a few minutes to understand that you were just refactoring the
> code in the above function, without really changing much. Might be nice
> to do that in a separate patch instead.
>
>> -#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */
>>
>>  #ifdef CONFIG_CIFS_POSIX
>>
>> diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
>> index 6457690..0d525c3 100644
>> --- a/fs/cifs/smb1ops.c
>> +++ b/fs/cifs/smb1ops.c
>> @@ -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,
>
> Other than the nit above...
>
> Acked-by: Jeff Layton <jlayton@redhat.com>

Thank you for reviewing the series. As for this patch - I decided not
to create an extra patch for refactoring CIFSSMBQuerySymLink function
since nobody used it till now.
diff mbox

Patch

diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index 7e8523c..9d40a14 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -1490,11 +1490,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 {
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index a82b3c0..63c19ed 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -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,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index a89c4cb..a3d74fe 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -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;
-			}
+		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:
 	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
 
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 6457690..0d525c3 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -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,