Commit d244bf2dfbebfded05f494ffd53659fa7b1e32c1
Committed by
Steve French
1 parent
b42bf88828
Exists in
master
and in
20 other branches
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>
Showing 4 changed files with 92 additions and 71 deletions Side-by-side Diff
fs/cifs/cifspdu.h
... | ... | @@ -1495,11 +1495,12 @@ |
1495 | 1495 | __u32 ReparseTag; |
1496 | 1496 | __u16 ReparseDataLength; |
1497 | 1497 | __u16 Reserved; |
1498 | - __u16 AltNameOffset; | |
1499 | - __u16 AltNameLen; | |
1500 | - __u16 TargetNameOffset; | |
1501 | - __u16 TargetNameLen; | |
1502 | - char LinkNamesBuf[1]; | |
1498 | + __u16 SubstituteNameOffset; | |
1499 | + __u16 SubstituteNameLength; | |
1500 | + __u16 PrintNameOffset; | |
1501 | + __u16 PrintNameLength; | |
1502 | + __u32 Flags; | |
1503 | + char PathBuffer[0]; | |
1503 | 1504 | } __attribute__((packed)); |
1504 | 1505 | |
1505 | 1506 | struct cifs_quota_data { |
fs/cifs/cifsproto.h
... | ... | @@ -357,13 +357,9 @@ |
357 | 357 | struct cifs_tcon *tcon, |
358 | 358 | const unsigned char *searchName, char **syminfo, |
359 | 359 | const struct nls_table *nls_codepage); |
360 | -#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL | |
361 | -extern int CIFSSMBQueryReparseLinkInfo(const unsigned int xid, | |
362 | - struct cifs_tcon *tcon, | |
363 | - const unsigned char *searchName, | |
364 | - char *symlinkinfo, const int buflen, __u16 fid, | |
365 | - const struct nls_table *nls_codepage); | |
366 | -#endif /* temporarily unused until cifs_symlink fixed */ | |
360 | +extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, | |
361 | + __u16 fid, char **symlinkinfo, | |
362 | + const struct nls_table *nls_codepage); | |
367 | 363 | extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, |
368 | 364 | const char *fileName, const int disposition, |
369 | 365 | const int access_flags, const int omode, |
fs/cifs/cifssmb.c
... | ... | @@ -3067,7 +3067,6 @@ |
3067 | 3067 | return rc; |
3068 | 3068 | } |
3069 | 3069 | |
3070 | -#ifdef CONFIG_CIFS_SYMLINK_EXPERIMENTAL | |
3071 | 3070 | /* |
3072 | 3071 | * Recent Windows versions now create symlinks more frequently |
3073 | 3072 | * and they use the "reparse point" mechanism below. We can of course |
3074 | 3073 | |
3075 | 3074 | |
... | ... | @@ -3079,18 +3078,22 @@ |
3079 | 3078 | * it is not compiled in by default until callers fixed up and more tested. |
3080 | 3079 | */ |
3081 | 3080 | int |
3082 | -CIFSSMBQueryReparseLinkInfo(const unsigned int xid, struct cifs_tcon *tcon, | |
3083 | - const unsigned char *searchName, | |
3084 | - char *symlinkinfo, const int buflen, __u16 fid, | |
3085 | - const struct nls_table *nls_codepage) | |
3081 | +CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon, | |
3082 | + __u16 fid, char **symlinkinfo, | |
3083 | + const struct nls_table *nls_codepage) | |
3086 | 3084 | { |
3087 | 3085 | int rc = 0; |
3088 | 3086 | int bytes_returned; |
3089 | 3087 | struct smb_com_transaction_ioctl_req *pSMB; |
3090 | 3088 | struct smb_com_transaction_ioctl_rsp *pSMBr; |
3089 | + bool is_unicode; | |
3090 | + unsigned int sub_len; | |
3091 | + char *sub_start; | |
3092 | + struct reparse_data *reparse_buf; | |
3093 | + __u32 data_offset, data_count; | |
3094 | + char *end_of_smb; | |
3091 | 3095 | |
3092 | - cifs_dbg(FYI, "In Windows reparse style QueryLink for path %s\n", | |
3093 | - searchName); | |
3096 | + cifs_dbg(FYI, "In Windows reparse style QueryLink for fid %u\n", fid); | |
3094 | 3097 | rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, |
3095 | 3098 | (void **) &pSMBr); |
3096 | 3099 | if (rc) |
3097 | 3100 | |
3098 | 3101 | |
3099 | 3102 | |
3100 | 3103 | |
3101 | 3104 | |
... | ... | @@ -3119,66 +3122,55 @@ |
3119 | 3122 | (struct smb_hdr *) pSMBr, &bytes_returned, 0); |
3120 | 3123 | if (rc) { |
3121 | 3124 | cifs_dbg(FYI, "Send error in QueryReparseLinkInfo = %d\n", rc); |
3122 | - } else { /* decode response */ | |
3123 | - __u32 data_offset = le32_to_cpu(pSMBr->DataOffset); | |
3124 | - __u32 data_count = le32_to_cpu(pSMBr->DataCount); | |
3125 | - if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { | |
3126 | - /* BB also check enough total bytes returned */ | |
3127 | - rc = -EIO; /* bad smb */ | |
3128 | - goto qreparse_out; | |
3129 | - } | |
3130 | - if (data_count && (data_count < 2048)) { | |
3131 | - char *end_of_smb = 2 /* sizeof byte count */ + | |
3132 | - get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; | |
3125 | + goto qreparse_out; | |
3126 | + } | |
3133 | 3127 | |
3134 | - struct reparse_data *reparse_buf = | |
3135 | - (struct reparse_data *) | |
3136 | - ((char *)&pSMBr->hdr.Protocol | |
3137 | - + data_offset); | |
3138 | - if ((char *)reparse_buf >= end_of_smb) { | |
3139 | - rc = -EIO; | |
3140 | - goto qreparse_out; | |
3141 | - } | |
3142 | - if ((reparse_buf->LinkNamesBuf + | |
3143 | - reparse_buf->TargetNameOffset + | |
3144 | - reparse_buf->TargetNameLen) > end_of_smb) { | |
3145 | - cifs_dbg(FYI, "reparse buf beyond SMB\n"); | |
3146 | - rc = -EIO; | |
3147 | - goto qreparse_out; | |
3148 | - } | |
3149 | - | |
3150 | - if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) { | |
3151 | - cifs_from_ucs2(symlinkinfo, (__le16 *) | |
3152 | - (reparse_buf->LinkNamesBuf + | |
3153 | - reparse_buf->TargetNameOffset), | |
3154 | - buflen, | |
3155 | - reparse_buf->TargetNameLen, | |
3156 | - nls_codepage, 0); | |
3157 | - } else { /* ASCII names */ | |
3158 | - strncpy(symlinkinfo, | |
3159 | - reparse_buf->LinkNamesBuf + | |
3160 | - reparse_buf->TargetNameOffset, | |
3161 | - min_t(const int, buflen, | |
3162 | - reparse_buf->TargetNameLen)); | |
3163 | - } | |
3164 | - } else { | |
3165 | - rc = -EIO; | |
3166 | - cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); | |
3167 | - } | |
3168 | - symlinkinfo[buflen] = 0; /* just in case so the caller | |
3169 | - does not go off the end of the buffer */ | |
3170 | - cifs_dbg(FYI, "readlink result - %s\n", symlinkinfo); | |
3128 | + data_offset = le32_to_cpu(pSMBr->DataOffset); | |
3129 | + data_count = le32_to_cpu(pSMBr->DataCount); | |
3130 | + if (get_bcc(&pSMBr->hdr) < 2 || data_offset > 512) { | |
3131 | + /* BB also check enough total bytes returned */ | |
3132 | + rc = -EIO; /* bad smb */ | |
3133 | + goto qreparse_out; | |
3171 | 3134 | } |
3135 | + if (!data_count || (data_count > 2048)) { | |
3136 | + rc = -EIO; | |
3137 | + cifs_dbg(FYI, "Invalid return data count on get reparse info ioctl\n"); | |
3138 | + goto qreparse_out; | |
3139 | + } | |
3140 | + end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; | |
3141 | + reparse_buf = (struct reparse_data *) | |
3142 | + ((char *)&pSMBr->hdr.Protocol + data_offset); | |
3143 | + if ((char *)reparse_buf >= end_of_smb) { | |
3144 | + rc = -EIO; | |
3145 | + goto qreparse_out; | |
3146 | + } | |
3147 | + if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset + | |
3148 | + reparse_buf->PrintNameLength) > end_of_smb) { | |
3149 | + cifs_dbg(FYI, "reparse buf beyond SMB\n"); | |
3150 | + rc = -EIO; | |
3151 | + goto qreparse_out; | |
3152 | + } | |
3153 | + sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer; | |
3154 | + sub_len = reparse_buf->SubstituteNameLength; | |
3155 | + if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE) | |
3156 | + is_unicode = true; | |
3157 | + else | |
3158 | + is_unicode = false; | |
3172 | 3159 | |
3160 | + /* BB FIXME investigate remapping reserved chars here */ | |
3161 | + *symlinkinfo = cifs_strndup_from_utf16(sub_start, sub_len, is_unicode, | |
3162 | + nls_codepage); | |
3163 | + if (!*symlinkinfo) | |
3164 | + rc = -ENOMEM; | |
3173 | 3165 | qreparse_out: |
3174 | 3166 | cifs_buf_release(pSMB); |
3175 | 3167 | |
3176 | - /* Note: On -EAGAIN error only caller can retry on handle based calls | |
3177 | - since file handle passed in no longer valid */ | |
3178 | - | |
3168 | + /* | |
3169 | + * Note: On -EAGAIN error only caller can retry on handle based calls | |
3170 | + * since file handle passed in no longer valid. | |
3171 | + */ | |
3179 | 3172 | return rc; |
3180 | 3173 | } |
3181 | -#endif /* CIFS_SYMLINK_EXPERIMENTAL */ /* BB temporarily unused */ | |
3182 | 3174 | |
3183 | 3175 | #ifdef CONFIG_CIFS_POSIX |
3184 | 3176 |
fs/cifs/smb1ops.c
... | ... | @@ -881,6 +881,37 @@ |
881 | 881 | (__u8)type, wait, 0); |
882 | 882 | } |
883 | 883 | |
884 | +static int | |
885 | +cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, | |
886 | + const char *full_path, char **target_path, | |
887 | + struct cifs_sb_info *cifs_sb) | |
888 | +{ | |
889 | + int rc; | |
890 | + int oplock = 0; | |
891 | + __u16 netfid; | |
892 | + | |
893 | + cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); | |
894 | + | |
895 | + rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, | |
896 | + FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid, | |
897 | + &oplock, NULL, cifs_sb->local_nls, | |
898 | + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); | |
899 | + if (rc) | |
900 | + return rc; | |
901 | + | |
902 | + rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path, | |
903 | + cifs_sb->local_nls); | |
904 | + if (rc) { | |
905 | + CIFSSMBClose(xid, tcon, netfid); | |
906 | + return rc; | |
907 | + } | |
908 | + | |
909 | + convert_delimiter(*target_path, '/'); | |
910 | + CIFSSMBClose(xid, tcon, netfid); | |
911 | + cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); | |
912 | + return rc; | |
913 | +} | |
914 | + | |
884 | 915 | struct smb_version_operations smb1_operations = { |
885 | 916 | .send_cancel = send_nt_cancel, |
886 | 917 | .compare_fids = cifs_compare_fids, |
... | ... | @@ -927,6 +958,7 @@ |
927 | 958 | .rename_pending_delete = cifs_rename_pending_delete, |
928 | 959 | .rename = CIFSSMBRename, |
929 | 960 | .create_hardlink = CIFSCreateHardLink, |
961 | + .query_symlink = cifs_query_symlink, | |
930 | 962 | .open = cifs_open_file, |
931 | 963 | .set_fid = cifs_set_fid, |
932 | 964 | .close = cifs_close_file, |