.. | .. |
---|
32 | 32 | #include "cifs_debug.h" |
---|
33 | 33 | #include "cifs_fs_sb.h" |
---|
34 | 34 | #include "cifsfs.h" |
---|
| 35 | +#include "smb2proto.h" |
---|
35 | 36 | |
---|
36 | 37 | /* |
---|
37 | 38 | * To be safe - for UCS to UTF-8 with strings loaded with the rare long |
---|
.. | .. |
---|
52 | 53 | return; |
---|
53 | 54 | } |
---|
54 | 55 | if (cf->invalidHandle) |
---|
55 | | - cifs_dbg(FYI, "invalid handle\n"); |
---|
| 56 | + cifs_dbg(FYI, "Invalid handle\n"); |
---|
56 | 57 | if (cf->srch_inf.endOfSearch) |
---|
57 | 58 | cifs_dbg(FYI, "end of search\n"); |
---|
58 | 59 | if (cf->srch_inf.emptyDir) |
---|
.. | .. |
---|
139 | 140 | dput(dentry); |
---|
140 | 141 | } |
---|
141 | 142 | |
---|
| 143 | +static bool reparse_file_needs_reval(const struct cifs_fattr *fattr) |
---|
| 144 | +{ |
---|
| 145 | + if (!(fattr->cf_cifsattrs & ATTR_REPARSE)) |
---|
| 146 | + return false; |
---|
| 147 | + /* |
---|
| 148 | + * The DFS tags should be only intepreted by server side as per |
---|
| 149 | + * MS-FSCC 2.1.2.1, but let's include them anyway. |
---|
| 150 | + * |
---|
| 151 | + * Besides, if cf_cifstag is unset (0), then we still need it to be |
---|
| 152 | + * revalidated to know exactly what reparse point it is. |
---|
| 153 | + */ |
---|
| 154 | + switch (fattr->cf_cifstag) { |
---|
| 155 | + case IO_REPARSE_TAG_DFS: |
---|
| 156 | + case IO_REPARSE_TAG_DFSR: |
---|
| 157 | + case IO_REPARSE_TAG_SYMLINK: |
---|
| 158 | + case IO_REPARSE_TAG_NFS: |
---|
| 159 | + case 0: |
---|
| 160 | + return true; |
---|
| 161 | + } |
---|
| 162 | + return false; |
---|
| 163 | +} |
---|
| 164 | + |
---|
142 | 165 | static void |
---|
143 | 166 | cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) |
---|
144 | 167 | { |
---|
145 | 168 | fattr->cf_uid = cifs_sb->mnt_uid; |
---|
146 | 169 | fattr->cf_gid = cifs_sb->mnt_gid; |
---|
147 | 170 | |
---|
| 171 | + /* |
---|
| 172 | + * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they |
---|
| 173 | + * are preferred by the Linux client in some cases since, unlike |
---|
| 174 | + * the NFS reparse tag (or EAs), they don't require an extra query |
---|
| 175 | + * to determine which type of special file they represent. |
---|
| 176 | + * TODO: go through all documented reparse tags to see if we can |
---|
| 177 | + * reasonably map some of them to directories vs. files vs. symlinks |
---|
| 178 | + */ |
---|
148 | 179 | if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
---|
149 | 180 | fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; |
---|
150 | 181 | fattr->cf_dtype = DT_DIR; |
---|
151 | | - } else { |
---|
| 182 | + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_SYMLINK) { |
---|
| 183 | + fattr->cf_mode |= S_IFLNK | cifs_sb->mnt_file_mode; |
---|
| 184 | + fattr->cf_dtype = DT_LNK; |
---|
| 185 | + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_FIFO) { |
---|
| 186 | + fattr->cf_mode |= S_IFIFO | cifs_sb->mnt_file_mode; |
---|
| 187 | + fattr->cf_dtype = DT_FIFO; |
---|
| 188 | + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_AF_UNIX) { |
---|
| 189 | + fattr->cf_mode |= S_IFSOCK | cifs_sb->mnt_file_mode; |
---|
| 190 | + fattr->cf_dtype = DT_SOCK; |
---|
| 191 | + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_CHR) { |
---|
| 192 | + fattr->cf_mode |= S_IFCHR | cifs_sb->mnt_file_mode; |
---|
| 193 | + fattr->cf_dtype = DT_CHR; |
---|
| 194 | + } else if (fattr->cf_cifstag == IO_REPARSE_TAG_LX_BLK) { |
---|
| 195 | + fattr->cf_mode |= S_IFBLK | cifs_sb->mnt_file_mode; |
---|
| 196 | + fattr->cf_dtype = DT_BLK; |
---|
| 197 | + } else { /* TODO: should we mark some other reparse points (like DFSR) as directories? */ |
---|
152 | 198 | fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; |
---|
153 | 199 | fattr->cf_dtype = DT_REG; |
---|
154 | 200 | } |
---|
.. | .. |
---|
158 | 204 | * is a symbolic link, DFS referral or a reparse point with a direct |
---|
159 | 205 | * access like junctions, deduplicated files, NFS symlinks. |
---|
160 | 206 | */ |
---|
161 | | - if (fattr->cf_cifsattrs & ATTR_REPARSE) |
---|
| 207 | + if (reparse_file_needs_reval(fattr)) |
---|
162 | 208 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; |
---|
163 | 209 | |
---|
164 | 210 | /* non-unix readdir doesn't provide nlink */ |
---|
.. | .. |
---|
174 | 220 | * may look wrong since the inodes may not have timed out by the time |
---|
175 | 221 | * "ls" does a stat() call on them. |
---|
176 | 222 | */ |
---|
177 | | - if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) |
---|
| 223 | + if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) || |
---|
| 224 | + (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) |
---|
178 | 225 | fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; |
---|
179 | 226 | |
---|
180 | 227 | if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL && |
---|
.. | .. |
---|
194 | 241 | } |
---|
195 | 242 | } |
---|
196 | 243 | |
---|
| 244 | +/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */ |
---|
| 245 | +static void |
---|
| 246 | +cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info, |
---|
| 247 | + struct cifs_sb_info *cifs_sb) |
---|
| 248 | +{ |
---|
| 249 | + struct smb2_posix_info_parsed parsed; |
---|
| 250 | + |
---|
| 251 | + posix_info_parse(info, NULL, &parsed); |
---|
| 252 | + |
---|
| 253 | + memset(fattr, 0, sizeof(*fattr)); |
---|
| 254 | + fattr->cf_uniqueid = le64_to_cpu(info->Inode); |
---|
| 255 | + fattr->cf_bytes = le64_to_cpu(info->AllocationSize); |
---|
| 256 | + fattr->cf_eof = le64_to_cpu(info->EndOfFile); |
---|
| 257 | + |
---|
| 258 | + fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); |
---|
| 259 | + fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); |
---|
| 260 | + fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime); |
---|
| 261 | + |
---|
| 262 | + fattr->cf_nlink = le32_to_cpu(info->HardLinks); |
---|
| 263 | + fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes); |
---|
| 264 | + |
---|
| 265 | + /* |
---|
| 266 | + * Since we set the inode type below we need to mask off |
---|
| 267 | + * to avoid strange results if bits set above. |
---|
| 268 | + * XXX: why not make server&client use the type bits? |
---|
| 269 | + */ |
---|
| 270 | + fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT; |
---|
| 271 | + |
---|
| 272 | + cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n", |
---|
| 273 | + le32_to_cpu(info->DeviceId), |
---|
| 274 | + le32_to_cpu(info->ReparseTag), |
---|
| 275 | + le32_to_cpu(info->Mode)); |
---|
| 276 | + |
---|
| 277 | + if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { |
---|
| 278 | + fattr->cf_mode |= S_IFDIR; |
---|
| 279 | + fattr->cf_dtype = DT_DIR; |
---|
| 280 | + } else { |
---|
| 281 | + /* |
---|
| 282 | + * mark anything that is not a dir as regular |
---|
| 283 | + * file. special files should have the REPARSE |
---|
| 284 | + * attribute and will be marked as needing revaluation |
---|
| 285 | + */ |
---|
| 286 | + fattr->cf_mode |= S_IFREG; |
---|
| 287 | + fattr->cf_dtype = DT_REG; |
---|
| 288 | + } |
---|
| 289 | + |
---|
| 290 | + if (reparse_file_needs_reval(fattr)) |
---|
| 291 | + fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; |
---|
| 292 | + |
---|
| 293 | + sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER); |
---|
| 294 | + sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP); |
---|
| 295 | +} |
---|
| 296 | + |
---|
| 297 | +static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) |
---|
| 298 | +{ |
---|
| 299 | + const FILE_DIRECTORY_INFO *fi = info; |
---|
| 300 | + |
---|
| 301 | + memset(fattr, 0, sizeof(*fattr)); |
---|
| 302 | + fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes); |
---|
| 303 | + fattr->cf_eof = le64_to_cpu(fi->EndOfFile); |
---|
| 304 | + fattr->cf_bytes = le64_to_cpu(fi->AllocationSize); |
---|
| 305 | + fattr->cf_createtime = le64_to_cpu(fi->CreationTime); |
---|
| 306 | + fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime); |
---|
| 307 | + fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime); |
---|
| 308 | + fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime); |
---|
| 309 | +} |
---|
| 310 | + |
---|
197 | 311 | void |
---|
198 | 312 | cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info, |
---|
199 | 313 | struct cifs_sb_info *cifs_sb) |
---|
200 | 314 | { |
---|
201 | | - memset(fattr, 0, sizeof(*fattr)); |
---|
202 | | - fattr->cf_cifsattrs = le32_to_cpu(info->ExtFileAttributes); |
---|
203 | | - fattr->cf_eof = le64_to_cpu(info->EndOfFile); |
---|
204 | | - fattr->cf_bytes = le64_to_cpu(info->AllocationSize); |
---|
205 | | - fattr->cf_createtime = le64_to_cpu(info->CreationTime); |
---|
206 | | - fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime); |
---|
207 | | - fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime); |
---|
208 | | - fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); |
---|
| 315 | + __dir_info_to_fattr(fattr, info); |
---|
| 316 | + cifs_fill_common_info(fattr, cifs_sb); |
---|
| 317 | +} |
---|
209 | 318 | |
---|
| 319 | +static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr, |
---|
| 320 | + SEARCH_ID_FULL_DIR_INFO *info, |
---|
| 321 | + struct cifs_sb_info *cifs_sb) |
---|
| 322 | +{ |
---|
| 323 | + __dir_info_to_fattr(fattr, info); |
---|
| 324 | + |
---|
| 325 | + /* See MS-FSCC 2.4.18 FileIdFullDirectoryInformation */ |
---|
| 326 | + if (fattr->cf_cifsattrs & ATTR_REPARSE) |
---|
| 327 | + fattr->cf_cifstag = le32_to_cpu(info->EaSize); |
---|
210 | 328 | cifs_fill_common_info(fattr, cifs_sb); |
---|
211 | 329 | } |
---|
212 | 330 | |
---|
.. | .. |
---|
264 | 382 | */ |
---|
265 | 383 | |
---|
266 | 384 | static int |
---|
267 | | -initiate_cifs_search(const unsigned int xid, struct file *file) |
---|
| 385 | +initiate_cifs_search(const unsigned int xid, struct file *file, |
---|
| 386 | + char *full_path) |
---|
268 | 387 | { |
---|
269 | 388 | __u16 search_flags; |
---|
270 | 389 | int rc = 0; |
---|
271 | | - char *full_path = NULL; |
---|
272 | 390 | struct cifsFileInfo *cifsFile; |
---|
273 | 391 | struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); |
---|
274 | 392 | struct tcon_link *tlink = NULL; |
---|
.. | .. |
---|
304 | 422 | cifsFile->invalidHandle = true; |
---|
305 | 423 | cifsFile->srch_inf.endOfSearch = false; |
---|
306 | 424 | |
---|
307 | | - full_path = build_path_from_dentry(file_dentry(file)); |
---|
308 | | - if (full_path == NULL) { |
---|
309 | | - rc = -ENOMEM; |
---|
310 | | - goto error_exit; |
---|
311 | | - } |
---|
312 | | - |
---|
313 | 425 | cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos); |
---|
314 | 426 | |
---|
315 | 427 | ffirst_retry: |
---|
.. | .. |
---|
318 | 430 | /* if (cap_unix(tcon->ses) { */ |
---|
319 | 431 | if (tcon->unix_ext) |
---|
320 | 432 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; |
---|
| 433 | + else if (tcon->posix_extensions) |
---|
| 434 | + cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO; |
---|
321 | 435 | else if ((tcon->ses->capabilities & |
---|
322 | 436 | tcon->ses->server->vals->cap_nt_find) == 0) { |
---|
323 | 437 | cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; |
---|
.. | .. |
---|
346 | 460 | goto ffirst_retry; |
---|
347 | 461 | } |
---|
348 | 462 | error_exit: |
---|
349 | | - kfree(full_path); |
---|
350 | 463 | cifs_put_tlink(tlink); |
---|
351 | 464 | return rc; |
---|
352 | 465 | } |
---|
.. | .. |
---|
380 | 493 | u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset); |
---|
381 | 494 | |
---|
382 | 495 | if (old_entry + next_offset < old_entry) { |
---|
383 | | - cifs_dbg(VFS, "invalid offset %u\n", next_offset); |
---|
| 496 | + cifs_dbg(VFS, "Invalid offset %u\n", next_offset); |
---|
384 | 497 | return NULL; |
---|
385 | 498 | } |
---|
386 | 499 | new_entry = old_entry + next_offset; |
---|
.. | .. |
---|
409 | 522 | u32 resume_key; |
---|
410 | 523 | u64 ino; |
---|
411 | 524 | }; |
---|
| 525 | + |
---|
| 526 | +static void cifs_fill_dirent_posix(struct cifs_dirent *de, |
---|
| 527 | + const struct smb2_posix_info *info) |
---|
| 528 | +{ |
---|
| 529 | + struct smb2_posix_info_parsed parsed; |
---|
| 530 | + |
---|
| 531 | + /* payload should have already been checked at this point */ |
---|
| 532 | + if (posix_info_parse(info, NULL, &parsed) < 0) { |
---|
| 533 | + cifs_dbg(VFS, "Invalid POSIX info payload\n"); |
---|
| 534 | + return; |
---|
| 535 | + } |
---|
| 536 | + |
---|
| 537 | + de->name = parsed.name; |
---|
| 538 | + de->namelen = parsed.name_len; |
---|
| 539 | + de->resume_key = info->Ignored; |
---|
| 540 | + de->ino = le64_to_cpu(info->Inode); |
---|
| 541 | +} |
---|
412 | 542 | |
---|
413 | 543 | static void cifs_fill_dirent_unix(struct cifs_dirent *de, |
---|
414 | 544 | const FILE_UNIX_INFO *info, bool is_unicode) |
---|
.. | .. |
---|
470 | 600 | memset(de, 0, sizeof(*de)); |
---|
471 | 601 | |
---|
472 | 602 | switch (level) { |
---|
| 603 | + case SMB_FIND_FILE_POSIX_INFO: |
---|
| 604 | + cifs_fill_dirent_posix(de, info); |
---|
| 605 | + break; |
---|
473 | 606 | case SMB_FIND_FILE_UNIX: |
---|
474 | 607 | cifs_fill_dirent_unix(de, info, is_unicode); |
---|
475 | 608 | break; |
---|
.. | .. |
---|
570 | 703 | */ |
---|
571 | 704 | static int |
---|
572 | 705 | find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos, |
---|
573 | | - struct file *file, char **current_entry, int *num_to_ret) |
---|
| 706 | + struct file *file, char *full_path, |
---|
| 707 | + char **current_entry, int *num_to_ret) |
---|
574 | 708 | { |
---|
575 | 709 | __u16 search_flags; |
---|
576 | 710 | int rc = 0; |
---|
.. | .. |
---|
623 | 757 | ntwrk_buf_start); |
---|
624 | 758 | cfile->srch_inf.ntwrk_buf_start = NULL; |
---|
625 | 759 | } |
---|
626 | | - rc = initiate_cifs_search(xid, file); |
---|
| 760 | + rc = initiate_cifs_search(xid, file, full_path); |
---|
627 | 761 | if (rc) { |
---|
628 | 762 | cifs_dbg(FYI, "error %d reinitiating a search on rewind\n", |
---|
629 | 763 | rc); |
---|
.. | .. |
---|
745 | 879 | } |
---|
746 | 880 | |
---|
747 | 881 | switch (file_info->srch_inf.info_level) { |
---|
| 882 | + case SMB_FIND_FILE_POSIX_INFO: |
---|
| 883 | + cifs_posix_to_fattr(&fattr, |
---|
| 884 | + (struct smb2_posix_info *)find_entry, |
---|
| 885 | + cifs_sb); |
---|
| 886 | + break; |
---|
748 | 887 | case SMB_FIND_FILE_UNIX: |
---|
749 | 888 | cifs_unix_basic_to_fattr(&fattr, |
---|
750 | 889 | &((FILE_UNIX_INFO *)find_entry)->basic, |
---|
.. | .. |
---|
754 | 893 | cifs_std_info_to_fattr(&fattr, |
---|
755 | 894 | (FIND_FILE_STANDARD_INFO *)find_entry, |
---|
756 | 895 | cifs_sb); |
---|
| 896 | + break; |
---|
| 897 | + case SMB_FIND_FILE_ID_FULL_DIR_INFO: |
---|
| 898 | + cifs_fulldir_info_to_fattr(&fattr, |
---|
| 899 | + (SEARCH_ID_FULL_DIR_INFO *)find_entry, |
---|
| 900 | + cifs_sb); |
---|
757 | 901 | break; |
---|
758 | 902 | default: |
---|
759 | 903 | cifs_dir_info_to_fattr(&fattr, |
---|
.. | .. |
---|
797 | 941 | char *tmp_buf = NULL; |
---|
798 | 942 | char *end_of_smb; |
---|
799 | 943 | unsigned int max_len; |
---|
| 944 | + char *full_path = NULL; |
---|
800 | 945 | |
---|
801 | 946 | xid = get_xid(); |
---|
| 947 | + |
---|
| 948 | + full_path = build_path_from_dentry(file_dentry(file)); |
---|
| 949 | + if (full_path == NULL) { |
---|
| 950 | + rc = -ENOMEM; |
---|
| 951 | + goto rddir2_exit; |
---|
| 952 | + } |
---|
802 | 953 | |
---|
803 | 954 | /* |
---|
804 | 955 | * Ensure FindFirst doesn't fail before doing filldir() for '.' and |
---|
805 | 956 | * '..'. Otherwise we won't be able to notify VFS in case of failure. |
---|
806 | 957 | */ |
---|
807 | 958 | if (file->private_data == NULL) { |
---|
808 | | - rc = initiate_cifs_search(xid, file); |
---|
| 959 | + rc = initiate_cifs_search(xid, file, full_path); |
---|
809 | 960 | cifs_dbg(FYI, "initiate cifs search rc %d\n", rc); |
---|
810 | 961 | if (rc) |
---|
811 | 962 | goto rddir2_exit; |
---|
.. | .. |
---|
832 | 983 | } */ |
---|
833 | 984 | |
---|
834 | 985 | tcon = tlink_tcon(cifsFile->tlink); |
---|
835 | | - rc = find_cifs_entry(xid, tcon, ctx->pos, file, ¤t_entry, |
---|
836 | | - &num_to_fill); |
---|
| 986 | + rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path, |
---|
| 987 | + ¤t_entry, &num_to_fill); |
---|
837 | 988 | if (rc) { |
---|
838 | 989 | cifs_dbg(FYI, "fce error %d\n", rc); |
---|
839 | 990 | goto rddir2_exit; |
---|
840 | 991 | } else if (current_entry != NULL) { |
---|
841 | 992 | cifs_dbg(FYI, "entry %lld found\n", ctx->pos); |
---|
842 | 993 | } else { |
---|
843 | | - cifs_dbg(FYI, "could not find entry\n"); |
---|
| 994 | + cifs_dbg(FYI, "Could not find entry\n"); |
---|
844 | 995 | goto rddir2_exit; |
---|
845 | 996 | } |
---|
846 | 997 | cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n", |
---|
.. | .. |
---|
891 | 1042 | kfree(tmp_buf); |
---|
892 | 1043 | |
---|
893 | 1044 | rddir2_exit: |
---|
| 1045 | + kfree(full_path); |
---|
894 | 1046 | free_xid(xid); |
---|
895 | 1047 | return rc; |
---|
896 | 1048 | } |
---|