162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   vfs operations that deal with dentries
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2009
762306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/stat.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/namei.h>
1462306a36Sopenharmony_ci#include <linux/mount.h>
1562306a36Sopenharmony_ci#include <linux/file.h>
1662306a36Sopenharmony_ci#include "cifsfs.h"
1762306a36Sopenharmony_ci#include "cifspdu.h"
1862306a36Sopenharmony_ci#include "cifsglob.h"
1962306a36Sopenharmony_ci#include "cifsproto.h"
2062306a36Sopenharmony_ci#include "cifs_debug.h"
2162306a36Sopenharmony_ci#include "cifs_fs_sb.h"
2262306a36Sopenharmony_ci#include "cifs_unicode.h"
2362306a36Sopenharmony_ci#include "fs_context.h"
2462306a36Sopenharmony_ci#include "cifs_ioctl.h"
2562306a36Sopenharmony_ci#include "fscache.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic void
2862306a36Sopenharmony_cirenew_parental_timestamps(struct dentry *direntry)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	/* BB check if there is a way to get the kernel to do this or if we
3162306a36Sopenharmony_ci	   really need this */
3262306a36Sopenharmony_ci	do {
3362306a36Sopenharmony_ci		cifs_set_time(direntry, jiffies);
3462306a36Sopenharmony_ci		direntry = direntry->d_parent;
3562306a36Sopenharmony_ci	} while (!IS_ROOT(direntry));
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cichar *
3962306a36Sopenharmony_cicifs_build_path_to_root(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
4062306a36Sopenharmony_ci			struct cifs_tcon *tcon, int add_treename)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int pplen = ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
4362306a36Sopenharmony_ci	int dfsplen;
4462306a36Sopenharmony_ci	char *full_path = NULL;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* if no prefix path, simply set path to the root of share to "" */
4762306a36Sopenharmony_ci	if (pplen == 0) {
4862306a36Sopenharmony_ci		full_path = kzalloc(1, GFP_KERNEL);
4962306a36Sopenharmony_ci		return full_path;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	if (add_treename)
5362306a36Sopenharmony_ci		dfsplen = strnlen(tcon->tree_name, MAX_TREE_SIZE + 1);
5462306a36Sopenharmony_ci	else
5562306a36Sopenharmony_ci		dfsplen = 0;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	full_path = kmalloc(dfsplen + pplen + 1, GFP_KERNEL);
5862306a36Sopenharmony_ci	if (full_path == NULL)
5962306a36Sopenharmony_ci		return full_path;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (dfsplen)
6262306a36Sopenharmony_ci		memcpy(full_path, tcon->tree_name, dfsplen);
6362306a36Sopenharmony_ci	full_path[dfsplen] = CIFS_DIR_SEP(cifs_sb);
6462306a36Sopenharmony_ci	memcpy(full_path + dfsplen + 1, ctx->prepath, pplen);
6562306a36Sopenharmony_ci	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
6662306a36Sopenharmony_ci	return full_path;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci/* Note: caller must free return buffer */
7062306a36Sopenharmony_ciconst char *
7162306a36Sopenharmony_cibuild_path_from_dentry(struct dentry *direntry, void *page)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
7462306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
7562306a36Sopenharmony_ci	bool prefix = tcon->Flags & SMB_SHARE_IS_IN_DFS;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return build_path_from_dentry_optional_prefix(direntry, page,
7862306a36Sopenharmony_ci						      prefix);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cichar *__build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page,
8262306a36Sopenharmony_ci					       const char *tree, int tree_len,
8362306a36Sopenharmony_ci					       bool prefix)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int dfsplen;
8662306a36Sopenharmony_ci	int pplen = 0;
8762306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
8862306a36Sopenharmony_ci	char dirsep = CIFS_DIR_SEP(cifs_sb);
8962306a36Sopenharmony_ci	char *s;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (unlikely(!page))
9262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (prefix)
9562306a36Sopenharmony_ci		dfsplen = strnlen(tree, tree_len + 1);
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		dfsplen = 0;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
10062306a36Sopenharmony_ci		pplen = cifs_sb->prepath ? strlen(cifs_sb->prepath) + 1 : 0;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	s = dentry_path_raw(direntry, page, PATH_MAX);
10362306a36Sopenharmony_ci	if (IS_ERR(s))
10462306a36Sopenharmony_ci		return s;
10562306a36Sopenharmony_ci	if (!s[1])	// for root we want "", not "/"
10662306a36Sopenharmony_ci		s++;
10762306a36Sopenharmony_ci	if (s < (char *)page + pplen + dfsplen)
10862306a36Sopenharmony_ci		return ERR_PTR(-ENAMETOOLONG);
10962306a36Sopenharmony_ci	if (pplen) {
11062306a36Sopenharmony_ci		cifs_dbg(FYI, "using cifs_sb prepath <%s>\n", cifs_sb->prepath);
11162306a36Sopenharmony_ci		s -= pplen;
11262306a36Sopenharmony_ci		memcpy(s + 1, cifs_sb->prepath, pplen - 1);
11362306a36Sopenharmony_ci		*s = '/';
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci	if (dirsep != '/') {
11662306a36Sopenharmony_ci		/* BB test paths to Windows with '/' in the midst of prepath */
11762306a36Sopenharmony_ci		char *p;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		for (p = s; *p; p++)
12062306a36Sopenharmony_ci			if (*p == '/')
12162306a36Sopenharmony_ci				*p = dirsep;
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci	if (dfsplen) {
12462306a36Sopenharmony_ci		s -= dfsplen;
12562306a36Sopenharmony_ci		memcpy(s, tree, dfsplen);
12662306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
12762306a36Sopenharmony_ci			int i;
12862306a36Sopenharmony_ci			for (i = 0; i < dfsplen; i++) {
12962306a36Sopenharmony_ci				if (s[i] == '\\')
13062306a36Sopenharmony_ci					s[i] = '/';
13162306a36Sopenharmony_ci			}
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci	return s;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cichar *build_path_from_dentry_optional_prefix(struct dentry *direntry, void *page,
13862306a36Sopenharmony_ci					     bool prefix)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
14162306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	return __build_path_from_dentry_optional_prefix(direntry, page, tcon->tree_name,
14462306a36Sopenharmony_ci							MAX_TREE_SIZE, prefix);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci/*
14862306a36Sopenharmony_ci * Don't allow path components longer than the server max.
14962306a36Sopenharmony_ci * Don't allow the separator character in a path component.
15062306a36Sopenharmony_ci * The VFS will not allow "/", but "\" is allowed by posix.
15162306a36Sopenharmony_ci */
15262306a36Sopenharmony_cistatic int
15362306a36Sopenharmony_cicheck_name(struct dentry *direntry, struct cifs_tcon *tcon)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
15662306a36Sopenharmony_ci	int i;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (unlikely(tcon->fsAttrInfo.MaxPathNameComponentLength &&
15962306a36Sopenharmony_ci		     direntry->d_name.len >
16062306a36Sopenharmony_ci		     le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength)))
16162306a36Sopenharmony_ci		return -ENAMETOOLONG;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS)) {
16462306a36Sopenharmony_ci		for (i = 0; i < direntry->d_name.len; i++) {
16562306a36Sopenharmony_ci			if (direntry->d_name.name[i] == '\\') {
16662306a36Sopenharmony_ci				cifs_dbg(FYI, "Invalid file name\n");
16762306a36Sopenharmony_ci				return -EINVAL;
16862306a36Sopenharmony_ci			}
16962306a36Sopenharmony_ci		}
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	return 0;
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci/* Inode operations in similar order to how they appear in Linux file fs.h */
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
17862306a36Sopenharmony_ci			  struct tcon_link *tlink, unsigned int oflags, umode_t mode, __u32 *oplock,
17962306a36Sopenharmony_ci			  struct cifs_fid *fid, struct cifs_open_info_data *buf)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	int rc = -ENOENT;
18262306a36Sopenharmony_ci	int create_options = CREATE_NOT_DIR;
18362306a36Sopenharmony_ci	int desired_access;
18462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
18562306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(tlink);
18662306a36Sopenharmony_ci	const char *full_path;
18762306a36Sopenharmony_ci	void *page = alloc_dentry_path();
18862306a36Sopenharmony_ci	struct inode *newinode = NULL;
18962306a36Sopenharmony_ci	int disposition;
19062306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
19162306a36Sopenharmony_ci	struct cifs_open_parms oparms;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	*oplock = 0;
19462306a36Sopenharmony_ci	if (tcon->ses->server->oplocks)
19562306a36Sopenharmony_ci		*oplock = REQ_OPLOCK;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
19862306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
19962306a36Sopenharmony_ci		free_dentry_path(page);
20062306a36Sopenharmony_ci		return PTR_ERR(full_path);
20162306a36Sopenharmony_ci	}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
20462306a36Sopenharmony_ci	if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
20562306a36Sopenharmony_ci	    (CIFS_UNIX_POSIX_PATH_OPS_CAP &
20662306a36Sopenharmony_ci			le64_to_cpu(tcon->fsUnixInfo.Capability))) {
20762306a36Sopenharmony_ci		rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
20862306a36Sopenharmony_ci				     oflags, oplock, &fid->netfid, xid);
20962306a36Sopenharmony_ci		switch (rc) {
21062306a36Sopenharmony_ci		case 0:
21162306a36Sopenharmony_ci			if (newinode == NULL) {
21262306a36Sopenharmony_ci				/* query inode info */
21362306a36Sopenharmony_ci				goto cifs_create_get_file_info;
21462306a36Sopenharmony_ci			}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci			if (S_ISDIR(newinode->i_mode)) {
21762306a36Sopenharmony_ci				CIFSSMBClose(xid, tcon, fid->netfid);
21862306a36Sopenharmony_ci				iput(newinode);
21962306a36Sopenharmony_ci				rc = -EISDIR;
22062306a36Sopenharmony_ci				goto out;
22162306a36Sopenharmony_ci			}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci			if (!S_ISREG(newinode->i_mode)) {
22462306a36Sopenharmony_ci				/*
22562306a36Sopenharmony_ci				 * The server may allow us to open things like
22662306a36Sopenharmony_ci				 * FIFOs, but the client isn't set up to deal
22762306a36Sopenharmony_ci				 * with that. If it's not a regular file, just
22862306a36Sopenharmony_ci				 * close it and proceed as if it were a normal
22962306a36Sopenharmony_ci				 * lookup.
23062306a36Sopenharmony_ci				 */
23162306a36Sopenharmony_ci				CIFSSMBClose(xid, tcon, fid->netfid);
23262306a36Sopenharmony_ci				goto cifs_create_get_file_info;
23362306a36Sopenharmony_ci			}
23462306a36Sopenharmony_ci			/* success, no need to query */
23562306a36Sopenharmony_ci			goto cifs_create_set_dentry;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		case -ENOENT:
23862306a36Sopenharmony_ci			goto cifs_create_get_file_info;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		case -EIO:
24162306a36Sopenharmony_ci		case -EINVAL:
24262306a36Sopenharmony_ci			/*
24362306a36Sopenharmony_ci			 * EIO could indicate that (posix open) operation is not
24462306a36Sopenharmony_ci			 * supported, despite what server claimed in capability
24562306a36Sopenharmony_ci			 * negotiation.
24662306a36Sopenharmony_ci			 *
24762306a36Sopenharmony_ci			 * POSIX open in samba versions 3.3.1 and earlier could
24862306a36Sopenharmony_ci			 * incorrectly fail with invalid parameter.
24962306a36Sopenharmony_ci			 */
25062306a36Sopenharmony_ci			tcon->broken_posix_open = true;
25162306a36Sopenharmony_ci			break;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		case -EREMOTE:
25462306a36Sopenharmony_ci		case -EOPNOTSUPP:
25562306a36Sopenharmony_ci			/*
25662306a36Sopenharmony_ci			 * EREMOTE indicates DFS junction, which is not handled
25762306a36Sopenharmony_ci			 * in posix open.  If either that or op not supported
25862306a36Sopenharmony_ci			 * returned, follow the normal lookup.
25962306a36Sopenharmony_ci			 */
26062306a36Sopenharmony_ci			break;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		default:
26362306a36Sopenharmony_ci			goto out;
26462306a36Sopenharmony_ci		}
26562306a36Sopenharmony_ci		/*
26662306a36Sopenharmony_ci		 * fallthrough to retry, using older open call, this is case
26762306a36Sopenharmony_ci		 * where server does not support this SMB level, and falsely
26862306a36Sopenharmony_ci		 * claims capability (also get here for DFS case which should be
26962306a36Sopenharmony_ci		 * rare for path not covered on files)
27062306a36Sopenharmony_ci		 */
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	desired_access = 0;
27562306a36Sopenharmony_ci	if (OPEN_FMODE(oflags) & FMODE_READ)
27662306a36Sopenharmony_ci		desired_access |= GENERIC_READ; /* is this too little? */
27762306a36Sopenharmony_ci	if (OPEN_FMODE(oflags) & FMODE_WRITE)
27862306a36Sopenharmony_ci		desired_access |= GENERIC_WRITE;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	disposition = FILE_OVERWRITE_IF;
28162306a36Sopenharmony_ci	if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
28262306a36Sopenharmony_ci		disposition = FILE_CREATE;
28362306a36Sopenharmony_ci	else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
28462306a36Sopenharmony_ci		disposition = FILE_OVERWRITE_IF;
28562306a36Sopenharmony_ci	else if ((oflags & O_CREAT) == O_CREAT)
28662306a36Sopenharmony_ci		disposition = FILE_OPEN_IF;
28762306a36Sopenharmony_ci	else
28862306a36Sopenharmony_ci		cifs_dbg(FYI, "Create flag not set in create function\n");
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/*
29162306a36Sopenharmony_ci	 * BB add processing to set equivalent of mode - e.g. via CreateX with
29262306a36Sopenharmony_ci	 * ACLs
29362306a36Sopenharmony_ci	 */
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (!server->ops->open) {
29662306a36Sopenharmony_ci		rc = -ENOSYS;
29762306a36Sopenharmony_ci		goto out;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	/*
30162306a36Sopenharmony_ci	 * if we're not using unix extensions, see if we need to set
30262306a36Sopenharmony_ci	 * ATTR_READONLY on the create call
30362306a36Sopenharmony_ci	 */
30462306a36Sopenharmony_ci	if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
30562306a36Sopenharmony_ci		create_options |= CREATE_OPTION_READONLY;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	oparms = (struct cifs_open_parms) {
30862306a36Sopenharmony_ci		.tcon = tcon,
30962306a36Sopenharmony_ci		.cifs_sb = cifs_sb,
31062306a36Sopenharmony_ci		.desired_access = desired_access,
31162306a36Sopenharmony_ci		.create_options = cifs_create_options(cifs_sb, create_options),
31262306a36Sopenharmony_ci		.disposition = disposition,
31362306a36Sopenharmony_ci		.path = full_path,
31462306a36Sopenharmony_ci		.fid = fid,
31562306a36Sopenharmony_ci		.mode = mode,
31662306a36Sopenharmony_ci	};
31762306a36Sopenharmony_ci	rc = server->ops->open(xid, &oparms, oplock, buf);
31862306a36Sopenharmony_ci	if (rc) {
31962306a36Sopenharmony_ci		cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
32062306a36Sopenharmony_ci		goto out;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
32462306a36Sopenharmony_ci	/*
32562306a36Sopenharmony_ci	 * If Open reported that we actually created a file then we now have to
32662306a36Sopenharmony_ci	 * set the mode if possible.
32762306a36Sopenharmony_ci	 */
32862306a36Sopenharmony_ci	if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
32962306a36Sopenharmony_ci		struct cifs_unix_set_info_args args = {
33062306a36Sopenharmony_ci				.mode	= mode,
33162306a36Sopenharmony_ci				.ctime	= NO_CHANGE_64,
33262306a36Sopenharmony_ci				.atime	= NO_CHANGE_64,
33362306a36Sopenharmony_ci				.mtime	= NO_CHANGE_64,
33462306a36Sopenharmony_ci				.device	= 0,
33562306a36Sopenharmony_ci		};
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
33862306a36Sopenharmony_ci			args.uid = current_fsuid();
33962306a36Sopenharmony_ci			if (inode->i_mode & S_ISGID)
34062306a36Sopenharmony_ci				args.gid = inode->i_gid;
34162306a36Sopenharmony_ci			else
34262306a36Sopenharmony_ci				args.gid = current_fsgid();
34362306a36Sopenharmony_ci		} else {
34462306a36Sopenharmony_ci			args.uid = INVALID_UID; /* no change */
34562306a36Sopenharmony_ci			args.gid = INVALID_GID; /* no change */
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci		CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid,
34862306a36Sopenharmony_ci				       current->tgid);
34962306a36Sopenharmony_ci	} else {
35062306a36Sopenharmony_ci		/*
35162306a36Sopenharmony_ci		 * BB implement mode setting via Windows security
35262306a36Sopenharmony_ci		 * descriptors e.g.
35362306a36Sopenharmony_ci		 */
35462306a36Sopenharmony_ci		/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci		/* Could set r/o dos attribute if mode & 0222 == 0 */
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cicifs_create_get_file_info:
36062306a36Sopenharmony_ci	/* server might mask mode so we have to query for it */
36162306a36Sopenharmony_ci	if (tcon->unix_ext)
36262306a36Sopenharmony_ci		rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
36362306a36Sopenharmony_ci					      xid);
36462306a36Sopenharmony_ci	else {
36562306a36Sopenharmony_ci#else
36662306a36Sopenharmony_ci	{
36762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
36862306a36Sopenharmony_ci		/* TODO: Add support for calling POSIX query info here, but passing in fid */
36962306a36Sopenharmony_ci		rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, fid);
37062306a36Sopenharmony_ci		if (newinode) {
37162306a36Sopenharmony_ci			if (server->ops->set_lease_key)
37262306a36Sopenharmony_ci				server->ops->set_lease_key(newinode, fid);
37362306a36Sopenharmony_ci			if ((*oplock & CIFS_CREATE_ACTION) && S_ISREG(newinode->i_mode)) {
37462306a36Sopenharmony_ci				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
37562306a36Sopenharmony_ci					newinode->i_mode = mode;
37662306a36Sopenharmony_ci				if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
37762306a36Sopenharmony_ci					newinode->i_uid = current_fsuid();
37862306a36Sopenharmony_ci					if (inode->i_mode & S_ISGID)
37962306a36Sopenharmony_ci						newinode->i_gid = inode->i_gid;
38062306a36Sopenharmony_ci					else
38162306a36Sopenharmony_ci						newinode->i_gid = current_fsgid();
38262306a36Sopenharmony_ci				}
38362306a36Sopenharmony_ci			}
38462306a36Sopenharmony_ci		}
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
38862306a36Sopenharmony_cicifs_create_set_dentry:
38962306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
39062306a36Sopenharmony_ci	if (rc != 0) {
39162306a36Sopenharmony_ci		cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n",
39262306a36Sopenharmony_ci			 rc);
39362306a36Sopenharmony_ci		goto out_err;
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	if (newinode)
39762306a36Sopenharmony_ci		if (S_ISDIR(newinode->i_mode)) {
39862306a36Sopenharmony_ci			rc = -EISDIR;
39962306a36Sopenharmony_ci			goto out_err;
40062306a36Sopenharmony_ci		}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	d_drop(direntry);
40362306a36Sopenharmony_ci	d_add(direntry, newinode);
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ciout:
40662306a36Sopenharmony_ci	free_dentry_path(page);
40762306a36Sopenharmony_ci	return rc;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ciout_err:
41062306a36Sopenharmony_ci	if (server->ops->close)
41162306a36Sopenharmony_ci		server->ops->close(xid, tcon, fid);
41262306a36Sopenharmony_ci	if (newinode)
41362306a36Sopenharmony_ci		iput(newinode);
41462306a36Sopenharmony_ci	goto out;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ciint
41862306a36Sopenharmony_cicifs_atomic_open(struct inode *inode, struct dentry *direntry,
41962306a36Sopenharmony_ci		 struct file *file, unsigned oflags, umode_t mode)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	int rc;
42262306a36Sopenharmony_ci	unsigned int xid;
42362306a36Sopenharmony_ci	struct tcon_link *tlink;
42462306a36Sopenharmony_ci	struct cifs_tcon *tcon;
42562306a36Sopenharmony_ci	struct TCP_Server_Info *server;
42662306a36Sopenharmony_ci	struct cifs_fid fid = {};
42762306a36Sopenharmony_ci	struct cifs_pending_open open;
42862306a36Sopenharmony_ci	__u32 oplock;
42962306a36Sopenharmony_ci	struct cifsFileInfo *file_info;
43062306a36Sopenharmony_ci	struct cifs_open_info_data buf = {};
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
43362306a36Sopenharmony_ci		return -EIO;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/*
43662306a36Sopenharmony_ci	 * Posix open is only called (at lookup time) for file create now. For
43762306a36Sopenharmony_ci	 * opens (rather than creates), because we do not know if it is a file
43862306a36Sopenharmony_ci	 * or directory yet, and current Samba no longer allows us to do posix
43962306a36Sopenharmony_ci	 * open on dirs, we could end up wasting an open call on what turns out
44062306a36Sopenharmony_ci	 * to be a dir. For file opens, we wait to call posix open till
44162306a36Sopenharmony_ci	 * cifs_open.  It could be added to atomic_open in the future but the
44262306a36Sopenharmony_ci	 * performance tradeoff of the extra network request when EISDIR or
44362306a36Sopenharmony_ci	 * EACCES is returned would have to be weighed against the 50% reduction
44462306a36Sopenharmony_ci	 * in network traffic in the other paths.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	if (!(oflags & O_CREAT)) {
44762306a36Sopenharmony_ci		struct dentry *res;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		/*
45062306a36Sopenharmony_ci		 * Check for hashed negative dentry. We have already revalidated
45162306a36Sopenharmony_ci		 * the dentry and it is fine. No need to perform another lookup.
45262306a36Sopenharmony_ci		 */
45362306a36Sopenharmony_ci		if (!d_in_lookup(direntry))
45462306a36Sopenharmony_ci			return -ENOENT;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		res = cifs_lookup(inode, direntry, 0);
45762306a36Sopenharmony_ci		if (IS_ERR(res))
45862306a36Sopenharmony_ci			return PTR_ERR(res);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci		return finish_no_open(file, res);
46162306a36Sopenharmony_ci	}
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	xid = get_xid();
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
46662306a36Sopenharmony_ci		 inode, direntry, direntry);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
46962306a36Sopenharmony_ci	if (IS_ERR(tlink)) {
47062306a36Sopenharmony_ci		rc = PTR_ERR(tlink);
47162306a36Sopenharmony_ci		goto out_free_xid;
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	rc = check_name(direntry, tcon);
47762306a36Sopenharmony_ci	if (rc)
47862306a36Sopenharmony_ci		goto out;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	server = tcon->ses->server;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	if (server->ops->new_lease_key)
48362306a36Sopenharmony_ci		server->ops->new_lease_key(&fid);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	cifs_add_pending_open(&fid, tlink, &open);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
48862306a36Sopenharmony_ci			    &oplock, &fid, &buf);
48962306a36Sopenharmony_ci	if (rc) {
49062306a36Sopenharmony_ci		cifs_del_pending_open(&open);
49162306a36Sopenharmony_ci		goto out;
49262306a36Sopenharmony_ci	}
49362306a36Sopenharmony_ci
49462306a36Sopenharmony_ci	if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
49562306a36Sopenharmony_ci		file->f_mode |= FMODE_CREATED;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	rc = finish_open(file, direntry, generic_file_open);
49862306a36Sopenharmony_ci	if (rc) {
49962306a36Sopenharmony_ci		if (server->ops->close)
50062306a36Sopenharmony_ci			server->ops->close(xid, tcon, &fid);
50162306a36Sopenharmony_ci		cifs_del_pending_open(&open);
50262306a36Sopenharmony_ci		goto out;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	if (file->f_flags & O_DIRECT &&
50662306a36Sopenharmony_ci	    CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
50762306a36Sopenharmony_ci		if (CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
50862306a36Sopenharmony_ci			file->f_op = &cifs_file_direct_nobrl_ops;
50962306a36Sopenharmony_ci		else
51062306a36Sopenharmony_ci			file->f_op = &cifs_file_direct_ops;
51162306a36Sopenharmony_ci		}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	file_info = cifs_new_fileinfo(&fid, file, tlink, oplock, buf.symlink_target);
51462306a36Sopenharmony_ci	if (file_info == NULL) {
51562306a36Sopenharmony_ci		if (server->ops->close)
51662306a36Sopenharmony_ci			server->ops->close(xid, tcon, &fid);
51762306a36Sopenharmony_ci		cifs_del_pending_open(&open);
51862306a36Sopenharmony_ci		rc = -ENOMEM;
51962306a36Sopenharmony_ci		goto out;
52062306a36Sopenharmony_ci	}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	fscache_use_cookie(cifs_inode_cookie(file_inode(file)),
52362306a36Sopenharmony_ci			   file->f_mode & FMODE_WRITE);
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ciout:
52662306a36Sopenharmony_ci	cifs_put_tlink(tlink);
52762306a36Sopenharmony_ciout_free_xid:
52862306a36Sopenharmony_ci	free_xid(xid);
52962306a36Sopenharmony_ci	cifs_free_open_info(&buf);
53062306a36Sopenharmony_ci	return rc;
53162306a36Sopenharmony_ci}
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ciint cifs_create(struct mnt_idmap *idmap, struct inode *inode,
53462306a36Sopenharmony_ci		struct dentry *direntry, umode_t mode, bool excl)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int rc;
53762306a36Sopenharmony_ci	unsigned int xid = get_xid();
53862306a36Sopenharmony_ci	/*
53962306a36Sopenharmony_ci	 * BB below access is probably too much for mknod to request
54062306a36Sopenharmony_ci	 *    but we have to do query and setpathinfo so requesting
54162306a36Sopenharmony_ci	 *    less could fail (unless we want to request getatr and setatr
54262306a36Sopenharmony_ci	 *    permissions (only).  At least for POSIX we do not have to
54362306a36Sopenharmony_ci	 *    request so much.
54462306a36Sopenharmony_ci	 */
54562306a36Sopenharmony_ci	unsigned oflags = O_EXCL | O_CREAT | O_RDWR;
54662306a36Sopenharmony_ci	struct tcon_link *tlink;
54762306a36Sopenharmony_ci	struct cifs_tcon *tcon;
54862306a36Sopenharmony_ci	struct TCP_Server_Info *server;
54962306a36Sopenharmony_ci	struct cifs_fid fid;
55062306a36Sopenharmony_ci	__u32 oplock;
55162306a36Sopenharmony_ci	struct cifs_open_info_data buf = {};
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
55462306a36Sopenharmony_ci		 inode, direntry, direntry);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb)))) {
55762306a36Sopenharmony_ci		rc = -EIO;
55862306a36Sopenharmony_ci		goto out_free_xid;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	tlink = cifs_sb_tlink(CIFS_SB(inode->i_sb));
56262306a36Sopenharmony_ci	rc = PTR_ERR(tlink);
56362306a36Sopenharmony_ci	if (IS_ERR(tlink))
56462306a36Sopenharmony_ci		goto out_free_xid;
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
56762306a36Sopenharmony_ci	server = tcon->ses->server;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (server->ops->new_lease_key)
57062306a36Sopenharmony_ci		server->ops->new_lease_key(&fid);
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, &oplock, &fid, &buf);
57362306a36Sopenharmony_ci	if (!rc && server->ops->close)
57462306a36Sopenharmony_ci		server->ops->close(xid, tcon, &fid);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	cifs_free_open_info(&buf);
57762306a36Sopenharmony_ci	cifs_put_tlink(tlink);
57862306a36Sopenharmony_ciout_free_xid:
57962306a36Sopenharmony_ci	free_xid(xid);
58062306a36Sopenharmony_ci	return rc;
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciint cifs_mknod(struct mnt_idmap *idmap, struct inode *inode,
58462306a36Sopenharmony_ci	       struct dentry *direntry, umode_t mode, dev_t device_number)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci	int rc = -EPERM;
58762306a36Sopenharmony_ci	unsigned int xid;
58862306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
58962306a36Sopenharmony_ci	struct tcon_link *tlink;
59062306a36Sopenharmony_ci	struct cifs_tcon *tcon;
59162306a36Sopenharmony_ci	const char *full_path;
59262306a36Sopenharmony_ci	void *page;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	if (!old_valid_dev(device_number))
59562306a36Sopenharmony_ci		return -EINVAL;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	cifs_sb = CIFS_SB(inode->i_sb);
59862306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
59962306a36Sopenharmony_ci		return -EIO;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
60262306a36Sopenharmony_ci	if (IS_ERR(tlink))
60362306a36Sopenharmony_ci		return PTR_ERR(tlink);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	page = alloc_dentry_path();
60662306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
60762306a36Sopenharmony_ci	xid = get_xid();
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
61062306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
61162306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
61262306a36Sopenharmony_ci		goto mknod_out;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	rc = tcon->ses->server->ops->make_node(xid, inode, direntry, tcon,
61662306a36Sopenharmony_ci					       full_path, mode,
61762306a36Sopenharmony_ci					       device_number);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cimknod_out:
62062306a36Sopenharmony_ci	free_dentry_path(page);
62162306a36Sopenharmony_ci	free_xid(xid);
62262306a36Sopenharmony_ci	cifs_put_tlink(tlink);
62362306a36Sopenharmony_ci	return rc;
62462306a36Sopenharmony_ci}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_cistruct dentry *
62762306a36Sopenharmony_cicifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
62862306a36Sopenharmony_ci	    unsigned int flags)
62962306a36Sopenharmony_ci{
63062306a36Sopenharmony_ci	unsigned int xid;
63162306a36Sopenharmony_ci	int rc = 0; /* to get around spurious gcc warning, set to zero here */
63262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
63362306a36Sopenharmony_ci	struct tcon_link *tlink;
63462306a36Sopenharmony_ci	struct cifs_tcon *pTcon;
63562306a36Sopenharmony_ci	struct inode *newInode = NULL;
63662306a36Sopenharmony_ci	const char *full_path;
63762306a36Sopenharmony_ci	void *page;
63862306a36Sopenharmony_ci	int retry_count = 0;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	xid = get_xid();
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n",
64362306a36Sopenharmony_ci		 parent_dir_inode, direntry, direntry);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	/* check whether path exists */
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
64862306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
64962306a36Sopenharmony_ci	if (IS_ERR(tlink)) {
65062306a36Sopenharmony_ci		free_xid(xid);
65162306a36Sopenharmony_ci		return ERR_CAST(tlink);
65262306a36Sopenharmony_ci	}
65362306a36Sopenharmony_ci	pTcon = tlink_tcon(tlink);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	rc = check_name(direntry, pTcon);
65662306a36Sopenharmony_ci	if (unlikely(rc)) {
65762306a36Sopenharmony_ci		cifs_put_tlink(tlink);
65862306a36Sopenharmony_ci		free_xid(xid);
65962306a36Sopenharmony_ci		return ERR_PTR(rc);
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	/* can not grab the rename sem here since it would
66362306a36Sopenharmony_ci	deadlock in the cases (beginning of sys_rename itself)
66462306a36Sopenharmony_ci	in which we already have the sb rename sem */
66562306a36Sopenharmony_ci	page = alloc_dentry_path();
66662306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
66762306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
66862306a36Sopenharmony_ci		cifs_put_tlink(tlink);
66962306a36Sopenharmony_ci		free_xid(xid);
67062306a36Sopenharmony_ci		free_dentry_path(page);
67162306a36Sopenharmony_ci		return ERR_CAST(full_path);
67262306a36Sopenharmony_ci	}
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (d_really_is_positive(direntry)) {
67562306a36Sopenharmony_ci		cifs_dbg(FYI, "non-NULL inode in lookup\n");
67662306a36Sopenharmony_ci	} else {
67762306a36Sopenharmony_ci		cifs_dbg(FYI, "NULL inode in lookup\n");
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	cifs_dbg(FYI, "Full path: %s inode = 0x%p\n",
68062306a36Sopenharmony_ci		 full_path, d_inode(direntry));
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ciagain:
68362306a36Sopenharmony_ci	if (pTcon->posix_extensions)
68462306a36Sopenharmony_ci		rc = smb311_posix_get_inode_info(&newInode, full_path, parent_dir_inode->i_sb, xid);
68562306a36Sopenharmony_ci	else if (pTcon->unix_ext) {
68662306a36Sopenharmony_ci		rc = cifs_get_inode_info_unix(&newInode, full_path,
68762306a36Sopenharmony_ci					      parent_dir_inode->i_sb, xid);
68862306a36Sopenharmony_ci	} else {
68962306a36Sopenharmony_ci		rc = cifs_get_inode_info(&newInode, full_path, NULL,
69062306a36Sopenharmony_ci				parent_dir_inode->i_sb, xid, NULL);
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	if (rc == 0) {
69462306a36Sopenharmony_ci		/* since paths are not looked up by component - the parent
69562306a36Sopenharmony_ci		   directories are presumed to be good here */
69662306a36Sopenharmony_ci		renew_parental_timestamps(direntry);
69762306a36Sopenharmony_ci	} else if (rc == -EAGAIN && retry_count++ < 10) {
69862306a36Sopenharmony_ci		goto again;
69962306a36Sopenharmony_ci	} else if (rc == -ENOENT) {
70062306a36Sopenharmony_ci		cifs_set_time(direntry, jiffies);
70162306a36Sopenharmony_ci		newInode = NULL;
70262306a36Sopenharmony_ci	} else {
70362306a36Sopenharmony_ci		if (rc != -EACCES) {
70462306a36Sopenharmony_ci			cifs_dbg(FYI, "Unexpected lookup error %d\n", rc);
70562306a36Sopenharmony_ci			/* We special case check for Access Denied - since that
70662306a36Sopenharmony_ci			is a common return code */
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci		newInode = ERR_PTR(rc);
70962306a36Sopenharmony_ci	}
71062306a36Sopenharmony_ci	free_dentry_path(page);
71162306a36Sopenharmony_ci	cifs_put_tlink(tlink);
71262306a36Sopenharmony_ci	free_xid(xid);
71362306a36Sopenharmony_ci	return d_splice_alias(newInode, direntry);
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_cistatic int
71762306a36Sopenharmony_cicifs_d_revalidate(struct dentry *direntry, unsigned int flags)
71862306a36Sopenharmony_ci{
71962306a36Sopenharmony_ci	struct inode *inode;
72062306a36Sopenharmony_ci	int rc;
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	if (flags & LOOKUP_RCU)
72362306a36Sopenharmony_ci		return -ECHILD;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	if (d_really_is_positive(direntry)) {
72662306a36Sopenharmony_ci		inode = d_inode(direntry);
72762306a36Sopenharmony_ci		if ((flags & LOOKUP_REVAL) && !CIFS_CACHE_READ(CIFS_I(inode)))
72862306a36Sopenharmony_ci			CIFS_I(inode)->time = 0; /* force reval */
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci		rc = cifs_revalidate_dentry(direntry);
73162306a36Sopenharmony_ci		if (rc) {
73262306a36Sopenharmony_ci			cifs_dbg(FYI, "cifs_revalidate_dentry failed with rc=%d", rc);
73362306a36Sopenharmony_ci			switch (rc) {
73462306a36Sopenharmony_ci			case -ENOENT:
73562306a36Sopenharmony_ci			case -ESTALE:
73662306a36Sopenharmony_ci				/*
73762306a36Sopenharmony_ci				 * Those errors mean the dentry is invalid
73862306a36Sopenharmony_ci				 * (file was deleted or recreated)
73962306a36Sopenharmony_ci				 */
74062306a36Sopenharmony_ci				return 0;
74162306a36Sopenharmony_ci			default:
74262306a36Sopenharmony_ci				/*
74362306a36Sopenharmony_ci				 * Otherwise some unexpected error happened
74462306a36Sopenharmony_ci				 * report it as-is to VFS layer
74562306a36Sopenharmony_ci				 */
74662306a36Sopenharmony_ci				return rc;
74762306a36Sopenharmony_ci			}
74862306a36Sopenharmony_ci		}
74962306a36Sopenharmony_ci		else {
75062306a36Sopenharmony_ci			/*
75162306a36Sopenharmony_ci			 * If the inode wasn't known to be a dfs entry when
75262306a36Sopenharmony_ci			 * the dentry was instantiated, such as when created
75362306a36Sopenharmony_ci			 * via ->readdir(), it needs to be set now since the
75462306a36Sopenharmony_ci			 * attributes will have been updated by
75562306a36Sopenharmony_ci			 * cifs_revalidate_dentry().
75662306a36Sopenharmony_ci			 */
75762306a36Sopenharmony_ci			if (IS_AUTOMOUNT(inode) &&
75862306a36Sopenharmony_ci			   !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) {
75962306a36Sopenharmony_ci				spin_lock(&direntry->d_lock);
76062306a36Sopenharmony_ci				direntry->d_flags |= DCACHE_NEED_AUTOMOUNT;
76162306a36Sopenharmony_ci				spin_unlock(&direntry->d_lock);
76262306a36Sopenharmony_ci			}
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci			return 1;
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	/*
76962306a36Sopenharmony_ci	 * This may be nfsd (or something), anyway, we can't see the
77062306a36Sopenharmony_ci	 * intent of this. So, since this can be for creation, drop it.
77162306a36Sopenharmony_ci	 */
77262306a36Sopenharmony_ci	if (!flags)
77362306a36Sopenharmony_ci		return 0;
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	/*
77662306a36Sopenharmony_ci	 * Drop the negative dentry, in order to make sure to use the
77762306a36Sopenharmony_ci	 * case sensitive name which is specified by user if this is
77862306a36Sopenharmony_ci	 * for creation.
77962306a36Sopenharmony_ci	 */
78062306a36Sopenharmony_ci	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
78162306a36Sopenharmony_ci		return 0;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	if (time_after(jiffies, cifs_get_time(direntry) + HZ) || !lookupCacheEnabled)
78462306a36Sopenharmony_ci		return 0;
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_ci	return 1;
78762306a36Sopenharmony_ci}
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci/* static int cifs_d_delete(struct dentry *direntry)
79062306a36Sopenharmony_ci{
79162306a36Sopenharmony_ci	int rc = 0;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	cifs_dbg(FYI, "In cifs d_delete, name = %pd\n", direntry);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	return rc;
79662306a36Sopenharmony_ci}     */
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ciconst struct dentry_operations cifs_dentry_ops = {
79962306a36Sopenharmony_ci	.d_revalidate = cifs_d_revalidate,
80062306a36Sopenharmony_ci	.d_automount = cifs_d_automount,
80162306a36Sopenharmony_ci/* d_delete:       cifs_d_delete,      */ /* not needed except for debugging */
80262306a36Sopenharmony_ci};
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int cifs_ci_hash(const struct dentry *dentry, struct qstr *q)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
80762306a36Sopenharmony_ci	unsigned long hash;
80862306a36Sopenharmony_ci	wchar_t c;
80962306a36Sopenharmony_ci	int i, charlen;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	hash = init_name_hash(dentry);
81262306a36Sopenharmony_ci	for (i = 0; i < q->len; i += charlen) {
81362306a36Sopenharmony_ci		charlen = codepage->char2uni(&q->name[i], q->len - i, &c);
81462306a36Sopenharmony_ci		/* error out if we can't convert the character */
81562306a36Sopenharmony_ci		if (unlikely(charlen < 0))
81662306a36Sopenharmony_ci			return charlen;
81762306a36Sopenharmony_ci		hash = partial_name_hash(cifs_toupper(c), hash);
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci	q->hash = end_name_hash(hash);
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci	return 0;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_cistatic int cifs_ci_compare(const struct dentry *dentry,
82562306a36Sopenharmony_ci		unsigned int len, const char *str, const struct qstr *name)
82662306a36Sopenharmony_ci{
82762306a36Sopenharmony_ci	struct nls_table *codepage = CIFS_SB(dentry->d_sb)->local_nls;
82862306a36Sopenharmony_ci	wchar_t c1, c2;
82962306a36Sopenharmony_ci	int i, l1, l2;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	/*
83262306a36Sopenharmony_ci	 * We make the assumption here that uppercase characters in the local
83362306a36Sopenharmony_ci	 * codepage are always the same length as their lowercase counterparts.
83462306a36Sopenharmony_ci	 *
83562306a36Sopenharmony_ci	 * If that's ever not the case, then this will fail to match it.
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	if (name->len != len)
83862306a36Sopenharmony_ci		return 1;
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	for (i = 0; i < len; i += l1) {
84162306a36Sopenharmony_ci		/* Convert characters in both strings to UTF-16. */
84262306a36Sopenharmony_ci		l1 = codepage->char2uni(&str[i], len - i, &c1);
84362306a36Sopenharmony_ci		l2 = codepage->char2uni(&name->name[i], name->len - i, &c2);
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci		/*
84662306a36Sopenharmony_ci		 * If we can't convert either character, just declare it to
84762306a36Sopenharmony_ci		 * be 1 byte long and compare the original byte.
84862306a36Sopenharmony_ci		 */
84962306a36Sopenharmony_ci		if (unlikely(l1 < 0 && l2 < 0)) {
85062306a36Sopenharmony_ci			if (str[i] != name->name[i])
85162306a36Sopenharmony_ci				return 1;
85262306a36Sopenharmony_ci			l1 = 1;
85362306a36Sopenharmony_ci			continue;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		/*
85762306a36Sopenharmony_ci		 * Here, we again ass|u|me that upper/lowercase versions of
85862306a36Sopenharmony_ci		 * a character are the same length in the local NLS.
85962306a36Sopenharmony_ci		 */
86062306a36Sopenharmony_ci		if (l1 != l2)
86162306a36Sopenharmony_ci			return 1;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci		/* Now compare uppercase versions of these characters */
86462306a36Sopenharmony_ci		if (cifs_toupper(c1) != cifs_toupper(c2))
86562306a36Sopenharmony_ci			return 1;
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	return 0;
86962306a36Sopenharmony_ci}
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ciconst struct dentry_operations cifs_ci_dentry_ops = {
87262306a36Sopenharmony_ci	.d_revalidate = cifs_d_revalidate,
87362306a36Sopenharmony_ci	.d_hash = cifs_ci_hash,
87462306a36Sopenharmony_ci	.d_compare = cifs_ci_compare,
87562306a36Sopenharmony_ci	.d_automount = cifs_d_automount,
87662306a36Sopenharmony_ci};
877