162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Directory search handling
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2004, 2008
762306a36Sopenharmony_ci *   Copyright (C) Red Hat, Inc., 2011
862306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/pagemap.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/stat.h>
1562306a36Sopenharmony_ci#include "cifspdu.h"
1662306a36Sopenharmony_ci#include "cifsglob.h"
1762306a36Sopenharmony_ci#include "cifsproto.h"
1862306a36Sopenharmony_ci#include "cifs_unicode.h"
1962306a36Sopenharmony_ci#include "cifs_debug.h"
2062306a36Sopenharmony_ci#include "cifs_fs_sb.h"
2162306a36Sopenharmony_ci#include "cifsfs.h"
2262306a36Sopenharmony_ci#include "smb2proto.h"
2362306a36Sopenharmony_ci#include "fs_context.h"
2462306a36Sopenharmony_ci#include "cached_dir.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * To be safe - for UCS to UTF-8 with strings loaded with the rare long
2862306a36Sopenharmony_ci * characters alloc more to account for such multibyte target UTF-8
2962306a36Sopenharmony_ci * characters.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_ci#define UNICODE_NAME_MAX ((4 * NAME_MAX) + 2)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
3462306a36Sopenharmony_cistatic void dump_cifs_file_struct(struct file *file, char *label)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct cifsFileInfo *cf;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (file) {
3962306a36Sopenharmony_ci		cf = file->private_data;
4062306a36Sopenharmony_ci		if (cf == NULL) {
4162306a36Sopenharmony_ci			cifs_dbg(FYI, "empty cifs private file data\n");
4262306a36Sopenharmony_ci			return;
4362306a36Sopenharmony_ci		}
4462306a36Sopenharmony_ci		if (cf->invalidHandle)
4562306a36Sopenharmony_ci			cifs_dbg(FYI, "Invalid handle\n");
4662306a36Sopenharmony_ci		if (cf->srch_inf.endOfSearch)
4762306a36Sopenharmony_ci			cifs_dbg(FYI, "end of search\n");
4862306a36Sopenharmony_ci		if (cf->srch_inf.emptyDir)
4962306a36Sopenharmony_ci			cifs_dbg(FYI, "empty dir\n");
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci#else
5362306a36Sopenharmony_cistatic inline void dump_cifs_file_struct(struct file *file, char *label)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci#endif /* DEBUG2 */
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/*
5962306a36Sopenharmony_ci * Match a reparse point inode if reparse tag and ctime haven't changed.
6062306a36Sopenharmony_ci *
6162306a36Sopenharmony_ci * Windows Server updates ctime of reparse points when their data have changed.
6262306a36Sopenharmony_ci * The server doesn't allow changing reparse tags from existing reparse points,
6362306a36Sopenharmony_ci * though it's worth checking.
6462306a36Sopenharmony_ci */
6562306a36Sopenharmony_cistatic inline bool reparse_inode_match(struct inode *inode,
6662306a36Sopenharmony_ci				       struct cifs_fattr *fattr)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct timespec64 ctime = inode_get_ctime(inode);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return (CIFS_I(inode)->cifsAttrs & ATTR_REPARSE) &&
7162306a36Sopenharmony_ci		CIFS_I(inode)->reparse_tag == fattr->cf_cifstag &&
7262306a36Sopenharmony_ci		timespec64_equal(&ctime, &fattr->cf_ctime);
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*
7662306a36Sopenharmony_ci * Attempt to preload the dcache with the results from the FIND_FIRST/NEXT
7762306a36Sopenharmony_ci *
7862306a36Sopenharmony_ci * Find the dentry that matches "name". If there isn't one, create one. If it's
7962306a36Sopenharmony_ci * a negative dentry or the uniqueid or filetype(mode) changed,
8062306a36Sopenharmony_ci * then drop it and recreate it.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic void
8362306a36Sopenharmony_cicifs_prime_dcache(struct dentry *parent, struct qstr *name,
8462306a36Sopenharmony_ci		    struct cifs_fattr *fattr)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct dentry *dentry, *alias;
8762306a36Sopenharmony_ci	struct inode *inode;
8862306a36Sopenharmony_ci	struct super_block *sb = parent->d_sb;
8962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
9062306a36Sopenharmony_ci	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
9162306a36Sopenharmony_ci	int rc;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: for %s\n", __func__, name->name);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	dentry = d_hash_and_lookup(parent, name);
9662306a36Sopenharmony_ci	if (!dentry) {
9762306a36Sopenharmony_ci		/*
9862306a36Sopenharmony_ci		 * If we know that the inode will need to be revalidated
9962306a36Sopenharmony_ci		 * immediately, then don't create a new dentry for it.
10062306a36Sopenharmony_ci		 * We'll end up doing an on the wire call either way and
10162306a36Sopenharmony_ci		 * this spares us an invalidation.
10262306a36Sopenharmony_ci		 */
10362306a36Sopenharmony_ciretry:
10462306a36Sopenharmony_ci		if ((fattr->cf_cifsattrs & ATTR_REPARSE) ||
10562306a36Sopenharmony_ci		    (fattr->cf_flags & CIFS_FATTR_NEED_REVAL))
10662306a36Sopenharmony_ci			return;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci		dentry = d_alloc_parallel(parent, name, &wq);
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	if (IS_ERR(dentry))
11162306a36Sopenharmony_ci		return;
11262306a36Sopenharmony_ci	if (!d_in_lookup(dentry)) {
11362306a36Sopenharmony_ci		inode = d_inode(dentry);
11462306a36Sopenharmony_ci		if (inode) {
11562306a36Sopenharmony_ci			if (d_mountpoint(dentry)) {
11662306a36Sopenharmony_ci				dput(dentry);
11762306a36Sopenharmony_ci				return;
11862306a36Sopenharmony_ci			}
11962306a36Sopenharmony_ci			/*
12062306a36Sopenharmony_ci			 * If we're generating inode numbers, then we don't
12162306a36Sopenharmony_ci			 * want to clobber the existing one with the one that
12262306a36Sopenharmony_ci			 * the readdir code created.
12362306a36Sopenharmony_ci			 */
12462306a36Sopenharmony_ci			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM))
12562306a36Sopenharmony_ci				fattr->cf_uniqueid = CIFS_I(inode)->uniqueid;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci			/*
12862306a36Sopenharmony_ci			 * Update inode in place if both i_ino and i_mode didn't
12962306a36Sopenharmony_ci			 * change.
13062306a36Sopenharmony_ci			 */
13162306a36Sopenharmony_ci			if (CIFS_I(inode)->uniqueid == fattr->cf_uniqueid) {
13262306a36Sopenharmony_ci				/*
13362306a36Sopenharmony_ci				 * Query dir responses don't provide enough
13462306a36Sopenharmony_ci				 * information about reparse points other than
13562306a36Sopenharmony_ci				 * their reparse tags.  Save an invalidation by
13662306a36Sopenharmony_ci				 * not clobbering the existing mode, size and
13762306a36Sopenharmony_ci				 * symlink target (if any) when reparse tag and
13862306a36Sopenharmony_ci				 * ctime haven't changed.
13962306a36Sopenharmony_ci				 */
14062306a36Sopenharmony_ci				rc = 0;
14162306a36Sopenharmony_ci				if (fattr->cf_cifsattrs & ATTR_REPARSE) {
14262306a36Sopenharmony_ci					if (likely(reparse_inode_match(inode, fattr))) {
14362306a36Sopenharmony_ci						fattr->cf_mode = inode->i_mode;
14462306a36Sopenharmony_ci						fattr->cf_eof = CIFS_I(inode)->server_eof;
14562306a36Sopenharmony_ci						fattr->cf_symlink_target = NULL;
14662306a36Sopenharmony_ci					} else {
14762306a36Sopenharmony_ci						CIFS_I(inode)->time = 0;
14862306a36Sopenharmony_ci						rc = -ESTALE;
14962306a36Sopenharmony_ci					}
15062306a36Sopenharmony_ci				}
15162306a36Sopenharmony_ci				if (!rc && !cifs_fattr_to_inode(inode, fattr, true)) {
15262306a36Sopenharmony_ci					dput(dentry);
15362306a36Sopenharmony_ci					return;
15462306a36Sopenharmony_ci				}
15562306a36Sopenharmony_ci			}
15662306a36Sopenharmony_ci		}
15762306a36Sopenharmony_ci		d_invalidate(dentry);
15862306a36Sopenharmony_ci		dput(dentry);
15962306a36Sopenharmony_ci		goto retry;
16062306a36Sopenharmony_ci	} else {
16162306a36Sopenharmony_ci		inode = cifs_iget(sb, fattr);
16262306a36Sopenharmony_ci		if (!inode)
16362306a36Sopenharmony_ci			inode = ERR_PTR(-ENOMEM);
16462306a36Sopenharmony_ci		alias = d_splice_alias(inode, dentry);
16562306a36Sopenharmony_ci		d_lookup_done(dentry);
16662306a36Sopenharmony_ci		if (alias && !IS_ERR(alias))
16762306a36Sopenharmony_ci			dput(alias);
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	dput(dentry);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void
17362306a36Sopenharmony_cicifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	struct cifs_open_info_data data = {
17662306a36Sopenharmony_ci		.reparse = { .tag = fattr->cf_cifstag, },
17762306a36Sopenharmony_ci	};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	fattr->cf_uid = cifs_sb->ctx->linux_uid;
18062306a36Sopenharmony_ci	fattr->cf_gid = cifs_sb->ctx->linux_gid;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	/*
18362306a36Sopenharmony_ci	 * The IO_REPARSE_TAG_LX_ tags originally were used by WSL but they
18462306a36Sopenharmony_ci	 * are preferred by the Linux client in some cases since, unlike
18562306a36Sopenharmony_ci	 * the NFS reparse tag (or EAs), they don't require an extra query
18662306a36Sopenharmony_ci	 * to determine which type of special file they represent.
18762306a36Sopenharmony_ci	 * TODO: go through all documented  reparse tags to see if we can
18862306a36Sopenharmony_ci	 * reasonably map some of them to directories vs. files vs. symlinks
18962306a36Sopenharmony_ci	 */
19062306a36Sopenharmony_ci	if ((fattr->cf_cifsattrs & ATTR_REPARSE) &&
19162306a36Sopenharmony_ci	    cifs_reparse_point_to_fattr(cifs_sb, fattr, &data))
19262306a36Sopenharmony_ci		goto out_reparse;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
19562306a36Sopenharmony_ci		fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
19662306a36Sopenharmony_ci		fattr->cf_dtype = DT_DIR;
19762306a36Sopenharmony_ci	} else {
19862306a36Sopenharmony_ci		fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
19962306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciout_reparse:
20362306a36Sopenharmony_ci	/* non-unix readdir doesn't provide nlink */
20462306a36Sopenharmony_ci	fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if (fattr->cf_cifsattrs & ATTR_READONLY)
20762306a36Sopenharmony_ci		fattr->cf_mode &= ~S_IWUGO;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci	/*
21062306a36Sopenharmony_ci	 * We of course don't get ACL info in FIND_FIRST/NEXT results, so
21162306a36Sopenharmony_ci	 * mark it for revalidation so that "ls -l" will look right. It might
21262306a36Sopenharmony_ci	 * be super-slow, but if we don't do this then the ownership of files
21362306a36Sopenharmony_ci	 * may look wrong since the inodes may not have timed out by the time
21462306a36Sopenharmony_ci	 * "ls" does a stat() call on them.
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
21762306a36Sopenharmony_ci	    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID))
21862306a36Sopenharmony_ci		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL &&
22162306a36Sopenharmony_ci	    fattr->cf_cifsattrs & ATTR_SYSTEM) {
22262306a36Sopenharmony_ci		if (fattr->cf_eof == 0)  {
22362306a36Sopenharmony_ci			fattr->cf_mode &= ~S_IFMT;
22462306a36Sopenharmony_ci			fattr->cf_mode |= S_IFIFO;
22562306a36Sopenharmony_ci			fattr->cf_dtype = DT_FIFO;
22662306a36Sopenharmony_ci		} else {
22762306a36Sopenharmony_ci			/*
22862306a36Sopenharmony_ci			 * trying to get the type and mode via SFU can be slow,
22962306a36Sopenharmony_ci			 * so just call those regular files for now, and mark
23062306a36Sopenharmony_ci			 * for reval
23162306a36Sopenharmony_ci			 */
23262306a36Sopenharmony_ci			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
23362306a36Sopenharmony_ci		}
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */
23862306a36Sopenharmony_cistatic void
23962306a36Sopenharmony_cicifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
24062306a36Sopenharmony_ci		    struct cifs_sb_info *cifs_sb)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct smb2_posix_info_parsed parsed;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	posix_info_parse(info, NULL, &parsed);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
24762306a36Sopenharmony_ci	fattr->cf_uniqueid = le64_to_cpu(info->Inode);
24862306a36Sopenharmony_ci	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
24962306a36Sopenharmony_ci	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
25262306a36Sopenharmony_ci	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
25362306a36Sopenharmony_ci	fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	fattr->cf_nlink = le32_to_cpu(info->HardLinks);
25662306a36Sopenharmony_ci	fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/*
25962306a36Sopenharmony_ci	 * Since we set the inode type below we need to mask off
26062306a36Sopenharmony_ci	 * to avoid strange results if bits set above.
26162306a36Sopenharmony_ci	 * XXX: why not make server&client use the type bits?
26262306a36Sopenharmony_ci	 */
26362306a36Sopenharmony_ci	fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	cifs_dbg(FYI, "posix fattr: dev %d, reparse %d, mode %o\n",
26662306a36Sopenharmony_ci		 le32_to_cpu(info->DeviceId),
26762306a36Sopenharmony_ci		 le32_to_cpu(info->ReparseTag),
26862306a36Sopenharmony_ci		 le32_to_cpu(info->Mode));
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
27162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFDIR;
27262306a36Sopenharmony_ci		fattr->cf_dtype = DT_DIR;
27362306a36Sopenharmony_ci	} else {
27462306a36Sopenharmony_ci		/*
27562306a36Sopenharmony_ci		 * mark anything that is not a dir as regular
27662306a36Sopenharmony_ci		 * file. special files should have the REPARSE
27762306a36Sopenharmony_ci		 * attribute and will be marked as needing revaluation
27862306a36Sopenharmony_ci		 */
27962306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG;
28062306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	sid_to_id(cifs_sb, &parsed.owner, fattr, SIDOWNER);
28462306a36Sopenharmony_ci	sid_to_id(cifs_sb, &parsed.group, fattr, SIDGROUP);
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	const FILE_DIRECTORY_INFO *fi = info;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
29262306a36Sopenharmony_ci	fattr->cf_cifsattrs = le32_to_cpu(fi->ExtFileAttributes);
29362306a36Sopenharmony_ci	fattr->cf_eof = le64_to_cpu(fi->EndOfFile);
29462306a36Sopenharmony_ci	fattr->cf_bytes = le64_to_cpu(fi->AllocationSize);
29562306a36Sopenharmony_ci	fattr->cf_createtime = le64_to_cpu(fi->CreationTime);
29662306a36Sopenharmony_ci	fattr->cf_atime = cifs_NTtimeToUnix(fi->LastAccessTime);
29762306a36Sopenharmony_ci	fattr->cf_ctime = cifs_NTtimeToUnix(fi->ChangeTime);
29862306a36Sopenharmony_ci	fattr->cf_mtime = cifs_NTtimeToUnix(fi->LastWriteTime);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_civoid
30262306a36Sopenharmony_cicifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
30362306a36Sopenharmony_ci		       struct cifs_sb_info *cifs_sb)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	__dir_info_to_fattr(fattr, info);
30662306a36Sopenharmony_ci	cifs_fill_common_info(fattr, cifs_sb);
30762306a36Sopenharmony_ci}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic void cifs_fulldir_info_to_fattr(struct cifs_fattr *fattr,
31062306a36Sopenharmony_ci				       const void *info,
31162306a36Sopenharmony_ci				       struct cifs_sb_info *cifs_sb)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	const FILE_FULL_DIRECTORY_INFO *di = info;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	__dir_info_to_fattr(fattr, info);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* See MS-FSCC 2.4.14, 2.4.19 */
31862306a36Sopenharmony_ci	if (fattr->cf_cifsattrs & ATTR_REPARSE)
31962306a36Sopenharmony_ci		fattr->cf_cifstag = le32_to_cpu(di->EaSize);
32062306a36Sopenharmony_ci	cifs_fill_common_info(fattr, cifs_sb);
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic void
32462306a36Sopenharmony_cicifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
32562306a36Sopenharmony_ci		       struct cifs_sb_info *cifs_sb)
32662306a36Sopenharmony_ci{
32762306a36Sopenharmony_ci	int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
33062306a36Sopenharmony_ci	fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
33162306a36Sopenharmony_ci					    info->LastAccessTime, offset);
33262306a36Sopenharmony_ci	fattr->cf_ctime = cnvrtDosUnixTm(info->LastWriteDate,
33362306a36Sopenharmony_ci					    info->LastWriteTime, offset);
33462306a36Sopenharmony_ci	fattr->cf_mtime = cnvrtDosUnixTm(info->LastWriteDate,
33562306a36Sopenharmony_ci					    info->LastWriteTime, offset);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	fattr->cf_cifsattrs = le16_to_cpu(info->Attributes);
33862306a36Sopenharmony_ci	fattr->cf_bytes = le32_to_cpu(info->AllocationSize);
33962306a36Sopenharmony_ci	fattr->cf_eof = le32_to_cpu(info->DataSize);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	cifs_fill_common_info(fattr, cifs_sb);
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int
34562306a36Sopenharmony_ci_initiate_cifs_search(const unsigned int xid, struct file *file,
34662306a36Sopenharmony_ci		     const char *full_path)
34762306a36Sopenharmony_ci{
34862306a36Sopenharmony_ci	__u16 search_flags;
34962306a36Sopenharmony_ci	int rc = 0;
35062306a36Sopenharmony_ci	struct cifsFileInfo *cifsFile;
35162306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
35262306a36Sopenharmony_ci	struct tcon_link *tlink = NULL;
35362306a36Sopenharmony_ci	struct cifs_tcon *tcon;
35462306a36Sopenharmony_ci	struct TCP_Server_Info *server;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (file->private_data == NULL) {
35762306a36Sopenharmony_ci		tlink = cifs_sb_tlink(cifs_sb);
35862306a36Sopenharmony_ci		if (IS_ERR(tlink))
35962306a36Sopenharmony_ci			return PTR_ERR(tlink);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		cifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
36262306a36Sopenharmony_ci		if (cifsFile == NULL) {
36362306a36Sopenharmony_ci			rc = -ENOMEM;
36462306a36Sopenharmony_ci			goto error_exit;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		spin_lock_init(&cifsFile->file_info_lock);
36762306a36Sopenharmony_ci		file->private_data = cifsFile;
36862306a36Sopenharmony_ci		cifsFile->tlink = cifs_get_tlink(tlink);
36962306a36Sopenharmony_ci		tcon = tlink_tcon(tlink);
37062306a36Sopenharmony_ci	} else {
37162306a36Sopenharmony_ci		cifsFile = file->private_data;
37262306a36Sopenharmony_ci		tcon = tlink_tcon(cifsFile->tlink);
37362306a36Sopenharmony_ci	}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	server = tcon->ses->server;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	if (!server->ops->query_dir_first) {
37862306a36Sopenharmony_ci		rc = -ENOSYS;
37962306a36Sopenharmony_ci		goto error_exit;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	cifsFile->invalidHandle = true;
38362306a36Sopenharmony_ci	cifsFile->srch_inf.endOfSearch = false;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	cifs_dbg(FYI, "Full path: %s start at: %lld\n", full_path, file->f_pos);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ciffirst_retry:
38862306a36Sopenharmony_ci	/* test for Unix extensions */
38962306a36Sopenharmony_ci	/* but now check for them on the share/mount not on the SMB session */
39062306a36Sopenharmony_ci	/* if (cap_unix(tcon->ses) { */
39162306a36Sopenharmony_ci	if (tcon->unix_ext)
39262306a36Sopenharmony_ci		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
39362306a36Sopenharmony_ci	else if (tcon->posix_extensions)
39462306a36Sopenharmony_ci		cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO;
39562306a36Sopenharmony_ci	else if ((tcon->ses->capabilities &
39662306a36Sopenharmony_ci		  tcon->ses->server->vals->cap_nt_find) == 0) {
39762306a36Sopenharmony_ci		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
39862306a36Sopenharmony_ci	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
39962306a36Sopenharmony_ci		cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
40062306a36Sopenharmony_ci	} else /* not srvinos - BB fixme add check for backlevel? */ {
40162306a36Sopenharmony_ci		cifsFile->srch_inf.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
40562306a36Sopenharmony_ci	if (backup_cred(cifs_sb))
40662306a36Sopenharmony_ci		search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
40962306a36Sopenharmony_ci					  &cifsFile->fid, search_flags,
41062306a36Sopenharmony_ci					  &cifsFile->srch_inf);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (rc == 0) {
41362306a36Sopenharmony_ci		cifsFile->invalidHandle = false;
41462306a36Sopenharmony_ci	} else if ((rc == -EOPNOTSUPP) &&
41562306a36Sopenharmony_ci		   (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
41662306a36Sopenharmony_ci		cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
41762306a36Sopenharmony_ci		goto ffirst_retry;
41862306a36Sopenharmony_ci	}
41962306a36Sopenharmony_cierror_exit:
42062306a36Sopenharmony_ci	cifs_put_tlink(tlink);
42162306a36Sopenharmony_ci	return rc;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int
42562306a36Sopenharmony_ciinitiate_cifs_search(const unsigned int xid, struct file *file,
42662306a36Sopenharmony_ci		     const char *full_path)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	int rc, retry_count = 0;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	do {
43162306a36Sopenharmony_ci		rc = _initiate_cifs_search(xid, file, full_path);
43262306a36Sopenharmony_ci		/*
43362306a36Sopenharmony_ci		 * If we don't have enough credits to start reading the
43462306a36Sopenharmony_ci		 * directory just try again after short wait.
43562306a36Sopenharmony_ci		 */
43662306a36Sopenharmony_ci		if (rc != -EDEADLK)
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		usleep_range(512, 2048);
44062306a36Sopenharmony_ci	} while (retry_count++ < 5);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return rc;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/* return length of unicode string in bytes */
44662306a36Sopenharmony_cistatic int cifs_unicode_bytelen(const char *str)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	int len;
44962306a36Sopenharmony_ci	const __le16 *ustr = (const __le16 *)str;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	for (len = 0; len <= PATH_MAX; len++) {
45262306a36Sopenharmony_ci		if (ustr[len] == 0)
45362306a36Sopenharmony_ci			return len << 1;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci	cifs_dbg(FYI, "Unicode string longer than PATH_MAX found\n");
45662306a36Sopenharmony_ci	return len << 1;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	char *new_entry;
46262306a36Sopenharmony_ci	FILE_DIRECTORY_INFO *pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	if (level == SMB_FIND_FILE_INFO_STANDARD) {
46562306a36Sopenharmony_ci		FIND_FILE_STANDARD_INFO *pfData;
46662306a36Sopenharmony_ci		pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) + 1 +
46962306a36Sopenharmony_ci				pfData->FileNameLength;
47062306a36Sopenharmony_ci	} else {
47162306a36Sopenharmony_ci		u32 next_offset = le32_to_cpu(pDirInfo->NextEntryOffset);
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (old_entry + next_offset < old_entry) {
47462306a36Sopenharmony_ci			cifs_dbg(VFS, "Invalid offset %u\n", next_offset);
47562306a36Sopenharmony_ci			return NULL;
47662306a36Sopenharmony_ci		}
47762306a36Sopenharmony_ci		new_entry = old_entry + next_offset;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci	cifs_dbg(FYI, "new entry %p old entry %p\n", new_entry, old_entry);
48062306a36Sopenharmony_ci	/* validate that new_entry is not past end of SMB */
48162306a36Sopenharmony_ci	if (new_entry >= end_of_smb) {
48262306a36Sopenharmony_ci		cifs_dbg(VFS, "search entry %p began after end of SMB %p old entry %p\n",
48362306a36Sopenharmony_ci			 new_entry, end_of_smb, old_entry);
48462306a36Sopenharmony_ci		return NULL;
48562306a36Sopenharmony_ci	} else if (((level == SMB_FIND_FILE_INFO_STANDARD) &&
48662306a36Sopenharmony_ci		    (new_entry + sizeof(FIND_FILE_STANDARD_INFO) + 1 > end_of_smb))
48762306a36Sopenharmony_ci		  || ((level != SMB_FIND_FILE_INFO_STANDARD) &&
48862306a36Sopenharmony_ci		   (new_entry + sizeof(FILE_DIRECTORY_INFO) + 1 > end_of_smb)))  {
48962306a36Sopenharmony_ci		cifs_dbg(VFS, "search entry %p extends after end of SMB %p\n",
49062306a36Sopenharmony_ci			 new_entry, end_of_smb);
49162306a36Sopenharmony_ci		return NULL;
49262306a36Sopenharmony_ci	} else
49362306a36Sopenharmony_ci		return new_entry;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci}
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistruct cifs_dirent {
49862306a36Sopenharmony_ci	const char	*name;
49962306a36Sopenharmony_ci	size_t		namelen;
50062306a36Sopenharmony_ci	u32		resume_key;
50162306a36Sopenharmony_ci	u64		ino;
50262306a36Sopenharmony_ci};
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic void cifs_fill_dirent_posix(struct cifs_dirent *de,
50562306a36Sopenharmony_ci				   const struct smb2_posix_info *info)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct smb2_posix_info_parsed parsed;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	/* payload should have already been checked at this point */
51062306a36Sopenharmony_ci	if (posix_info_parse(info, NULL, &parsed) < 0) {
51162306a36Sopenharmony_ci		cifs_dbg(VFS, "Invalid POSIX info payload\n");
51262306a36Sopenharmony_ci		return;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	de->name = parsed.name;
51662306a36Sopenharmony_ci	de->namelen = parsed.name_len;
51762306a36Sopenharmony_ci	de->resume_key = info->Ignored;
51862306a36Sopenharmony_ci	de->ino = le64_to_cpu(info->Inode);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void cifs_fill_dirent_unix(struct cifs_dirent *de,
52262306a36Sopenharmony_ci		const FILE_UNIX_INFO *info, bool is_unicode)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	de->name = &info->FileName[0];
52562306a36Sopenharmony_ci	if (is_unicode)
52662306a36Sopenharmony_ci		de->namelen = cifs_unicode_bytelen(de->name);
52762306a36Sopenharmony_ci	else
52862306a36Sopenharmony_ci		de->namelen = strnlen(de->name, PATH_MAX);
52962306a36Sopenharmony_ci	de->resume_key = info->ResumeKey;
53062306a36Sopenharmony_ci	de->ino = le64_to_cpu(info->basic.UniqueId);
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_cistatic void cifs_fill_dirent_dir(struct cifs_dirent *de,
53462306a36Sopenharmony_ci		const FILE_DIRECTORY_INFO *info)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	de->name = &info->FileName[0];
53762306a36Sopenharmony_ci	de->namelen = le32_to_cpu(info->FileNameLength);
53862306a36Sopenharmony_ci	de->resume_key = info->FileIndex;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_cistatic void cifs_fill_dirent_full(struct cifs_dirent *de,
54262306a36Sopenharmony_ci		const FILE_FULL_DIRECTORY_INFO *info)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	de->name = &info->FileName[0];
54562306a36Sopenharmony_ci	de->namelen = le32_to_cpu(info->FileNameLength);
54662306a36Sopenharmony_ci	de->resume_key = info->FileIndex;
54762306a36Sopenharmony_ci}
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_cistatic void cifs_fill_dirent_search(struct cifs_dirent *de,
55062306a36Sopenharmony_ci		const SEARCH_ID_FULL_DIR_INFO *info)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	de->name = &info->FileName[0];
55362306a36Sopenharmony_ci	de->namelen = le32_to_cpu(info->FileNameLength);
55462306a36Sopenharmony_ci	de->resume_key = info->FileIndex;
55562306a36Sopenharmony_ci	de->ino = le64_to_cpu(info->UniqueId);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic void cifs_fill_dirent_both(struct cifs_dirent *de,
55962306a36Sopenharmony_ci		const FILE_BOTH_DIRECTORY_INFO *info)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	de->name = &info->FileName[0];
56262306a36Sopenharmony_ci	de->namelen = le32_to_cpu(info->FileNameLength);
56362306a36Sopenharmony_ci	de->resume_key = info->FileIndex;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic void cifs_fill_dirent_std(struct cifs_dirent *de,
56762306a36Sopenharmony_ci		const FIND_FILE_STANDARD_INFO *info)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	de->name = &info->FileName[0];
57062306a36Sopenharmony_ci	/* one byte length, no endianess conversion */
57162306a36Sopenharmony_ci	de->namelen = info->FileNameLength;
57262306a36Sopenharmony_ci	de->resume_key = info->ResumeKey;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
57662306a36Sopenharmony_ci		u16 level, bool is_unicode)
57762306a36Sopenharmony_ci{
57862306a36Sopenharmony_ci	memset(de, 0, sizeof(*de));
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	switch (level) {
58162306a36Sopenharmony_ci	case SMB_FIND_FILE_POSIX_INFO:
58262306a36Sopenharmony_ci		cifs_fill_dirent_posix(de, info);
58362306a36Sopenharmony_ci		break;
58462306a36Sopenharmony_ci	case SMB_FIND_FILE_UNIX:
58562306a36Sopenharmony_ci		cifs_fill_dirent_unix(de, info, is_unicode);
58662306a36Sopenharmony_ci		break;
58762306a36Sopenharmony_ci	case SMB_FIND_FILE_DIRECTORY_INFO:
58862306a36Sopenharmony_ci		cifs_fill_dirent_dir(de, info);
58962306a36Sopenharmony_ci		break;
59062306a36Sopenharmony_ci	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
59162306a36Sopenharmony_ci		cifs_fill_dirent_full(de, info);
59262306a36Sopenharmony_ci		break;
59362306a36Sopenharmony_ci	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
59462306a36Sopenharmony_ci		cifs_fill_dirent_search(de, info);
59562306a36Sopenharmony_ci		break;
59662306a36Sopenharmony_ci	case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
59762306a36Sopenharmony_ci		cifs_fill_dirent_both(de, info);
59862306a36Sopenharmony_ci		break;
59962306a36Sopenharmony_ci	case SMB_FIND_FILE_INFO_STANDARD:
60062306a36Sopenharmony_ci		cifs_fill_dirent_std(de, info);
60162306a36Sopenharmony_ci		break;
60262306a36Sopenharmony_ci	default:
60362306a36Sopenharmony_ci		cifs_dbg(FYI, "Unknown findfirst level %d\n", level);
60462306a36Sopenharmony_ci		return -EINVAL;
60562306a36Sopenharmony_ci	}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	return 0;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci#define UNICODE_DOT cpu_to_le16(0x2e)
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/* return 0 if no match and 1 for . (current directory) and 2 for .. (parent) */
61362306a36Sopenharmony_cistatic int cifs_entry_is_dot(struct cifs_dirent *de, bool is_unicode)
61462306a36Sopenharmony_ci{
61562306a36Sopenharmony_ci	int rc = 0;
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci	if (!de->name)
61862306a36Sopenharmony_ci		return 0;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (is_unicode) {
62162306a36Sopenharmony_ci		__le16 *ufilename = (__le16 *)de->name;
62262306a36Sopenharmony_ci		if (de->namelen == 2) {
62362306a36Sopenharmony_ci			/* check for . */
62462306a36Sopenharmony_ci			if (ufilename[0] == UNICODE_DOT)
62562306a36Sopenharmony_ci				rc = 1;
62662306a36Sopenharmony_ci		} else if (de->namelen == 4) {
62762306a36Sopenharmony_ci			/* check for .. */
62862306a36Sopenharmony_ci			if (ufilename[0] == UNICODE_DOT &&
62962306a36Sopenharmony_ci			    ufilename[1] == UNICODE_DOT)
63062306a36Sopenharmony_ci				rc = 2;
63162306a36Sopenharmony_ci		}
63262306a36Sopenharmony_ci	} else /* ASCII */ {
63362306a36Sopenharmony_ci		if (de->namelen == 1) {
63462306a36Sopenharmony_ci			if (de->name[0] == '.')
63562306a36Sopenharmony_ci				rc = 1;
63662306a36Sopenharmony_ci		} else if (de->namelen == 2) {
63762306a36Sopenharmony_ci			if (de->name[0] == '.' && de->name[1] == '.')
63862306a36Sopenharmony_ci				rc = 2;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	return rc;
64362306a36Sopenharmony_ci}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci/* Check if directory that we are searching has changed so we can decide
64662306a36Sopenharmony_ci   whether we can use the cached search results from the previous search */
64762306a36Sopenharmony_cistatic int is_dir_changed(struct file *file)
64862306a36Sopenharmony_ci{
64962306a36Sopenharmony_ci	struct inode *inode = file_inode(file);
65062306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInfo = CIFS_I(inode);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (cifsInfo->time == 0)
65362306a36Sopenharmony_ci		return 1; /* directory was changed, perhaps due to unlink */
65462306a36Sopenharmony_ci	else
65562306a36Sopenharmony_ci		return 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int cifs_save_resume_key(const char *current_entry,
66062306a36Sopenharmony_ci	struct cifsFileInfo *file_info)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct cifs_dirent de;
66362306a36Sopenharmony_ci	int rc;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	rc = cifs_fill_dirent(&de, current_entry, file_info->srch_inf.info_level,
66662306a36Sopenharmony_ci			      file_info->srch_inf.unicode);
66762306a36Sopenharmony_ci	if (!rc) {
66862306a36Sopenharmony_ci		file_info->srch_inf.presume_name = de.name;
66962306a36Sopenharmony_ci		file_info->srch_inf.resume_name_len = de.namelen;
67062306a36Sopenharmony_ci		file_info->srch_inf.resume_key = de.resume_key;
67162306a36Sopenharmony_ci	}
67262306a36Sopenharmony_ci	return rc;
67362306a36Sopenharmony_ci}
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci/*
67662306a36Sopenharmony_ci * Find the corresponding entry in the search. Note that the SMB server returns
67762306a36Sopenharmony_ci * search entries for . and .. which complicates logic here if we choose to
67862306a36Sopenharmony_ci * parse for them and we do not assume that they are located in the findfirst
67962306a36Sopenharmony_ci * return buffer. We start counting in the buffer with entry 2 and increment for
68062306a36Sopenharmony_ci * every entry (do not increment for . or .. entry).
68162306a36Sopenharmony_ci */
68262306a36Sopenharmony_cistatic int
68362306a36Sopenharmony_cifind_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon, loff_t pos,
68462306a36Sopenharmony_ci		struct file *file, const char *full_path,
68562306a36Sopenharmony_ci		char **current_entry, int *num_to_ret)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	__u16 search_flags;
68862306a36Sopenharmony_ci	int rc = 0;
68962306a36Sopenharmony_ci	int pos_in_buf = 0;
69062306a36Sopenharmony_ci	loff_t first_entry_in_buffer;
69162306a36Sopenharmony_ci	loff_t index_to_find = pos;
69262306a36Sopenharmony_ci	struct cifsFileInfo *cfile = file->private_data;
69362306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
69462306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
69562306a36Sopenharmony_ci	/* check if index in the buffer */
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!server->ops->query_dir_first || !server->ops->query_dir_next)
69862306a36Sopenharmony_ci		return -ENOSYS;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
70162306a36Sopenharmony_ci		return -ENOENT;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	*current_entry = NULL;
70462306a36Sopenharmony_ci	first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
70562306a36Sopenharmony_ci					cfile->srch_inf.entries_in_buffer;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/*
70862306a36Sopenharmony_ci	 * If first entry in buf is zero then is first buffer
70962306a36Sopenharmony_ci	 * in search response data which means it is likely . and ..
71062306a36Sopenharmony_ci	 * will be in this buffer, although some servers do not return
71162306a36Sopenharmony_ci	 * . and .. for the root of a drive and for those we need
71262306a36Sopenharmony_ci	 * to start two entries earlier.
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci	dump_cifs_file_struct(file, "In fce ");
71662306a36Sopenharmony_ci	if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
71762306a36Sopenharmony_ci	     is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
71862306a36Sopenharmony_ci		/* close and restart search */
71962306a36Sopenharmony_ci		cifs_dbg(FYI, "search backing up - close and restart search\n");
72062306a36Sopenharmony_ci		spin_lock(&cfile->file_info_lock);
72162306a36Sopenharmony_ci		if (server->ops->dir_needs_close(cfile)) {
72262306a36Sopenharmony_ci			cfile->invalidHandle = true;
72362306a36Sopenharmony_ci			spin_unlock(&cfile->file_info_lock);
72462306a36Sopenharmony_ci			if (server->ops->close_dir)
72562306a36Sopenharmony_ci				server->ops->close_dir(xid, tcon, &cfile->fid);
72662306a36Sopenharmony_ci		} else
72762306a36Sopenharmony_ci			spin_unlock(&cfile->file_info_lock);
72862306a36Sopenharmony_ci		if (cfile->srch_inf.ntwrk_buf_start) {
72962306a36Sopenharmony_ci			cifs_dbg(FYI, "freeing SMB ff cache buf on search rewind\n");
73062306a36Sopenharmony_ci			if (cfile->srch_inf.smallBuf)
73162306a36Sopenharmony_ci				cifs_small_buf_release(cfile->srch_inf.
73262306a36Sopenharmony_ci						ntwrk_buf_start);
73362306a36Sopenharmony_ci			else
73462306a36Sopenharmony_ci				cifs_buf_release(cfile->srch_inf.
73562306a36Sopenharmony_ci						ntwrk_buf_start);
73662306a36Sopenharmony_ci			cfile->srch_inf.ntwrk_buf_start = NULL;
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci		rc = initiate_cifs_search(xid, file, full_path);
73962306a36Sopenharmony_ci		if (rc) {
74062306a36Sopenharmony_ci			cifs_dbg(FYI, "error %d reinitiating a search on rewind\n",
74162306a36Sopenharmony_ci				 rc);
74262306a36Sopenharmony_ci			return rc;
74362306a36Sopenharmony_ci		}
74462306a36Sopenharmony_ci		/* FindFirst/Next set last_entry to NULL on malformed reply */
74562306a36Sopenharmony_ci		if (cfile->srch_inf.last_entry)
74662306a36Sopenharmony_ci			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
75062306a36Sopenharmony_ci	if (backup_cred(cifs_sb))
75162306a36Sopenharmony_ci		search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
75462306a36Sopenharmony_ci	       (rc == 0) && !cfile->srch_inf.endOfSearch) {
75562306a36Sopenharmony_ci		cifs_dbg(FYI, "calling findnext2\n");
75662306a36Sopenharmony_ci		rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
75762306a36Sopenharmony_ci						 search_flags,
75862306a36Sopenharmony_ci						 &cfile->srch_inf);
75962306a36Sopenharmony_ci		/* FindFirst/Next set last_entry to NULL on malformed reply */
76062306a36Sopenharmony_ci		if (cfile->srch_inf.last_entry)
76162306a36Sopenharmony_ci			cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
76262306a36Sopenharmony_ci		if (rc)
76362306a36Sopenharmony_ci			return -ENOENT;
76462306a36Sopenharmony_ci	}
76562306a36Sopenharmony_ci	if (index_to_find < cfile->srch_inf.index_of_last_entry) {
76662306a36Sopenharmony_ci		/* we found the buffer that contains the entry */
76762306a36Sopenharmony_ci		/* scan and find it */
76862306a36Sopenharmony_ci		int i;
76962306a36Sopenharmony_ci		char *cur_ent;
77062306a36Sopenharmony_ci		char *end_of_smb;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		if (cfile->srch_inf.ntwrk_buf_start == NULL) {
77362306a36Sopenharmony_ci			cifs_dbg(VFS, "ntwrk_buf_start is NULL during readdir\n");
77462306a36Sopenharmony_ci			return -EIO;
77562306a36Sopenharmony_ci		}
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci		end_of_smb = cfile->srch_inf.ntwrk_buf_start +
77862306a36Sopenharmony_ci			server->ops->calc_smb_size(
77962306a36Sopenharmony_ci					cfile->srch_inf.ntwrk_buf_start);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci		cur_ent = cfile->srch_inf.srch_entries_start;
78262306a36Sopenharmony_ci		first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
78362306a36Sopenharmony_ci					- cfile->srch_inf.entries_in_buffer;
78462306a36Sopenharmony_ci		pos_in_buf = index_to_find - first_entry_in_buffer;
78562306a36Sopenharmony_ci		cifs_dbg(FYI, "found entry - pos_in_buf %d\n", pos_in_buf);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
78862306a36Sopenharmony_ci			/* go entry by entry figuring out which is first */
78962306a36Sopenharmony_ci			cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
79062306a36Sopenharmony_ci						cfile->srch_inf.info_level);
79162306a36Sopenharmony_ci		}
79262306a36Sopenharmony_ci		if ((cur_ent == NULL) && (i < pos_in_buf)) {
79362306a36Sopenharmony_ci			/* BB fixme - check if we should flag this error */
79462306a36Sopenharmony_ci			cifs_dbg(VFS, "reached end of buf searching for pos in buf %d index to find %lld rc %d\n",
79562306a36Sopenharmony_ci				 pos_in_buf, index_to_find, rc);
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci		rc = 0;
79862306a36Sopenharmony_ci		*current_entry = cur_ent;
79962306a36Sopenharmony_ci	} else {
80062306a36Sopenharmony_ci		cifs_dbg(FYI, "index not in buffer - could not findnext into it\n");
80162306a36Sopenharmony_ci		return 0;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
80562306a36Sopenharmony_ci		cifs_dbg(FYI, "can not return entries pos_in_buf beyond last\n");
80662306a36Sopenharmony_ci		*num_to_ret = 0;
80762306a36Sopenharmony_ci	} else
80862306a36Sopenharmony_ci		*num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	return rc;
81162306a36Sopenharmony_ci}
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_cistatic bool emit_cached_dirents(struct cached_dirents *cde,
81462306a36Sopenharmony_ci				struct dir_context *ctx)
81562306a36Sopenharmony_ci{
81662306a36Sopenharmony_ci	struct cached_dirent *dirent;
81762306a36Sopenharmony_ci	bool rc;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	list_for_each_entry(dirent, &cde->entries, entry) {
82062306a36Sopenharmony_ci		/*
82162306a36Sopenharmony_ci		 * Skip all early entries prior to the current lseek()
82262306a36Sopenharmony_ci		 * position.
82362306a36Sopenharmony_ci		 */
82462306a36Sopenharmony_ci		if (ctx->pos > dirent->pos)
82562306a36Sopenharmony_ci			continue;
82662306a36Sopenharmony_ci		/*
82762306a36Sopenharmony_ci		 * We recorded the current ->pos value for the dirent
82862306a36Sopenharmony_ci		 * when we stored it in the cache.
82962306a36Sopenharmony_ci		 * However, this sequence of ->pos values may have holes
83062306a36Sopenharmony_ci		 * in it, for example dot-dirs returned from the server
83162306a36Sopenharmony_ci		 * are suppressed.
83262306a36Sopenharmony_ci		 * Handle this bu forcing ctx->pos to be the same as the
83362306a36Sopenharmony_ci		 * ->pos of the current dirent we emit from the cache.
83462306a36Sopenharmony_ci		 * This means that when we emit these entries from the cache
83562306a36Sopenharmony_ci		 * we now emit them with the same ->pos value as in the
83662306a36Sopenharmony_ci		 * initial scan.
83762306a36Sopenharmony_ci		 */
83862306a36Sopenharmony_ci		ctx->pos = dirent->pos;
83962306a36Sopenharmony_ci		rc = dir_emit(ctx, dirent->name, dirent->namelen,
84062306a36Sopenharmony_ci			      dirent->fattr.cf_uniqueid,
84162306a36Sopenharmony_ci			      dirent->fattr.cf_dtype);
84262306a36Sopenharmony_ci		if (!rc)
84362306a36Sopenharmony_ci			return rc;
84462306a36Sopenharmony_ci		ctx->pos++;
84562306a36Sopenharmony_ci	}
84662306a36Sopenharmony_ci	return true;
84762306a36Sopenharmony_ci}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cistatic void update_cached_dirents_count(struct cached_dirents *cde,
85062306a36Sopenharmony_ci					struct dir_context *ctx)
85162306a36Sopenharmony_ci{
85262306a36Sopenharmony_ci	if (cde->ctx != ctx)
85362306a36Sopenharmony_ci		return;
85462306a36Sopenharmony_ci	if (cde->is_valid || cde->is_failed)
85562306a36Sopenharmony_ci		return;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	cde->pos++;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic void finished_cached_dirents_count(struct cached_dirents *cde,
86162306a36Sopenharmony_ci					struct dir_context *ctx)
86262306a36Sopenharmony_ci{
86362306a36Sopenharmony_ci	if (cde->ctx != ctx)
86462306a36Sopenharmony_ci		return;
86562306a36Sopenharmony_ci	if (cde->is_valid || cde->is_failed)
86662306a36Sopenharmony_ci		return;
86762306a36Sopenharmony_ci	if (ctx->pos != cde->pos)
86862306a36Sopenharmony_ci		return;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	cde->is_valid = 1;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic void add_cached_dirent(struct cached_dirents *cde,
87462306a36Sopenharmony_ci			      struct dir_context *ctx,
87562306a36Sopenharmony_ci			      const char *name, int namelen,
87662306a36Sopenharmony_ci			      struct cifs_fattr *fattr)
87762306a36Sopenharmony_ci{
87862306a36Sopenharmony_ci	struct cached_dirent *de;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (cde->ctx != ctx)
88162306a36Sopenharmony_ci		return;
88262306a36Sopenharmony_ci	if (cde->is_valid || cde->is_failed)
88362306a36Sopenharmony_ci		return;
88462306a36Sopenharmony_ci	if (ctx->pos != cde->pos) {
88562306a36Sopenharmony_ci		cde->is_failed = 1;
88662306a36Sopenharmony_ci		return;
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci	de = kzalloc(sizeof(*de), GFP_ATOMIC);
88962306a36Sopenharmony_ci	if (de == NULL) {
89062306a36Sopenharmony_ci		cde->is_failed = 1;
89162306a36Sopenharmony_ci		return;
89262306a36Sopenharmony_ci	}
89362306a36Sopenharmony_ci	de->namelen = namelen;
89462306a36Sopenharmony_ci	de->name = kstrndup(name, namelen, GFP_ATOMIC);
89562306a36Sopenharmony_ci	if (de->name == NULL) {
89662306a36Sopenharmony_ci		kfree(de);
89762306a36Sopenharmony_ci		cde->is_failed = 1;
89862306a36Sopenharmony_ci		return;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci	de->pos = ctx->pos;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
90362306a36Sopenharmony_ci
90462306a36Sopenharmony_ci	list_add_tail(&de->entry, &cde->entries);
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic bool cifs_dir_emit(struct dir_context *ctx,
90862306a36Sopenharmony_ci			  const char *name, int namelen,
90962306a36Sopenharmony_ci			  struct cifs_fattr *fattr,
91062306a36Sopenharmony_ci			  struct cached_fid *cfid)
91162306a36Sopenharmony_ci{
91262306a36Sopenharmony_ci	bool rc;
91362306a36Sopenharmony_ci	ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
91662306a36Sopenharmony_ci	if (!rc)
91762306a36Sopenharmony_ci		return rc;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	if (cfid) {
92062306a36Sopenharmony_ci		mutex_lock(&cfid->dirents.de_mutex);
92162306a36Sopenharmony_ci		add_cached_dirent(&cfid->dirents, ctx, name, namelen,
92262306a36Sopenharmony_ci				  fattr);
92362306a36Sopenharmony_ci		mutex_unlock(&cfid->dirents.de_mutex);
92462306a36Sopenharmony_ci	}
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	return rc;
92762306a36Sopenharmony_ci}
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_cistatic int cifs_filldir(char *find_entry, struct file *file,
93062306a36Sopenharmony_ci			struct dir_context *ctx,
93162306a36Sopenharmony_ci			char *scratch_buf, unsigned int max_len,
93262306a36Sopenharmony_ci			struct cached_fid *cfid)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct cifsFileInfo *file_info = file->private_data;
93562306a36Sopenharmony_ci	struct super_block *sb = file_inode(file)->i_sb;
93662306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
93762306a36Sopenharmony_ci	struct cifs_dirent de = { NULL, };
93862306a36Sopenharmony_ci	struct cifs_fattr fattr;
93962306a36Sopenharmony_ci	struct qstr name;
94062306a36Sopenharmony_ci	int rc = 0;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	rc = cifs_fill_dirent(&de, find_entry, file_info->srch_inf.info_level,
94362306a36Sopenharmony_ci			      file_info->srch_inf.unicode);
94462306a36Sopenharmony_ci	if (rc)
94562306a36Sopenharmony_ci		return rc;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (de.namelen > max_len) {
94862306a36Sopenharmony_ci		cifs_dbg(VFS, "bad search response length %zd past smb end\n",
94962306a36Sopenharmony_ci			 de.namelen);
95062306a36Sopenharmony_ci		return -EINVAL;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	/* skip . and .. since we added them first */
95462306a36Sopenharmony_ci	if (cifs_entry_is_dot(&de, file_info->srch_inf.unicode))
95562306a36Sopenharmony_ci		return 0;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	if (file_info->srch_inf.unicode) {
95862306a36Sopenharmony_ci		struct nls_table *nlt = cifs_sb->local_nls;
95962306a36Sopenharmony_ci		int map_type;
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci		map_type = cifs_remap(cifs_sb);
96262306a36Sopenharmony_ci		name.name = scratch_buf;
96362306a36Sopenharmony_ci		name.len =
96462306a36Sopenharmony_ci			cifs_from_utf16((char *)name.name, (__le16 *)de.name,
96562306a36Sopenharmony_ci					UNICODE_NAME_MAX,
96662306a36Sopenharmony_ci					min_t(size_t, de.namelen,
96762306a36Sopenharmony_ci					      (size_t)max_len), nlt, map_type);
96862306a36Sopenharmony_ci		name.len -= nls_nullsize(nlt);
96962306a36Sopenharmony_ci	} else {
97062306a36Sopenharmony_ci		name.name = de.name;
97162306a36Sopenharmony_ci		name.len = de.namelen;
97262306a36Sopenharmony_ci	}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	switch (file_info->srch_inf.info_level) {
97562306a36Sopenharmony_ci	case SMB_FIND_FILE_POSIX_INFO:
97662306a36Sopenharmony_ci		cifs_posix_to_fattr(&fattr,
97762306a36Sopenharmony_ci				    (struct smb2_posix_info *)find_entry,
97862306a36Sopenharmony_ci				    cifs_sb);
97962306a36Sopenharmony_ci		break;
98062306a36Sopenharmony_ci	case SMB_FIND_FILE_UNIX:
98162306a36Sopenharmony_ci		cifs_unix_basic_to_fattr(&fattr,
98262306a36Sopenharmony_ci					 &((FILE_UNIX_INFO *)find_entry)->basic,
98362306a36Sopenharmony_ci					 cifs_sb);
98462306a36Sopenharmony_ci		if (S_ISLNK(fattr.cf_mode))
98562306a36Sopenharmony_ci			fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
98662306a36Sopenharmony_ci		break;
98762306a36Sopenharmony_ci	case SMB_FIND_FILE_INFO_STANDARD:
98862306a36Sopenharmony_ci		cifs_std_info_to_fattr(&fattr,
98962306a36Sopenharmony_ci				       (FIND_FILE_STANDARD_INFO *)find_entry,
99062306a36Sopenharmony_ci				       cifs_sb);
99162306a36Sopenharmony_ci		break;
99262306a36Sopenharmony_ci	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
99362306a36Sopenharmony_ci	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
99462306a36Sopenharmony_ci		cifs_fulldir_info_to_fattr(&fattr, find_entry, cifs_sb);
99562306a36Sopenharmony_ci		break;
99662306a36Sopenharmony_ci	default:
99762306a36Sopenharmony_ci		cifs_dir_info_to_fattr(&fattr,
99862306a36Sopenharmony_ci				       (FILE_DIRECTORY_INFO *)find_entry,
99962306a36Sopenharmony_ci				       cifs_sb);
100062306a36Sopenharmony_ci		break;
100162306a36Sopenharmony_ci	}
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	if (de.ino && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
100462306a36Sopenharmony_ci		fattr.cf_uniqueid = de.ino;
100562306a36Sopenharmony_ci	} else {
100662306a36Sopenharmony_ci		fattr.cf_uniqueid = iunique(sb, ROOT_I);
100762306a36Sopenharmony_ci		cifs_autodisable_serverino(cifs_sb);
100862306a36Sopenharmony_ci	}
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ci	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
101162306a36Sopenharmony_ci	    couldbe_mf_symlink(&fattr))
101262306a36Sopenharmony_ci		/*
101362306a36Sopenharmony_ci		 * trying to get the type and mode can be slow,
101462306a36Sopenharmony_ci		 * so just call those regular files for now, and mark
101562306a36Sopenharmony_ci		 * for reval
101662306a36Sopenharmony_ci		 */
101762306a36Sopenharmony_ci		fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	cifs_prime_dcache(file_dentry(file), &name, &fattr);
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	return !cifs_dir_emit(ctx, name.name, name.len,
102262306a36Sopenharmony_ci			      &fattr, cfid);
102362306a36Sopenharmony_ci}
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ciint cifs_readdir(struct file *file, struct dir_context *ctx)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	int rc = 0;
102962306a36Sopenharmony_ci	unsigned int xid;
103062306a36Sopenharmony_ci	int i;
103162306a36Sopenharmony_ci	struct tcon_link *tlink = NULL;
103262306a36Sopenharmony_ci	struct cifs_tcon *tcon;
103362306a36Sopenharmony_ci	struct cifsFileInfo *cifsFile;
103462306a36Sopenharmony_ci	char *current_entry;
103562306a36Sopenharmony_ci	int num_to_fill = 0;
103662306a36Sopenharmony_ci	char *tmp_buf = NULL;
103762306a36Sopenharmony_ci	char *end_of_smb;
103862306a36Sopenharmony_ci	unsigned int max_len;
103962306a36Sopenharmony_ci	const char *full_path;
104062306a36Sopenharmony_ci	void *page = alloc_dentry_path();
104162306a36Sopenharmony_ci	struct cached_fid *cfid = NULL;
104262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	xid = get_xid();
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	full_path = build_path_from_dentry(file_dentry(file), page);
104762306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
104862306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
104962306a36Sopenharmony_ci		goto rddir2_exit;
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (file->private_data == NULL) {
105362306a36Sopenharmony_ci		tlink = cifs_sb_tlink(cifs_sb);
105462306a36Sopenharmony_ci		if (IS_ERR(tlink))
105562306a36Sopenharmony_ci			goto cache_not_found;
105662306a36Sopenharmony_ci		tcon = tlink_tcon(tlink);
105762306a36Sopenharmony_ci	} else {
105862306a36Sopenharmony_ci		cifsFile = file->private_data;
105962306a36Sopenharmony_ci		tcon = tlink_tcon(cifsFile->tlink);
106062306a36Sopenharmony_ci	}
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
106362306a36Sopenharmony_ci	cifs_put_tlink(tlink);
106462306a36Sopenharmony_ci	if (rc)
106562306a36Sopenharmony_ci		goto cache_not_found;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	mutex_lock(&cfid->dirents.de_mutex);
106862306a36Sopenharmony_ci	/*
106962306a36Sopenharmony_ci	 * If this was reading from the start of the directory
107062306a36Sopenharmony_ci	 * we need to initialize scanning and storing the
107162306a36Sopenharmony_ci	 * directory content.
107262306a36Sopenharmony_ci	 */
107362306a36Sopenharmony_ci	if (ctx->pos == 0 && cfid->dirents.ctx == NULL) {
107462306a36Sopenharmony_ci		cfid->dirents.ctx = ctx;
107562306a36Sopenharmony_ci		cfid->dirents.pos = 2;
107662306a36Sopenharmony_ci	}
107762306a36Sopenharmony_ci	/*
107862306a36Sopenharmony_ci	 * If we already have the entire directory cached then
107962306a36Sopenharmony_ci	 * we can just serve the cache.
108062306a36Sopenharmony_ci	 */
108162306a36Sopenharmony_ci	if (cfid->dirents.is_valid) {
108262306a36Sopenharmony_ci		if (!dir_emit_dots(file, ctx)) {
108362306a36Sopenharmony_ci			mutex_unlock(&cfid->dirents.de_mutex);
108462306a36Sopenharmony_ci			goto rddir2_exit;
108562306a36Sopenharmony_ci		}
108662306a36Sopenharmony_ci		emit_cached_dirents(&cfid->dirents, ctx);
108762306a36Sopenharmony_ci		mutex_unlock(&cfid->dirents.de_mutex);
108862306a36Sopenharmony_ci		goto rddir2_exit;
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci	mutex_unlock(&cfid->dirents.de_mutex);
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* Drop the cache while calling initiate_cifs_search and
109362306a36Sopenharmony_ci	 * find_cifs_entry in case there will be reconnects during
109462306a36Sopenharmony_ci	 * query_directory.
109562306a36Sopenharmony_ci	 */
109662306a36Sopenharmony_ci	close_cached_dir(cfid);
109762306a36Sopenharmony_ci	cfid = NULL;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci cache_not_found:
110062306a36Sopenharmony_ci	/*
110162306a36Sopenharmony_ci	 * Ensure FindFirst doesn't fail before doing filldir() for '.' and
110262306a36Sopenharmony_ci	 * '..'. Otherwise we won't be able to notify VFS in case of failure.
110362306a36Sopenharmony_ci	 */
110462306a36Sopenharmony_ci	if (file->private_data == NULL) {
110562306a36Sopenharmony_ci		rc = initiate_cifs_search(xid, file, full_path);
110662306a36Sopenharmony_ci		cifs_dbg(FYI, "initiate cifs search rc %d\n", rc);
110762306a36Sopenharmony_ci		if (rc)
110862306a36Sopenharmony_ci			goto rddir2_exit;
110962306a36Sopenharmony_ci	}
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	if (!dir_emit_dots(file, ctx))
111262306a36Sopenharmony_ci		goto rddir2_exit;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	/* 1) If search is active,
111562306a36Sopenharmony_ci		is in current search buffer?
111662306a36Sopenharmony_ci		if it before then restart search
111762306a36Sopenharmony_ci		if after then keep searching till find it */
111862306a36Sopenharmony_ci	cifsFile = file->private_data;
111962306a36Sopenharmony_ci	if (cifsFile->srch_inf.endOfSearch) {
112062306a36Sopenharmony_ci		if (cifsFile->srch_inf.emptyDir) {
112162306a36Sopenharmony_ci			cifs_dbg(FYI, "End of search, empty dir\n");
112262306a36Sopenharmony_ci			rc = 0;
112362306a36Sopenharmony_ci			goto rddir2_exit;
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci	} /* else {
112662306a36Sopenharmony_ci		cifsFile->invalidHandle = true;
112762306a36Sopenharmony_ci		tcon->ses->server->close(xid, tcon, &cifsFile->fid);
112862306a36Sopenharmony_ci	} */
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	tcon = tlink_tcon(cifsFile->tlink);
113162306a36Sopenharmony_ci	rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
113262306a36Sopenharmony_ci			     &current_entry, &num_to_fill);
113362306a36Sopenharmony_ci	open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
113462306a36Sopenharmony_ci	if (rc) {
113562306a36Sopenharmony_ci		cifs_dbg(FYI, "fce error %d\n", rc);
113662306a36Sopenharmony_ci		goto rddir2_exit;
113762306a36Sopenharmony_ci	} else if (current_entry != NULL) {
113862306a36Sopenharmony_ci		cifs_dbg(FYI, "entry %lld found\n", ctx->pos);
113962306a36Sopenharmony_ci	} else {
114062306a36Sopenharmony_ci		if (cfid) {
114162306a36Sopenharmony_ci			mutex_lock(&cfid->dirents.de_mutex);
114262306a36Sopenharmony_ci			finished_cached_dirents_count(&cfid->dirents, ctx);
114362306a36Sopenharmony_ci			mutex_unlock(&cfid->dirents.de_mutex);
114462306a36Sopenharmony_ci		}
114562306a36Sopenharmony_ci		cifs_dbg(FYI, "Could not find entry\n");
114662306a36Sopenharmony_ci		goto rddir2_exit;
114762306a36Sopenharmony_ci	}
114862306a36Sopenharmony_ci	cifs_dbg(FYI, "loop through %d times filling dir for net buf %p\n",
114962306a36Sopenharmony_ci		 num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
115062306a36Sopenharmony_ci	max_len = tcon->ses->server->ops->calc_smb_size(
115162306a36Sopenharmony_ci			cifsFile->srch_inf.ntwrk_buf_start);
115262306a36Sopenharmony_ci	end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci	tmp_buf = kmalloc(UNICODE_NAME_MAX, GFP_KERNEL);
115562306a36Sopenharmony_ci	if (tmp_buf == NULL) {
115662306a36Sopenharmony_ci		rc = -ENOMEM;
115762306a36Sopenharmony_ci		goto rddir2_exit;
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	for (i = 0; i < num_to_fill; i++) {
116162306a36Sopenharmony_ci		if (current_entry == NULL) {
116262306a36Sopenharmony_ci			/* evaluate whether this case is an error */
116362306a36Sopenharmony_ci			cifs_dbg(VFS, "past SMB end,  num to fill %d i %d\n",
116462306a36Sopenharmony_ci				 num_to_fill, i);
116562306a36Sopenharmony_ci			break;
116662306a36Sopenharmony_ci		}
116762306a36Sopenharmony_ci		/*
116862306a36Sopenharmony_ci		 * if buggy server returns . and .. late do we want to
116962306a36Sopenharmony_ci		 * check for that here?
117062306a36Sopenharmony_ci		 */
117162306a36Sopenharmony_ci		*tmp_buf = 0;
117262306a36Sopenharmony_ci		rc = cifs_filldir(current_entry, file, ctx,
117362306a36Sopenharmony_ci				  tmp_buf, max_len, cfid);
117462306a36Sopenharmony_ci		if (rc) {
117562306a36Sopenharmony_ci			if (rc > 0)
117662306a36Sopenharmony_ci				rc = 0;
117762306a36Sopenharmony_ci			break;
117862306a36Sopenharmony_ci		}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci		ctx->pos++;
118162306a36Sopenharmony_ci		if (cfid) {
118262306a36Sopenharmony_ci			mutex_lock(&cfid->dirents.de_mutex);
118362306a36Sopenharmony_ci			update_cached_dirents_count(&cfid->dirents, ctx);
118462306a36Sopenharmony_ci			mutex_unlock(&cfid->dirents.de_mutex);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci		if (ctx->pos ==
118862306a36Sopenharmony_ci			cifsFile->srch_inf.index_of_last_entry) {
118962306a36Sopenharmony_ci			cifs_dbg(FYI, "last entry in buf at pos %lld %s\n",
119062306a36Sopenharmony_ci				 ctx->pos, tmp_buf);
119162306a36Sopenharmony_ci			cifs_save_resume_key(current_entry, cifsFile);
119262306a36Sopenharmony_ci			break;
119362306a36Sopenharmony_ci		}
119462306a36Sopenharmony_ci		current_entry =
119562306a36Sopenharmony_ci			nxt_dir_entry(current_entry, end_of_smb,
119662306a36Sopenharmony_ci				      cifsFile->srch_inf.info_level);
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci	kfree(tmp_buf);
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cirddir2_exit:
120162306a36Sopenharmony_ci	if (cfid)
120262306a36Sopenharmony_ci		close_cached_dir(cfid);
120362306a36Sopenharmony_ci	free_dentry_path(page);
120462306a36Sopenharmony_ci	free_xid(xid);
120562306a36Sopenharmony_ci	return rc;
120662306a36Sopenharmony_ci}
1207