hc
2024-02-20 102a0743326a03cd1a1202ceda21e175b7d3575c
kernel/fs/cifs/readdir.c
....@@ -32,6 +32,7 @@
3232 #include "cifs_debug.h"
3333 #include "cifs_fs_sb.h"
3434 #include "cifsfs.h"
35
+#include "smb2proto.h"
3536
3637 /*
3738 * To be safe - for UCS to UTF-8 with strings loaded with the rare long
....@@ -52,7 +53,7 @@
5253 return;
5354 }
5455 if (cf->invalidHandle)
55
- cifs_dbg(FYI, "invalid handle\n");
56
+ cifs_dbg(FYI, "Invalid handle\n");
5657 if (cf->srch_inf.endOfSearch)
5758 cifs_dbg(FYI, "end of search\n");
5859 if (cf->srch_inf.emptyDir)
....@@ -139,16 +140,61 @@
139140 dput(dentry);
140141 }
141142
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
+
142165 static void
143166 cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
144167 {
145168 fattr->cf_uid = cifs_sb->mnt_uid;
146169 fattr->cf_gid = cifs_sb->mnt_gid;
147170
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
+ */
148179 if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
149180 fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
150181 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? */
152198 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
153199 fattr->cf_dtype = DT_REG;
154200 }
....@@ -158,7 +204,7 @@
158204 * is a symbolic link, DFS referral or a reparse point with a direct
159205 * access like junctions, deduplicated files, NFS symlinks.
160206 */
161
- if (fattr->cf_cifsattrs & ATTR_REPARSE)
207
+ if (reparse_file_needs_reval(fattr))
162208 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
163209
164210 /* non-unix readdir doesn't provide nlink */
....@@ -174,7 +220,8 @@
174220 * may look wrong since the inodes may not have timed out by the time
175221 * "ls" does a stat() call on them.
176222 */
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))
178225 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
179226
180227 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
....@@ -194,19 +241,90 @@
194241 }
195242 }
196243
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
+
197311 void
198312 cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
199313 struct cifs_sb_info *cifs_sb)
200314 {
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
+}
209318
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);
210328 cifs_fill_common_info(fattr, cifs_sb);
211329 }
212330
....@@ -264,11 +382,11 @@
264382 */
265383
266384 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)
268387 {
269388 __u16 search_flags;
270389 int rc = 0;
271
- char *full_path = NULL;
272390 struct cifsFileInfo *cifsFile;
273391 struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
274392 struct tcon_link *tlink = NULL;
....@@ -304,12 +422,6 @@
304422 cifsFile->invalidHandle = true;
305423 cifsFile->srch_inf.endOfSearch = false;
306424
307
- full_path = build_path_from_dentry(file_dentry(file));
308
- if (full_path == NULL) {
309
- rc = -ENOMEM;
310
- goto error_exit;
311
- }
312
-
313425 cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
314426
315427 ffirst_retry:
....@@ -318,6 +430,8 @@
318430 /* if (cap_unix(tcon->ses) { */
319431 if (tcon->unix_ext)
320432 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;
321435 else if ((tcon->ses->capabilities &
322436 tcon->ses->server->vals->cap_nt_find) == 0) {
323437 cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
....@@ -346,7 +460,6 @@
346460 goto ffirst_retry;
347461 }
348462 error_exit:
349
- kfree(full_path);
350463 cifs_put_tlink(tlink);
351464 return rc;
352465 }
....@@ -380,7 +493,7 @@
380493 u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset);
381494
382495 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);
384497 return NULL;
385498 }
386499 new_entry = old_entry + next_offset;
....@@ -409,6 +522,23 @@
409522 u32 resume_key;
410523 u64 ino;
411524 };
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
+}
412542
413543 static void cifs_fill_dirent_unix(struct cifs_dirent *de,
414544 const FILE_UNIX_INFO *info, bool is_unicode)
....@@ -470,6 +600,9 @@
470600 memset(de, 0, sizeof(*de));
471601
472602 switch (level) {
603
+ case SMB_FIND_FILE_POSIX_INFO:
604
+ cifs_fill_dirent_posix(de, info);
605
+ break;
473606 case SMB_FIND_FILE_UNIX:
474607 cifs_fill_dirent_unix(de, info, is_unicode);
475608 break;
....@@ -570,7 +703,8 @@
570703 */
571704 static int
572705 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)
574708 {
575709 __u16 search_flags;
576710 int rc = 0;
....@@ -623,7 +757,7 @@
623757 ntwrk_buf_start);
624758 cfile->srch_inf.ntwrk_buf_start = NULL;
625759 }
626
- rc = initiate_cifs_search(xid, file);
760
+ rc = initiate_cifs_search(xid, file, full_path);
627761 if (rc) {
628762 cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
629763 rc);
....@@ -745,6 +879,11 @@
745879 }
746880
747881 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;
748887 case SMB_FIND_FILE_UNIX:
749888 cifs_unix_basic_to_fattr(&fattr,
750889 &((FILE_UNIX_INFO *)find_entry)->basic,
....@@ -754,6 +893,11 @@
754893 cifs_std_info_to_fattr(&fattr,
755894 (FIND_FILE_STANDARD_INFO *)find_entry,
756895 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);
757901 break;
758902 default:
759903 cifs_dir_info_to_fattr(&fattr,
....@@ -797,15 +941,22 @@
797941 char *tmp_buf = NULL;
798942 char *end_of_smb;
799943 unsigned int max_len;
944
+ char *full_path = NULL;
800945
801946 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
+ }
802953
803954 /*
804955 * Ensure FindFirst doesn't fail before doing filldir() for '.' and
805956 * '..'. Otherwise we won't be able to notify VFS in case of failure.
806957 */
807958 if (file->private_data == NULL) {
808
- rc = initiate_cifs_search(xid, file);
959
+ rc = initiate_cifs_search(xid, file, full_path);
809960 cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
810961 if (rc)
811962 goto rddir2_exit;
....@@ -832,15 +983,15 @@
832983 } */
833984
834985 tcon = tlink_tcon(cifsFile->tlink);
835
- rc = find_cifs_entry(xid, tcon, ctx->pos, file, &current_entry,
836
- &num_to_fill);
986
+ rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
987
+ &current_entry, &num_to_fill);
837988 if (rc) {
838989 cifs_dbg(FYI, "fce error %d\n", rc);
839990 goto rddir2_exit;
840991 } else if (current_entry != NULL) {
841992 cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
842993 } else {
843
- cifs_dbg(FYI, "could not find entry\n");
994
+ cifs_dbg(FYI, "Could not find entry\n");
844995 goto rddir2_exit;
845996 }
846997 cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
....@@ -891,6 +1042,7 @@
8911042 kfree(tmp_buf);
8921043
8931044 rddir2_exit:
1045
+ kfree(full_path);
8941046 free_xid(xid);
8951047 return rc;
8961048 }