blob: 6df0922e7e304266fac709bd43546b8ab6da3de5 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/readdir.c
3 *
4 * Directory search handling
Steve French6dc0f872007-07-06 23:13:06 +00005 *
Steve Frenchad7a2922008-02-07 23:25:02 +00006 * Copyright (C) International Business Machines Corp., 2004, 2008
Christoph Hellwigcda0ec62011-07-16 15:24:05 -04007 * Copyright (C) Red Hat, Inc., 2011
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 * Author(s): Steve French (sfrench@us.ibm.com)
9 *
10 * This library is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU Lesser General Public License as published
12 * by the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
18 * the GNU Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24#include <linux/fs.h>
Dave Kleikamp273d81d2006-06-01 19:41:23 +000025#include <linux/pagemap.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090026#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <linux/stat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include "cifspdu.h"
29#include "cifsglob.h"
30#include "cifsproto.h"
31#include "cifs_unicode.h"
32#include "cifs_debug.h"
33#include "cifs_fs_sb.h"
34#include "cifsfs.h"
Aurelien Aptel3d519bd2020-02-08 15:50:58 +010035#include "smb2proto.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Jeff Laytonf5884162009-04-30 07:18:00 -040037/*
38 * To be safe - for UCS to UTF-8 with strings loaded with the rare long
39 * characters alloc more to account for such multibyte target UTF-8
40 * characters.
41 */
42#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)
43
Steve French39798772006-05-31 22:40:51 +000044#ifdef CONFIG_CIFS_DEBUG2
45static void dump_cifs_file_struct(struct file *file, char *label)
Linus Torvalds1da177e2005-04-16 15:20:36 -070046{
Steve French6dc0f872007-07-06 23:13:06 +000047 struct cifsFileInfo *cf;
Linus Torvalds1da177e2005-04-16 15:20:36 -070048
Steve French4523cc32007-04-30 20:13:06 +000049 if (file) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 cf = file->private_data;
Steve French4523cc32007-04-30 20:13:06 +000051 if (cf == NULL) {
Joe Perchesf96637b2013-05-04 22:12:25 -050052 cifs_dbg(FYI, "empty cifs private file data\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070053 return;
54 }
Steve Frenchad7a2922008-02-07 23:25:02 +000055 if (cf->invalidHandle)
Joe Perchesa0a30362020-04-14 22:42:53 -070056 cifs_dbg(FYI, "Invalid handle\n");
Steve Frenchad7a2922008-02-07 23:25:02 +000057 if (cf->srch_inf.endOfSearch)
Joe Perchesf96637b2013-05-04 22:12:25 -050058 cifs_dbg(FYI, "end of search\n");
Steve Frenchad7a2922008-02-07 23:25:02 +000059 if (cf->srch_inf.emptyDir)
Joe Perchesf96637b2013-05-04 22:12:25 -050060 cifs_dbg(FYI, "empty dir\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070061 }
Steve French39798772006-05-31 22:40:51 +000062}
Steve French90c81e02008-02-12 20:32:36 +000063#else
64static inline void dump_cifs_file_struct(struct file *file, char *label)
65{
66}
Steve French39798772006-05-31 22:40:51 +000067#endif /* DEBUG2 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Jeff Laytoncc0bad72009-06-25 00:56:52 -040069/*
Jeff Laytoneb1b3fa2012-12-03 06:05:37 -050070 * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
71 *
Jeff Laytoncc0bad72009-06-25 00:56:52 -040072 * Find the dentry that matches "name". If there isn't one, create one. If it's
Nakajima Akira9e6d7222014-12-19 15:38:14 +090073 * a negative dentry or the uniqueid or filetype(mode) changed,
74 * then drop it and recreate it.
Jeff Laytoncc0bad72009-06-25 00:56:52 -040075 */
Jeff Laytoneb1b3fa2012-12-03 06:05:37 -050076static void
77cifs_prime_dcache(struct dentry *parent, struct qstr *name,
Jeff Laytoncc0bad72009-06-25 00:56:52 -040078 struct cifs_fattr *fattr)
79{
80 struct dentry *dentry, *alias;
81 struct inode *inode;
Al Virofc640052016-04-10 01:33:30 -040082 struct super_block *sb = parent->d_sb;
Jeff Layton2f2591a2012-12-18 06:35:10 -050083 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
Al Viro3125d262016-04-20 17:40:47 -040084 DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
Jeff Laytoncc0bad72009-06-25 00:56:52 -040085
Joe Perchesf96637b2013-05-04 22:12:25 -050086 cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
Jeff Laytoncc0bad72009-06-25 00:56:52 -040087
Al Viro4f522a22013-02-11 23:20:37 -050088 dentry = d_hash_and_lookup(parent, name);
Al Viro3125d262016-04-20 17:40:47 -040089 if (!dentry) {
90 /*
91 * If we know that the inode will need to be revalidated
92 * immediately, then don't create a new dentry for it.
93 * We'll end up doing an on the wire call either way and
94 * this spares us an invalidation.
95 */
96 if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
97 return;
98retry:
99 dentry = d_alloc_parallel(parent, name, &wq);
100 }
Viresh Kumara1c83682015-08-12 15:59:44 +0530101 if (IS_ERR(dentry))
Al Viro4f522a22013-02-11 23:20:37 -0500102 return;
Al Viro3125d262016-04-20 17:40:47 -0400103 if (!d_in_lookup(dentry)) {
David Howells2b0143b2015-03-17 22:25:59 +0000104 inode = d_inode(dentry);
Jeff Layton2f2591a2012-12-18 06:35:10 -0500105 if (inode) {
Al Viro3125d262016-04-20 17:40:47 -0400106 if (d_mountpoint(dentry)) {
107 dput(dentry);
108 return;
109 }
Jeff Layton2f2591a2012-12-18 06:35:10 -0500110 /*
111 * If we're generating inode numbers, then we don't
112 * want to clobber the existing one with the one that
113 * the readdir code created.
114 */
115 if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
116 fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;
117
Nakajima Akira9e6d7222014-12-19 15:38:14 +0900118 /* update inode in place
119 * if both i_ino and i_mode didn't change */
120 if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid &&
121 (inode->i_mode & S_IFMT) ==
122 (fattr->cf_mode & S_IFMT)) {
Jeff Layton2f2591a2012-12-18 06:35:10 -0500123 cifs_fattr_to_inode(inode, fattr);
Al Viro3125d262016-04-20 17:40:47 -0400124 dput(dentry);
125 return;
Jeff Layton2f2591a2012-12-18 06:35:10 -0500126 }
Jeff Laytoncd600422012-07-06 07:09:42 -0400127 }
Eric W. Biederman5542aa22014-02-13 09:46:25 -0800128 d_invalidate(dentry);
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400129 dput(dentry);
Al Viro3125d262016-04-20 17:40:47 -0400130 goto retry;
131 } else {
132 inode = cifs_iget(sb, fattr);
133 if (!inode)
134 inode = ERR_PTR(-ENOMEM);
135 alias = d_splice_alias(inode, dentry);
136 d_lookup_done(dentry);
137 if (alias && !IS_ERR(alias))
138 dput(alias);
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400139 }
Jeff Laytoneb1b3fa2012-12-03 06:05:37 -0500140 dput(dentry);
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400141}
142
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300143static 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
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400165static void
166cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400168 fattr->cf_uid = cifs_sb->mnt_uid;
169 fattr->cf_gid = cifs_sb->mnt_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400171 if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
172 fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
173 fattr->cf_dtype = DT_DIR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 } else {
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400175 fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
176 fattr->cf_dtype = DT_REG;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 }
178
Pavel Shilovskyeb85d94b2013-10-23 17:49:47 +0400179 /*
180 * We need to revalidate it further to make a decision about whether it
181 * is a symbolic link, DFS referral or a reparse point with a direct
182 * access like junctions, deduplicated files, NFS symlinks.
183 */
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300184 if (reparse_file_needs_reval(fattr))
Pavel Shilovskyeb85d94b2013-10-23 17:49:47 +0400185 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
186
Jim McDonough74d290d2013-09-21 10:36:10 -0500187 /* non-unix readdir doesn't provide nlink */
188 fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
189
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400190 if (fattr->cf_cifsattrs & ATTR_READONLY)
191 fattr->cf_mode &= ~S_IWUGO;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Jeff Laytonccb5c002012-11-25 08:00:40 -0500193 /*
194 * We of course don't get ACL info in FIND_FIRST/NEXT results, so
195 * mark it for revalidation so that "ls -l" will look right. It might
196 * be super-slow, but if we don't do this then the ownership of files
197 * may look wrong since the inodes may not have timed out by the time
198 * "ls" does a stat() call on them.
199 */
Aurelien Aptele3e056c2020-02-06 18:16:55 +0100200 if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
201 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID))
Jeff Laytonccb5c002012-11-25 08:00:40 -0500202 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
203
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400204 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
205 fattr->cf_cifsattrs & ATTR_SYSTEM) {
206 if (fattr->cf_eof == 0) {
207 fattr->cf_mode &= ~S_IFMT;
208 fattr->cf_mode |= S_IFIFO;
209 fattr->cf_dtype = DT_FIFO;
Steve French3020a1f2005-11-18 11:31:10 -0800210 } else {
Jeff Layton4468eb32008-05-22 09:31:40 -0400211 /*
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400212 * trying to get the type and mode via SFU can be slow,
213 * so just call those regular files for now, and mark
214 * for reval
Jeff Layton4468eb32008-05-22 09:31:40 -0400215 */
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400216 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
Jeff Layton4468eb32008-05-22 09:31:40 -0400217 }
218 }
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400219}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100221/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */
222static void
223cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
224 struct cifs_sb_info *cifs_sb)
225{
226 struct smb2_posix_info_parsed parsed;
227
228 posix_info_parse(info, NULL, &parsed);
229
230 memset(fattr, 0, sizeof(*fattr));
231 fattr->cf_uniqueid = le64_to_cpu(info->Inode);
232 fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
233 fattr->cf_eof = le64_to_cpu(info->EndOfFile);
234
235 fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
236 fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
237 fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime);
238
239 fattr->cf_nlink = le32_to_cpu(info->HardLinks);
240 fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
241
242 /*
243 * Since we set the inode type below we need to mask off
244 * to avoid strange results if bits set above.
245 * XXX: why not make server&client use the type bits?
246 */
247 fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
248
Joe Perchesa0a30362020-04-14 22:42:53 -0700249 cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100250 le32_to_cpu(info->DeviceId),
251 le32_to_cpu(info->ReparseTag),
252 le32_to_cpu(info->Mode));
253
254 if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
255 fattr->cf_mode |= S_IFDIR;
256 fattr->cf_dtype = DT_DIR;
257 } else {
258 /*
259 * mark anything that is not a dir as regular
260 * file. special files should have the REPARSE
261 * attribute and will be marked as needing revaluation
262 */
263 fattr->cf_mode |= S_IFREG;
264 fattr->cf_dtype = DT_REG;
265 }
266
267 if (reparse_file_needs_reval(fattr))
268 fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
269
270 /* TODO map SIDs */
271 fattr->cf_uid = cifs_sb->mnt_uid;
272 fattr->cf_gid = cifs_sb->mnt_gid;
273}
274
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300275static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
276{
277 const FILE_DIRECTORY_INFO *fi = info;
278
279 memset(fattr, 0, sizeof(*fattr));
280 fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes);
281 fattr->cf_eof = le64_to_cpu(fi->EndOfFile);
282 fattr->cf_bytes = le64_to_cpu(fi->AllocationSize);
283 fattr->cf_createtime = le64_to_cpu(fi->CreationTime);
284 fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime);
285 fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime);
286 fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime);
287}
288
Shirish Pargaonkarc052e2b2012-09-28 12:21:14 -0500289void
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400290cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
291 struct cifs_sb_info *cifs_sb)
292{
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300293 __dir_info_to_fattr(fattr, info);
294 cifs_fill_common_info(fattr, cifs_sb);
295}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300297static void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
298 SEARCH_ID_FULL_DIR_INFO *info,
299 struct cifs_sb_info *cifs_sb)
300{
301 __dir_info_to_fattr(fattr, info);
302
303 /* See MS-FSCC 2.4.18 FileIdFullDirectoryInformation */
304 if (fattr->cf_cifsattrs & ATTR_REPARSE)
305 fattr->cf_cifstag = le32_to_cpu(info->EaSize);
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400306 cifs_fill_common_info(fattr, cifs_sb);
307}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
Steve French15dd4782009-09-25 02:24:45 +0000309static void
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400310cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
311 struct cifs_sb_info *cifs_sb)
312{
Jeff Layton0d424ad2010-09-20 16:01:35 -0700313 int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400315 memset(fattr, 0, sizeof(*fattr));
316 fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
317 info->LastAccessTime, offset);
318 fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
319 info->LastWriteTime, offset);
320 fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
321 info->LastWriteTime, offset);
Steve Frenchf3f6ec42006-01-08 20:12:58 -0800322
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400323 fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
324 fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
325 fattr->cf_eof = le32_to_cpu(info->DataSize);
Dave Kleikamp273d81d2006-06-01 19:41:23 +0000326
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400327 cifs_fill_common_info(fattr, cifs_sb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328}
329
Steve French0e0d2cf2009-05-01 05:27:32 +0000330/* BB eventually need to add the following helper function to
331 resolve NT_STATUS_STOPPED_ON_SYMLINK return code when
332 we try to do FindFirst on (NTFS) directory symlinks */
333/*
334int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
Pavel Shilovsky6d5786a2012-06-20 11:21:16 +0400335 unsigned int xid)
Steve French0e0d2cf2009-05-01 05:27:32 +0000336{
337 __u16 fid;
338 int len;
339 int oplock = 0;
340 int rc;
Steve French96daf2b2011-05-27 04:34:02 +0000341 struct cifs_tcon *ptcon = cifs_sb_tcon(cifs_sb);
Steve French0e0d2cf2009-05-01 05:27:32 +0000342 char *tmpbuffer;
343
344 rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
345 OPEN_REPARSE_POINT, &fid, &oplock, NULL,
346 cifs_sb->local_nls,
Steve French2baa2682014-09-27 02:19:01 -0500347 cifs_remap(cifs_sb);
Steve French0e0d2cf2009-05-01 05:27:32 +0000348 if (!rc) {
349 tmpbuffer = kmalloc(maxpath);
350 rc = CIFSSMBQueryReparseLinkInfo(xid, ptcon, full_path,
351 tmpbuffer,
352 maxpath -1,
353 fid,
354 cifs_sb->local_nls);
355 if (CIFSSMBClose(xid, ptcon, fid)) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500356 cifs_dbg(FYI, "Error closing temporary reparsepoint open\n");
Steve French0e0d2cf2009-05-01 05:27:32 +0000357 }
358 }
359}
360 */
361
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700362static int
363initiate_cifs_search(const unsigned int xid, struct file *file)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
Shirish Pargaonkar2608bee2012-05-15 10:19:16 -0500365 __u16 search_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 int rc = 0;
Jeff Layton7ffec372010-09-29 19:51:11 -0400367 char *full_path = NULL;
Steve French38702532007-07-08 15:40:40 +0000368 struct cifsFileInfo *cifsFile;
Al Viro7119e222014-10-22 00:25:12 -0400369 struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
Jeff Layton59c55ba2010-11-12 06:30:29 -0500370 struct tcon_link *tlink = NULL;
Pavel Shilovsky29e20f92012-07-13 13:58:14 +0400371 struct cifs_tcon *tcon;
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700372 struct TCP_Server_Info *server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373
Jeff Layton7ffec372010-09-29 19:51:11 -0400374 if (file->private_data == NULL) {
Jeff Layton59c55ba2010-11-12 06:30:29 -0500375 tlink = cifs_sb_tlink(cifs_sb);
376 if (IS_ERR(tlink))
377 return PTR_ERR(tlink);
378
379 cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
380 if (cifsFile == NULL) {
381 rc = -ENOMEM;
382 goto error_exit;
383 }
Rabin Vincent81ddd8c2017-01-13 15:00:16 +0100384 spin_lock_init(&cifsFile->file_info_lock);
Jeff Layton59c55ba2010-11-12 06:30:29 -0500385 file->private_data = cifsFile;
386 cifsFile->tlink = cifs_get_tlink(tlink);
Pavel Shilovsky29e20f92012-07-13 13:58:14 +0400387 tcon = tlink_tcon(tlink);
Jeff Layton59c55ba2010-11-12 06:30:29 -0500388 } else {
389 cifsFile = file->private_data;
Pavel Shilovsky29e20f92012-07-13 13:58:14 +0400390 tcon = tlink_tcon(cifsFile->tlink);
Jeff Layton7ffec372010-09-29 19:51:11 -0400391 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700393 server = tcon->ses->server;
394
395 if (!server->ops->query_dir_first) {
396 rc = -ENOSYS;
397 goto error_exit;
398 }
399
Steve French4b18f2a2008-04-29 00:06:05 +0000400 cifsFile->invalidHandle = true;
401 cifsFile->srch_inf.endOfSearch = false;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Goldwyn Rodrigues1f1735c2016-04-18 06:41:52 -0500403 full_path = build_path_from_dentry(file_dentry(file));
Jeff Layton7ffec372010-09-29 19:51:11 -0400404 if (full_path == NULL) {
405 rc = -ENOMEM;
406 goto error_exit;
407 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Joe Perchesf96637b2013-05-04 22:12:25 -0500409 cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410
Steve French75cf6bd2005-04-28 22:41:04 -0700411ffirst_retry:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 /* test for Unix extensions */
Steve Frenchc18c8422007-07-18 23:21:09 +0000413 /* but now check for them on the share/mount not on the SMB session */
Pavel Shilovsky29e20f92012-07-13 13:58:14 +0400414 /* if (cap_unix(tcon->ses) { */
415 if (tcon->unix_ext)
Steve French5bafd762006-06-07 00:18:43 +0000416 cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100417 else if (tcon->posix_extensions)
418 cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO;
Pavel Shilovsky29e20f92012-07-13 13:58:14 +0400419 else if ((tcon->ses->capabilities &
420 tcon->ses->server->vals->cap_nt_find) == 0) {
Steve French5bafd762006-06-07 00:18:43 +0000421 cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 } else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
423 cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
424 } else /* not srvinos - BB fixme add check for backlevel? */ {
425 cifsFile->srch_inf.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
426 }
427
Shirish Pargaonkar2608bee2012-05-15 10:19:16 -0500428 search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
429 if (backup_cred(cifs_sb))
430 search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
431
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700432 rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
433 &cifsFile->fid, search_flags,
434 &cifsFile->srch_inf);
435
Steve French4523cc32007-04-30 20:13:06 +0000436 if (rc == 0)
Steve French4b18f2a2008-04-29 00:06:05 +0000437 cifsFile->invalidHandle = false;
Steve Frenche836f012009-05-01 16:20:35 +0000438 /* BB add following call to handle readdir on new NTFS symlink errors
Steve French0e0d2cf2009-05-01 05:27:32 +0000439 else if STATUS_STOPPED_ON_SYMLINK
440 call get_symlink_reparse_path and retry with new path */
441 else if ((rc == -EOPNOTSUPP) &&
Steve French75cf6bd2005-04-28 22:41:04 -0700442 (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
443 cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
444 goto ffirst_retry;
445 }
Jeff Layton7ffec372010-09-29 19:51:11 -0400446error_exit:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 kfree(full_path);
Jeff Layton7ffec372010-09-29 19:51:11 -0400448 cifs_put_tlink(tlink);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 return rc;
450}
451
452/* return length of unicode string in bytes */
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400453static int cifs_unicode_bytelen(const char *str)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454{
455 int len;
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400456 const __le16 *ustr = (const __le16 *)str;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457
Steve French38702532007-07-08 15:40:40 +0000458 for (len = 0; len <= PATH_MAX; len++) {
Steve French4523cc32007-04-30 20:13:06 +0000459 if (ustr[len] == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 return len << 1;
461 }
Joe Perchesf96637b2013-05-04 22:12:25 -0500462 cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 return len << 1;
464}
465
Steve French5bafd762006-06-07 00:18:43 +0000466static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467{
Steve French38702532007-07-08 15:40:40 +0000468 char *new_entry;
Steve Frenchad7a2922008-02-07 23:25:02 +0000469 FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
Steve French4523cc32007-04-30 20:13:06 +0000471 if (level == SMB_FIND_FILE_INFO_STANDARD) {
Steve Frenchad7a2922008-02-07 23:25:02 +0000472 FIND_FILE_STANDARD_INFO *pfData;
Steve French5bafd762006-06-07 00:18:43 +0000473 pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
474
475 new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
476 pfData->FileNameLength;
Dan Carpenter8ad8aa32018-09-06 12:47:51 +0300477 } else {
478 u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset);
479
480 if (old_entry + next_offset < old_entry) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700481 cifs_dbg(VFS, "Invalid offset %u\n", next_offset);
Dan Carpenter8ad8aa32018-09-06 12:47:51 +0300482 return NULL;
483 }
484 new_entry = old_entry + next_offset;
485 }
Joe Perchesf96637b2013-05-04 22:12:25 -0500486 cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700487 /* validate that new_entry is not past end of SMB */
Steve French4523cc32007-04-30 20:13:06 +0000488 if (new_entry >= end_of_smb) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500489 cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n",
490 new_entry, end_of_smb, old_entry);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 return NULL;
Steve French4523cc32007-04-30 20:13:06 +0000492 } else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
Steve French38702532007-07-08 15:40:40 +0000493 (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb))
494 || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
Steve French5bafd762006-06-07 00:18:43 +0000495 (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb))) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500496 cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n",
497 new_entry, end_of_smb);
Steve French09d1db52005-04-28 22:41:08 -0700498 return NULL;
Steve French38702532007-07-08 15:40:40 +0000499 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 return new_entry;
501
502}
503
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400504struct cifs_dirent {
505 const char *name;
506 size_t namelen;
507 u32 resume_key;
508 u64 ino;
509};
510
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100511static void cifs_fill_dirent_posix(struct cifs_dirent *de,
512 const struct smb2_posix_info *info)
513{
514 struct smb2_posix_info_parsed parsed;
515
516 /* payload should have already been checked at this point */
517 if (posix_info_parse(info, NULL, &parsed) < 0) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700518 cifs_dbg(VFS, "Invalid POSIX info payload\n");
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100519 return;
520 }
521
522 de->name = parsed.name;
523 de->namelen = parsed.name_len;
524 de->resume_key = info->Ignored;
525 de->ino = le64_to_cpu(info->Inode);
526}
527
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400528static void cifs_fill_dirent_unix(struct cifs_dirent *de,
529 const FILE_UNIX_INFO *info, bool is_unicode)
530{
531 de->name = &info->FileName[0];
532 if (is_unicode)
533 de->namelen = cifs_unicode_bytelen(de->name);
534 else
535 de->namelen = strnlen(de->name, PATH_MAX);
536 de->resume_key = info->ResumeKey;
537 de->ino = le64_to_cpu(info->basic.UniqueId);
538}
539
540static void cifs_fill_dirent_dir(struct cifs_dirent *de,
541 const FILE_DIRECTORY_INFO *info)
542{
543 de->name = &info->FileName[0];
544 de->namelen = le32_to_cpu(info->FileNameLength);
545 de->resume_key = info->FileIndex;
546}
547
548static void cifs_fill_dirent_full(struct cifs_dirent *de,
549 const FILE_FULL_DIRECTORY_INFO *info)
550{
551 de->name = &info->FileName[0];
552 de->namelen = le32_to_cpu(info->FileNameLength);
553 de->resume_key = info->FileIndex;
554}
555
556static void cifs_fill_dirent_search(struct cifs_dirent *de,
557 const SEARCH_ID_FULL_DIR_INFO *info)
558{
559 de->name = &info->FileName[0];
560 de->namelen = le32_to_cpu(info->FileNameLength);
561 de->resume_key = info->FileIndex;
562 de->ino = le64_to_cpu(info->UniqueId);
563}
564
565static void cifs_fill_dirent_both(struct cifs_dirent *de,
566 const FILE_BOTH_DIRECTORY_INFO *info)
567{
568 de->name = &info->FileName[0];
569 de->namelen = le32_to_cpu(info->FileNameLength);
570 de->resume_key = info->FileIndex;
571}
572
573static void cifs_fill_dirent_std(struct cifs_dirent *de,
574 const FIND_FILE_STANDARD_INFO *info)
575{
576 de->name = &info->FileName[0];
577 /* one byte length, no endianess conversion */
578 de->namelen = info->FileNameLength;
579 de->resume_key = info->ResumeKey;
580}
581
582static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
583 u16 level, bool is_unicode)
584{
585 memset(de, 0, sizeof(*de));
586
587 switch (level) {
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100588 case SMB_FIND_FILE_POSIX_INFO:
589 cifs_fill_dirent_posix(de, info);
590 break;
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400591 case SMB_FIND_FILE_UNIX:
592 cifs_fill_dirent_unix(de, info, is_unicode);
593 break;
594 case SMB_FIND_FILE_DIRECTORY_INFO:
595 cifs_fill_dirent_dir(de, info);
596 break;
597 case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
598 cifs_fill_dirent_full(de, info);
599 break;
600 case SMB_FIND_FILE_ID_FULL_DIR_INFO:
601 cifs_fill_dirent_search(de, info);
602 break;
603 case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
604 cifs_fill_dirent_both(de, info);
605 break;
606 case SMB_FIND_FILE_INFO_STANDARD:
607 cifs_fill_dirent_std(de, info);
608 break;
609 default:
Joe Perchesf96637b2013-05-04 22:12:25 -0500610 cifs_dbg(FYI, "Unknown findfirst level %d\n", level);
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400611 return -EINVAL;
612 }
613
614 return 0;
615}
616
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617#define UNICODE_DOT cpu_to_le16(0x2e)
618
619/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400620static int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 int rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400624 if (!de->name)
625 return 0;
626
627 if (is_unicode) {
628 __le16 *ufilename = (__le16 *)de->name;
629 if (de->namelen == 2) {
630 /* check for . */
631 if (ufilename[0] == UNICODE_DOT)
632 rc = 1;
633 } else if (de->namelen == 4) {
634 /* check for .. */
635 if (ufilename[0] == UNICODE_DOT &&
636 ufilename[1] == UNICODE_DOT)
637 rc = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 }
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400639 } else /* ASCII */ {
640 if (de->namelen == 1) {
641 if (de->name[0] == '.')
642 rc = 1;
643 } else if (de->namelen == 2) {
644 if (de->name[0] == '.' && de->name[1] == '.')
645 rc = 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 }
647 }
648
649 return rc;
650}
651
Steve Frencheafe8702005-09-15 21:47:30 -0700652/* Check if directory that we are searching has changed so we can decide
653 whether we can use the cached search results from the previous search */
Steve French38702532007-07-08 15:40:40 +0000654static int is_dir_changed(struct file *file)
Steve Frencheafe8702005-09-15 21:47:30 -0700655{
Al Viro496ad9a2013-01-23 17:07:38 -0500656 struct inode *inode = file_inode(file);
Christoph Hellwigc33f8d32007-04-02 18:47:20 +0000657 struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
Steve Frencheafe8702005-09-15 21:47:30 -0700658
Christoph Hellwigc33f8d32007-04-02 18:47:20 +0000659 if (cifsInfo->time == 0)
Steve Frencheafe8702005-09-15 21:47:30 -0700660 return 1; /* directory was changed, perhaps due to unlink */
661 else
662 return 0;
663
664}
665
Steve French0752f152008-10-07 20:03:33 +0000666static int cifs_save_resume_key(const char *current_entry,
Christoph Hellwigeaf35b12011-07-16 15:24:37 -0400667 struct cifsFileInfo *file_info)
Steve French0752f152008-10-07 20:03:33 +0000668{
Christoph Hellwigeaf35b12011-07-16 15:24:37 -0400669 struct cifs_dirent de;
670 int rc;
Steve French0752f152008-10-07 20:03:33 +0000671
Christoph Hellwigeaf35b12011-07-16 15:24:37 -0400672 rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
673 file_info->srch_inf.unicode);
674 if (!rc) {
675 file_info->srch_inf.presume_name = de.name;
676 file_info->srch_inf.resume_name_len = de.namelen;
677 file_info->srch_inf.resume_key = de.resume_key;
Steve French0752f152008-10-07 20:03:33 +0000678 }
Steve French0752f152008-10-07 20:03:33 +0000679 return rc;
680}
681
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700682/*
683 * Find the corresponding entry in the search. Note that the SMB server returns
684 * search entries for . and .. which complicates logic here if we choose to
685 * parse for them and we do not assume that they are located in the findfirst
686 * return buffer. We start counting in the buffer with entry 2 and increment for
687 * every entry (do not increment for . or .. entry).
688 */
689static int
Al Virobe4ccdc2013-05-22 16:17:25 -0400690find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700691 struct file *file, char **current_entry, int *num_to_ret)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692{
Shirish Pargaonkar2608bee2012-05-15 10:19:16 -0500693 __u16 search_flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 int rc = 0;
695 int pos_in_buf = 0;
696 loff_t first_entry_in_buffer;
Al Virobe4ccdc2013-05-22 16:17:25 -0400697 loff_t index_to_find = pos;
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700698 struct cifsFileInfo *cfile = file->private_data;
Al Viro7119e222014-10-22 00:25:12 -0400699 struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700700 struct TCP_Server_Info *server = tcon->ses->server;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 /* check if index in the buffer */
Steve French50c2f752007-07-13 00:33:32 +0000702
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700703 if (!server->ops->query_dir_first || !server->ops->query_dir_next)
704 return -ENOSYS;
705
706 if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 return -ENOENT;
Steve French50c2f752007-07-13 00:33:32 +0000708
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700709 *current_entry = NULL;
710 first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
711 cfile->srch_inf.entries_in_buffer;
Steve French60808232006-04-22 15:53:05 +0000712
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700713 /*
714 * If first entry in buf is zero then is first buffer
715 * in search response data which means it is likely . and ..
716 * will be in this buffer, although some servers do not return
717 * . and .. for the root of a drive and for those we need
718 * to start two entries earlier.
719 */
Steve French60808232006-04-22 15:53:05 +0000720
Steve French39798772006-05-31 22:40:51 +0000721 dump_cifs_file_struct(file, "In fce ");
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700722 if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
723 is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 /* close and restart search */
Joe Perchesf96637b2013-05-04 22:12:25 -0500725 cifs_dbg(FYI, "search backing up - close and restart search\n");
Steve French3afca262016-09-22 18:58:16 -0500726 spin_lock(&cfile->file_info_lock);
Pavel Shilovsky52755802014-08-18 20:49:57 +0400727 if (server->ops->dir_needs_close(cfile)) {
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700728 cfile->invalidHandle = true;
Steve French3afca262016-09-22 18:58:16 -0500729 spin_unlock(&cfile->file_info_lock);
Pavel Shilovskyf7369062014-08-26 19:04:44 +0400730 if (server->ops->close_dir)
731 server->ops->close_dir(xid, tcon, &cfile->fid);
Steve Frenchddb4cbf2008-11-20 20:00:44 +0000732 } else
Steve French3afca262016-09-22 18:58:16 -0500733 spin_unlock(&cfile->file_info_lock);
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700734 if (cfile->srch_inf.ntwrk_buf_start) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500735 cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700736 if (cfile->srch_inf.smallBuf)
737 cifs_small_buf_release(cfile->srch_inf.
Steve Frenchd47d7c12006-02-28 03:45:48 +0000738 ntwrk_buf_start);
739 else
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700740 cifs_buf_release(cfile->srch_inf.
Steve Frenchd47d7c12006-02-28 03:45:48 +0000741 ntwrk_buf_start);
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700742 cfile->srch_inf.ntwrk_buf_start = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743 }
Steve French38702532007-07-08 15:40:40 +0000744 rc = initiate_cifs_search(xid, file);
Steve French4523cc32007-04-30 20:13:06 +0000745 if (rc) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500746 cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
Joe Perchesb6b38f72010-04-21 03:50:45 +0000747 rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748 return rc;
749 }
Jeff Layton70236762011-12-01 20:23:34 -0500750 /* FindFirst/Next set last_entry to NULL on malformed reply */
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700751 if (cfile->srch_inf.last_entry)
752 cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 }
754
Shirish Pargaonkar2608bee2012-05-15 10:19:16 -0500755 search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
756 if (backup_cred(cifs_sb))
757 search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
758
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700759 while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
760 (rc == 0) && !cfile->srch_inf.endOfSearch) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500761 cifs_dbg(FYI, "calling findnext2\n");
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700762 rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
763 search_flags,
764 &cfile->srch_inf);
Jeff Layton70236762011-12-01 20:23:34 -0500765 /* FindFirst/Next set last_entry to NULL on malformed reply */
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700766 if (cfile->srch_inf.last_entry)
767 cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
Steve French4523cc32007-04-30 20:13:06 +0000768 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 return -ENOENT;
770 }
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700771 if (index_to_find < cfile->srch_inf.index_of_last_entry) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772 /* we found the buffer that contains the entry */
773 /* scan and find it */
774 int i;
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700775 char *cur_ent;
Ronnie Sahlberg59a63e472018-12-13 08:06:16 +1000776 char *end_of_smb;
777
778 if (cfile->srch_inf.ntwrk_buf_start == NULL) {
779 cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n");
780 return -EIO;
781 }
782
783 end_of_smb = cfile->srch_inf.ntwrk_buf_start +
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700784 server->ops->calc_smb_size(
Ronnie Sahlberg9ec672b2018-04-22 15:30:12 -0600785 cfile->srch_inf.ntwrk_buf_start,
786 server);
Steve French60808232006-04-22 15:53:05 +0000787
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700788 cur_ent = cfile->srch_inf.srch_entries_start;
789 first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
790 - cfile->srch_inf.entries_in_buffer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 pos_in_buf = index_to_find - first_entry_in_buffer;
Joe Perchesf96637b2013-05-04 22:12:25 -0500792 cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf);
Steve French5bafd762006-06-07 00:18:43 +0000793
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700794 for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
Steve Frenchdfb75332005-06-22 17:13:47 -0700795 /* go entry by entry figuring out which is first */
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700796 cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
797 cfile->srch_inf.info_level);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 }
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700799 if ((cur_ent == NULL) && (i < pos_in_buf)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 /* BB fixme - check if we should flag this error */
Joe Perchesf96637b2013-05-04 22:12:25 -0500801 cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n",
802 pos_in_buf, index_to_find, rc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 }
804 rc = 0;
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700805 *current_entry = cur_ent;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 } else {
Joe Perchesf96637b2013-05-04 22:12:25 -0500807 cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 return 0;
809 }
810
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700811 if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500812 cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 *num_to_ret = 0;
814 } else
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700815 *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
817 return rc;
818}
819
Al Virobe4ccdc2013-05-22 16:17:25 -0400820static int cifs_filldir(char *find_entry, struct file *file,
821 struct dir_context *ctx,
822 char *scratch_buf, unsigned int max_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823{
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400824 struct cifsFileInfo *file_info = file->private_data;
Al Viro7119e222014-10-22 00:25:12 -0400825 struct super_block *sb = file_inode(file)->i_sb;
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400826 struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400827 struct cifs_dirent de = { NULL, };
Jeff Laytoncc0bad72009-06-25 00:56:52 -0400828 struct cifs_fattr fattr;
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400829 struct qstr name;
830 int rc = 0;
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400831 ino_t ino;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400833 rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
834 file_info->srch_inf.unicode);
Steve French790fe572007-07-07 19:25:05 +0000835 if (rc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836 return rc;
837
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400838 if (de.namelen > max_len) {
Joe Perchesf96637b2013-05-04 22:12:25 -0500839 cifs_dbg(VFS, "bad search response length %zd past smb end\n",
840 de.namelen);
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400841 return -EINVAL;
842 }
Jeff Layton132ac7b2009-02-10 07:33:57 -0500843
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 /* skip . and .. since we added them first */
Christoph Hellwigcda0ec62011-07-16 15:24:05 -0400845 if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 return 0;
847
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400848 if (file_info->srch_inf.unicode) {
849 struct nls_table *nlt = cifs_sb->local_nls;
Steve Frenchb6938552014-09-25 13:20:05 -0500850 int map_type;
851
Steve French2baa2682014-09-27 02:19:01 -0500852 map_type = cifs_remap(cifs_sb);
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400853 name.name = scratch_buf;
854 name.len =
Steve Frenchacbbb762012-01-18 22:32:33 -0600855 cifs_from_utf16((char *)name.name, (__le16 *)de.name,
856 UNICODE_NAME_MAX,
857 min_t(size_t, de.namelen,
Steve Frenchb6938552014-09-25 13:20:05 -0500858 (size_t)max_len), nlt, map_type);
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400859 name.len -= nls_nullsize(nlt);
860 } else {
861 name.name = de.name;
862 name.len = de.namelen;
863 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400865 switch (file_info->srch_inf.info_level) {
Aurelien Aptel3d519bd2020-02-08 15:50:58 +0100866 case SMB_FIND_FILE_POSIX_INFO:
867 cifs_posix_to_fattr(&fattr,
868 (struct smb2_posix_info *)find_entry,
869 cifs_sb);
870 break;
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400871 case SMB_FIND_FILE_UNIX:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 cifs_unix_basic_to_fattr(&fattr,
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400873 &((FILE_UNIX_INFO *)find_entry)->basic,
874 cifs_sb);
875 break;
876 case SMB_FIND_FILE_INFO_STANDARD:
877 cifs_std_info_to_fattr(&fattr,
878 (FIND_FILE_STANDARD_INFO *)find_entry,
879 cifs_sb);
880 break;
Paulo Alcantara (SUSE)046aca32019-12-18 18:11:37 -0300881 case SMB_FIND_FILE_ID_FULL_DIR_INFO:
882 cifs_fulldir_info_to_fattr(&fattr,
883 (SEARCH_ID_FULL_DIR_INFO *)find_entry,
884 cifs_sb);
885 break;
Christoph Hellwig9feed6f2011-07-16 15:23:49 -0400886 default:
887 cifs_dir_info_to_fattr(&fattr,
888 (FILE_DIRECTORY_INFO *)find_entry,
889 cifs_sb);
890 break;
891 }
Steve French38702532007-07-08 15:40:40 +0000892
Christoph Hellwigf16d59b2011-07-16 15:24:22 -0400893 if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
894 fattr.cf_uniqueid = de.ino;
Jeff Laytonec06aed2009-11-06 14:18:29 -0500895 } else {
Jeff Layton0b8f18e2009-07-09 01:46:37 -0400896 fattr.cf_uniqueid = iunique(sb, ROOT_I);
Jeff Laytonec06aed2009-11-06 14:18:29 -0500897 cifs_autodisable_serverino(cifs_sb);
898 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Stefan Metzmacher1b12b9c2010-08-05 21:19:56 +0200900 if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
Sachin Prabhucb084b12013-11-25 17:09:50 +0000901 couldbe_mf_symlink(&fattr))
Stefan Metzmacher1b12b9c2010-08-05 21:19:56 +0200902 /*
903 * trying to get the type and mode can be slow,
904 * so just call those regular files for now, and mark
905 * for reval
906 */
907 fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
908
Goldwyn Rodrigues1f1735c2016-04-18 06:41:52 -0500909 cifs_prime_dcache(file_dentry(file), &name, &fattr);
Steve French50c2f752007-07-13 00:33:32 +0000910
Jeff Laytoneb1b3fa2012-12-03 06:05:37 -0500911 ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
Al Virobe4ccdc2013-05-22 16:17:25 -0400912 return !dir_emit(ctx, name.name, name.len, ino, fattr.cf_dtype);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913}
914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Al Virobe4ccdc2013-05-22 16:17:25 -0400916int cifs_readdir(struct file *file, struct dir_context *ctx)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917{
918 int rc = 0;
Pavel Shilovsky6d5786a2012-06-20 11:21:16 +0400919 unsigned int xid;
920 int i;
Pavel Shilovsky92fc65a2012-09-18 16:20:32 -0700921 struct cifs_tcon *tcon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 struct cifsFileInfo *cifsFile = NULL;
Steve French38702532007-07-08 15:40:40 +0000923 char *current_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 int num_to_fill = 0;
Steve French38702532007-07-08 15:40:40 +0000925 char *tmp_buf = NULL;
Steve French50c2f752007-07-13 00:33:32 +0000926 char *end_of_smb;
Jeff Layton18295792009-04-30 20:45:45 -0400927 unsigned int max_len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Pavel Shilovsky6d5786a2012-06-20 11:21:16 +0400929 xid = get_xid();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Suresh Jayaraman6221ddd2010-10-01 21:23:33 +0530931 /*
932 * Ensure FindFirst doesn't fail before doing filldir() for '.' and
933 * '..'. Otherwise we won't be able to notify VFS in case of failure.
934 */
935 if (file->private_data == NULL) {
936 rc = initiate_cifs_search(xid, file);
Joe Perchesf96637b2013-05-04 22:12:25 -0500937 cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
Suresh Jayaraman6221ddd2010-10-01 21:23:33 +0530938 if (rc)
939 goto rddir2_exit;
940 }
941
Al Virobe4ccdc2013-05-22 16:17:25 -0400942 if (!dir_emit_dots(file, ctx))
943 goto rddir2_exit;
944
945 /* 1) If search is active,
946 is in current search buffer?
947 if it before then restart search
948 if after then keep searching till find it */
949
Al Virobe4ccdc2013-05-22 16:17:25 -0400950 cifsFile = file->private_data;
951 if (cifsFile->srch_inf.endOfSearch) {
952 if (cifsFile->srch_inf.emptyDir) {
953 cifs_dbg(FYI, "End of search, empty dir\n");
954 rc = 0;
955 goto rddir2_exit;
956 }
957 } /* else {
958 cifsFile->invalidHandle = true;
959 tcon->ses->server->close(xid, tcon, &cifsFile->fid);
960 } */
961
962 tcon = tlink_tcon(cifsFile->tlink);
963 rc = find_cifs_entry(xid, tcon, ctx->pos, file, &current_entry,
964 &num_to_fill);
965 if (rc) {
966 cifs_dbg(FYI, "fce error %d\n", rc);
967 goto rddir2_exit;
968 } else if (current_entry != NULL) {
969 cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
970 } else {
Joe Perchesa0a30362020-04-14 22:42:53 -0700971 cifs_dbg(FYI, "Could not find entry\n");
Al Virobe4ccdc2013-05-22 16:17:25 -0400972 goto rddir2_exit;
973 }
974 cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
975 num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
976 max_len = tcon->ses->server->ops->calc_smb_size(
Ronnie Sahlberg9ec672b2018-04-22 15:30:12 -0600977 cifsFile->srch_inf.ntwrk_buf_start,
978 tcon->ses->server);
Al Virobe4ccdc2013-05-22 16:17:25 -0400979 end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
980
981 tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
982 if (tmp_buf == NULL) {
983 rc = -ENOMEM;
984 goto rddir2_exit;
985 }
986
987 for (i = 0; i < num_to_fill; i++) {
988 if (current_entry == NULL) {
989 /* evaluate whether this case is an error */
990 cifs_dbg(VFS, "past SMB end, num to fill %d i %d\n",
991 num_to_fill, i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 break;
993 }
Al Virobe4ccdc2013-05-22 16:17:25 -0400994 /*
995 * if buggy server returns . and .. late do we want to
996 * check for that here?
997 */
Vasily Averin01b9b0b2016-01-14 13:41:14 +0300998 *tmp_buf = 0;
Al Virobe4ccdc2013-05-22 16:17:25 -0400999 rc = cifs_filldir(current_entry, file, ctx,
1000 tmp_buf, max_len);
Steve French790fe572007-07-07 19:25:05 +00001001 if (rc) {
Al Virobe4ccdc2013-05-22 16:17:25 -04001002 if (rc > 0)
1003 rc = 0;
Kulikov Vasiliyf55fdcc2010-07-16 20:15:25 +04001004 break;
1005 }
1006
Al Virobe4ccdc2013-05-22 16:17:25 -04001007 ctx->pos++;
1008 if (ctx->pos ==
1009 cifsFile->srch_inf.index_of_last_entry) {
1010 cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
1011 ctx->pos, tmp_buf);
1012 cifs_save_resume_key(current_entry, cifsFile);
1013 break;
1014 } else
1015 current_entry =
1016 nxt_dir_entry(current_entry, end_of_smb,
1017 cifsFile->srch_inf.info_level);
1018 }
1019 kfree(tmp_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
1021rddir2_exit:
Pavel Shilovsky6d5786a2012-06-20 11:21:16 +04001022 free_xid(xid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 return rc;
1024}