162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2010
562306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/stat.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/pagemap.h>
1262306a36Sopenharmony_ci#include <linux/freezer.h>
1362306a36Sopenharmony_ci#include <linux/sched/signal.h>
1462306a36Sopenharmony_ci#include <linux/wait_bit.h>
1562306a36Sopenharmony_ci#include <linux/fiemap.h>
1662306a36Sopenharmony_ci#include <asm/div64.h>
1762306a36Sopenharmony_ci#include "cifsfs.h"
1862306a36Sopenharmony_ci#include "cifspdu.h"
1962306a36Sopenharmony_ci#include "cifsglob.h"
2062306a36Sopenharmony_ci#include "cifsproto.h"
2162306a36Sopenharmony_ci#include "smb2proto.h"
2262306a36Sopenharmony_ci#include "cifs_debug.h"
2362306a36Sopenharmony_ci#include "cifs_fs_sb.h"
2462306a36Sopenharmony_ci#include "cifs_unicode.h"
2562306a36Sopenharmony_ci#include "fscache.h"
2662306a36Sopenharmony_ci#include "fs_context.h"
2762306a36Sopenharmony_ci#include "cifs_ioctl.h"
2862306a36Sopenharmony_ci#include "cached_dir.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic void cifs_set_ops(struct inode *inode)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	switch (inode->i_mode & S_IFMT) {
3562306a36Sopenharmony_ci	case S_IFREG:
3662306a36Sopenharmony_ci		inode->i_op = &cifs_file_inode_ops;
3762306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) {
3862306a36Sopenharmony_ci			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
3962306a36Sopenharmony_ci				inode->i_fop = &cifs_file_direct_nobrl_ops;
4062306a36Sopenharmony_ci			else
4162306a36Sopenharmony_ci				inode->i_fop = &cifs_file_direct_ops;
4262306a36Sopenharmony_ci		} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) {
4362306a36Sopenharmony_ci			if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
4462306a36Sopenharmony_ci				inode->i_fop = &cifs_file_strict_nobrl_ops;
4562306a36Sopenharmony_ci			else
4662306a36Sopenharmony_ci				inode->i_fop = &cifs_file_strict_ops;
4762306a36Sopenharmony_ci		} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL)
4862306a36Sopenharmony_ci			inode->i_fop = &cifs_file_nobrl_ops;
4962306a36Sopenharmony_ci		else { /* not direct, send byte range locks */
5062306a36Sopenharmony_ci			inode->i_fop = &cifs_file_ops;
5162306a36Sopenharmony_ci		}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci		/* check if server can support readahead */
5462306a36Sopenharmony_ci		if (cifs_sb_master_tcon(cifs_sb)->ses->server->max_read <
5562306a36Sopenharmony_ci				PAGE_SIZE + MAX_CIFS_HDR_SIZE)
5662306a36Sopenharmony_ci			inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
5762306a36Sopenharmony_ci		else
5862306a36Sopenharmony_ci			inode->i_data.a_ops = &cifs_addr_ops;
5962306a36Sopenharmony_ci		break;
6062306a36Sopenharmony_ci	case S_IFDIR:
6162306a36Sopenharmony_ci		if (IS_AUTOMOUNT(inode)) {
6262306a36Sopenharmony_ci			inode->i_op = &cifs_namespace_inode_operations;
6362306a36Sopenharmony_ci		} else {
6462306a36Sopenharmony_ci			inode->i_op = &cifs_dir_inode_ops;
6562306a36Sopenharmony_ci			inode->i_fop = &cifs_dir_ops;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci		break;
6862306a36Sopenharmony_ci	case S_IFLNK:
6962306a36Sopenharmony_ci		inode->i_op = &cifs_symlink_inode_ops;
7062306a36Sopenharmony_ci		break;
7162306a36Sopenharmony_ci	default:
7262306a36Sopenharmony_ci		init_special_inode(inode, inode->i_mode, inode->i_rdev);
7362306a36Sopenharmony_ci		break;
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/* check inode attributes against fattr. If they don't match, tag the
7862306a36Sopenharmony_ci * inode for cache invalidation
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic void
8162306a36Sopenharmony_cicifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct cifs_fscache_inode_coherency_data cd;
8462306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
8562306a36Sopenharmony_ci	struct timespec64 mtime;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: revalidating inode %llu\n",
8862306a36Sopenharmony_ci		 __func__, cifs_i->uniqueid);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (inode->i_state & I_NEW) {
9162306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: inode %llu is new\n",
9262306a36Sopenharmony_ci			 __func__, cifs_i->uniqueid);
9362306a36Sopenharmony_ci		return;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* don't bother with revalidation if we have an oplock */
9762306a36Sopenharmony_ci	if (CIFS_CACHE_READ(cifs_i)) {
9862306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: inode %llu is oplocked\n",
9962306a36Sopenharmony_ci			 __func__, cifs_i->uniqueid);
10062306a36Sopenharmony_ci		return;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	 /* revalidate if mtime or size have changed */
10462306a36Sopenharmony_ci	fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
10562306a36Sopenharmony_ci	mtime = inode_get_mtime(inode);
10662306a36Sopenharmony_ci	if (timespec64_equal(&mtime, &fattr->cf_mtime) &&
10762306a36Sopenharmony_ci	    cifs_i->server_eof == fattr->cf_eof) {
10862306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: inode %llu is unchanged\n",
10962306a36Sopenharmony_ci			 __func__, cifs_i->uniqueid);
11062306a36Sopenharmony_ci		return;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n",
11462306a36Sopenharmony_ci		 __func__, cifs_i->uniqueid);
11562306a36Sopenharmony_ci	set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags);
11662306a36Sopenharmony_ci	/* Invalidate fscache cookie */
11762306a36Sopenharmony_ci	cifs_fscache_fill_coherency(&cifs_i->netfs.inode, &cd);
11862306a36Sopenharmony_ci	fscache_invalidate(cifs_inode_cookie(inode), &cd, i_size_read(inode), 0);
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/*
12262306a36Sopenharmony_ci * copy nlink to the inode, unless it wasn't provided.  Provide
12362306a36Sopenharmony_ci * sane values if we don't have an existing one and none was provided
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic void
12662306a36Sopenharmony_cicifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	/*
12962306a36Sopenharmony_ci	 * if we're in a situation where we can't trust what we
13062306a36Sopenharmony_ci	 * got from the server (readdir, some non-unix cases)
13162306a36Sopenharmony_ci	 * fake reasonable values
13262306a36Sopenharmony_ci	 */
13362306a36Sopenharmony_ci	if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) {
13462306a36Sopenharmony_ci		/* only provide fake values on a new inode */
13562306a36Sopenharmony_ci		if (inode->i_state & I_NEW) {
13662306a36Sopenharmony_ci			if (fattr->cf_cifsattrs & ATTR_DIRECTORY)
13762306a36Sopenharmony_ci				set_nlink(inode, 2);
13862306a36Sopenharmony_ci			else
13962306a36Sopenharmony_ci				set_nlink(inode, 1);
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci		return;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	/* we trust the server, so update it */
14562306a36Sopenharmony_ci	set_nlink(inode, fattr->cf_nlink);
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* populate an inode with info from a cifs_fattr struct */
14962306a36Sopenharmony_ciint
15062306a36Sopenharmony_cicifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr,
15162306a36Sopenharmony_ci		    bool from_readdir)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
15462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!(inode->i_state & I_NEW) &&
15762306a36Sopenharmony_ci	    unlikely(inode_wrong_type(inode, fattr->cf_mode))) {
15862306a36Sopenharmony_ci		CIFS_I(inode)->time = 0; /* force reval */
15962306a36Sopenharmony_ci		return -ESTALE;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	cifs_revalidate_cache(inode, fattr);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
16562306a36Sopenharmony_ci	fattr->cf_mtime = timestamp_truncate(fattr->cf_mtime, inode);
16662306a36Sopenharmony_ci	fattr->cf_atime = timestamp_truncate(fattr->cf_atime, inode);
16762306a36Sopenharmony_ci	fattr->cf_ctime = timestamp_truncate(fattr->cf_ctime, inode);
16862306a36Sopenharmony_ci	/* we do not want atime to be less than mtime, it broke some apps */
16962306a36Sopenharmony_ci	if (timespec64_compare(&fattr->cf_atime, &fattr->cf_mtime) < 0)
17062306a36Sopenharmony_ci		inode_set_atime_to_ts(inode, fattr->cf_mtime);
17162306a36Sopenharmony_ci	else
17262306a36Sopenharmony_ci		inode_set_atime_to_ts(inode, fattr->cf_atime);
17362306a36Sopenharmony_ci	inode_set_mtime_to_ts(inode, fattr->cf_mtime);
17462306a36Sopenharmony_ci	inode_set_ctime_to_ts(inode, fattr->cf_ctime);
17562306a36Sopenharmony_ci	inode->i_rdev = fattr->cf_rdev;
17662306a36Sopenharmony_ci	cifs_nlink_fattr_to_inode(inode, fattr);
17762306a36Sopenharmony_ci	inode->i_uid = fattr->cf_uid;
17862306a36Sopenharmony_ci	inode->i_gid = fattr->cf_gid;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* if dynperm is set, don't clobber existing mode */
18162306a36Sopenharmony_ci	if (inode->i_state & I_NEW ||
18262306a36Sopenharmony_ci	    !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM))
18362306a36Sopenharmony_ci		inode->i_mode = fattr->cf_mode;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	cifs_i->cifsAttrs = fattr->cf_cifsattrs;
18662306a36Sopenharmony_ci	cifs_i->reparse_tag = fattr->cf_cifstag;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	if (fattr->cf_flags & CIFS_FATTR_NEED_REVAL)
18962306a36Sopenharmony_ci		cifs_i->time = 0;
19062306a36Sopenharmony_ci	else
19162306a36Sopenharmony_ci		cifs_i->time = jiffies;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING)
19462306a36Sopenharmony_ci		set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
19562306a36Sopenharmony_ci	else
19662306a36Sopenharmony_ci		clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	cifs_i->server_eof = fattr->cf_eof;
19962306a36Sopenharmony_ci	/*
20062306a36Sopenharmony_ci	 * Can't safely change the file size here if the client is writing to
20162306a36Sopenharmony_ci	 * it due to potential races.
20262306a36Sopenharmony_ci	 */
20362306a36Sopenharmony_ci	if (is_size_safe_to_change(cifs_i, fattr->cf_eof, from_readdir)) {
20462306a36Sopenharmony_ci		i_size_write(inode, fattr->cf_eof);
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci		/*
20762306a36Sopenharmony_ci		 * i_blocks is not related to (i_size / i_blksize),
20862306a36Sopenharmony_ci		 * but instead 512 byte (2**9) size is required for
20962306a36Sopenharmony_ci		 * calculating num blocks.
21062306a36Sopenharmony_ci		 */
21162306a36Sopenharmony_ci		inode->i_blocks = (512 - 1 + fattr->cf_bytes) >> 9;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (S_ISLNK(fattr->cf_mode) && fattr->cf_symlink_target) {
21562306a36Sopenharmony_ci		kfree(cifs_i->symlink_target);
21662306a36Sopenharmony_ci		cifs_i->symlink_target = fattr->cf_symlink_target;
21762306a36Sopenharmony_ci		fattr->cf_symlink_target = NULL;
21862306a36Sopenharmony_ci	}
21962306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (fattr->cf_flags & CIFS_FATTR_JUNCTION)
22262306a36Sopenharmony_ci		inode->i_flags |= S_AUTOMOUNT;
22362306a36Sopenharmony_ci	if (inode->i_state & I_NEW)
22462306a36Sopenharmony_ci		cifs_set_ops(inode);
22562306a36Sopenharmony_ci	return 0;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_civoid
22962306a36Sopenharmony_cicifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
23462306a36Sopenharmony_ci		return;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	fattr->cf_uniqueid = iunique(sb, ROOT_I);
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* Fill a cifs_fattr struct with info from FILE_UNIX_BASIC_INFO. */
24062306a36Sopenharmony_civoid
24162306a36Sopenharmony_cicifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
24262306a36Sopenharmony_ci			 struct cifs_sb_info *cifs_sb)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
24562306a36Sopenharmony_ci	fattr->cf_uniqueid = le64_to_cpu(info->UniqueId);
24662306a36Sopenharmony_ci	fattr->cf_bytes = le64_to_cpu(info->NumOfBytes);
24762306a36Sopenharmony_ci	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
25062306a36Sopenharmony_ci	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastModificationTime);
25162306a36Sopenharmony_ci	fattr->cf_ctime = cifs_NTtimeToUnix(info->LastStatusChange);
25262306a36Sopenharmony_ci	/* old POSIX extensions don't get create time */
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	fattr->cf_mode = le64_to_cpu(info->Permissions);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/*
25762306a36Sopenharmony_ci	 * Since we set the inode type below we need to mask off
25862306a36Sopenharmony_ci	 * to avoid strange results if bits set above.
25962306a36Sopenharmony_ci	 */
26062306a36Sopenharmony_ci	fattr->cf_mode &= ~S_IFMT;
26162306a36Sopenharmony_ci	switch (le32_to_cpu(info->Type)) {
26262306a36Sopenharmony_ci	case UNIX_FILE:
26362306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG;
26462306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci	case UNIX_SYMLINK:
26762306a36Sopenharmony_ci		fattr->cf_mode |= S_IFLNK;
26862306a36Sopenharmony_ci		fattr->cf_dtype = DT_LNK;
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case UNIX_DIR:
27162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFDIR;
27262306a36Sopenharmony_ci		fattr->cf_dtype = DT_DIR;
27362306a36Sopenharmony_ci		break;
27462306a36Sopenharmony_ci	case UNIX_CHARDEV:
27562306a36Sopenharmony_ci		fattr->cf_mode |= S_IFCHR;
27662306a36Sopenharmony_ci		fattr->cf_dtype = DT_CHR;
27762306a36Sopenharmony_ci		fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
27862306a36Sopenharmony_ci				       le64_to_cpu(info->DevMinor) & MINORMASK);
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	case UNIX_BLOCKDEV:
28162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFBLK;
28262306a36Sopenharmony_ci		fattr->cf_dtype = DT_BLK;
28362306a36Sopenharmony_ci		fattr->cf_rdev = MKDEV(le64_to_cpu(info->DevMajor),
28462306a36Sopenharmony_ci				       le64_to_cpu(info->DevMinor) & MINORMASK);
28562306a36Sopenharmony_ci		break;
28662306a36Sopenharmony_ci	case UNIX_FIFO:
28762306a36Sopenharmony_ci		fattr->cf_mode |= S_IFIFO;
28862306a36Sopenharmony_ci		fattr->cf_dtype = DT_FIFO;
28962306a36Sopenharmony_ci		break;
29062306a36Sopenharmony_ci	case UNIX_SOCKET:
29162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFSOCK;
29262306a36Sopenharmony_ci		fattr->cf_dtype = DT_SOCK;
29362306a36Sopenharmony_ci		break;
29462306a36Sopenharmony_ci	default:
29562306a36Sopenharmony_ci		/* safest to call it a file if we do not know */
29662306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG;
29762306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
29862306a36Sopenharmony_ci		cifs_dbg(FYI, "unknown type %d\n", le32_to_cpu(info->Type));
29962306a36Sopenharmony_ci		break;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	fattr->cf_uid = cifs_sb->ctx->linux_uid;
30362306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)) {
30462306a36Sopenharmony_ci		u64 id = le64_to_cpu(info->Uid);
30562306a36Sopenharmony_ci		if (id < ((uid_t)-1)) {
30662306a36Sopenharmony_ci			kuid_t uid = make_kuid(&init_user_ns, id);
30762306a36Sopenharmony_ci			if (uid_valid(uid))
30862306a36Sopenharmony_ci				fattr->cf_uid = uid;
30962306a36Sopenharmony_ci		}
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	fattr->cf_gid = cifs_sb->ctx->linux_gid;
31362306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID)) {
31462306a36Sopenharmony_ci		u64 id = le64_to_cpu(info->Gid);
31562306a36Sopenharmony_ci		if (id < ((gid_t)-1)) {
31662306a36Sopenharmony_ci			kgid_t gid = make_kgid(&init_user_ns, id);
31762306a36Sopenharmony_ci			if (gid_valid(gid))
31862306a36Sopenharmony_ci				fattr->cf_gid = gid;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	fattr->cf_nlink = le64_to_cpu(info->Nlinks);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * Fill a cifs_fattr struct with fake inode info.
32762306a36Sopenharmony_ci *
32862306a36Sopenharmony_ci * Needed to setup cifs_fattr data for the directory which is the
32962306a36Sopenharmony_ci * junction to the new submount (ie to setup the fake directory
33062306a36Sopenharmony_ci * which represents a DFS referral or reparse mount point).
33162306a36Sopenharmony_ci */
33262306a36Sopenharmony_cistatic void cifs_create_junction_fattr(struct cifs_fattr *fattr,
33362306a36Sopenharmony_ci				       struct super_block *sb)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: creating fake fattr\n", __func__);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
34062306a36Sopenharmony_ci	fattr->cf_mode = S_IFDIR | S_IXUGO | S_IRWXU;
34162306a36Sopenharmony_ci	fattr->cf_uid = cifs_sb->ctx->linux_uid;
34262306a36Sopenharmony_ci	fattr->cf_gid = cifs_sb->ctx->linux_gid;
34362306a36Sopenharmony_ci	ktime_get_coarse_real_ts64(&fattr->cf_mtime);
34462306a36Sopenharmony_ci	fattr->cf_atime = fattr->cf_ctime = fattr->cf_mtime;
34562306a36Sopenharmony_ci	fattr->cf_nlink = 2;
34662306a36Sopenharmony_ci	fattr->cf_flags = CIFS_FATTR_JUNCTION;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/* Update inode with final fattr data */
35062306a36Sopenharmony_cistatic int update_inode_info(struct super_block *sb,
35162306a36Sopenharmony_ci			     struct cifs_fattr *fattr,
35262306a36Sopenharmony_ci			     struct inode **inode)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
35562306a36Sopenharmony_ci	int rc = 0;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	if (!*inode) {
35862306a36Sopenharmony_ci		*inode = cifs_iget(sb, fattr);
35962306a36Sopenharmony_ci		if (!*inode)
36062306a36Sopenharmony_ci			rc = -ENOMEM;
36162306a36Sopenharmony_ci		return rc;
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	/* We already have inode, update it.
36462306a36Sopenharmony_ci	 *
36562306a36Sopenharmony_ci	 * If file type or uniqueid is different, return error.
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	if (unlikely((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
36862306a36Sopenharmony_ci		     CIFS_I(*inode)->uniqueid != fattr->cf_uniqueid)) {
36962306a36Sopenharmony_ci		CIFS_I(*inode)->time = 0; /* force reval */
37062306a36Sopenharmony_ci		return -ESTALE;
37162306a36Sopenharmony_ci	}
37262306a36Sopenharmony_ci	return cifs_fattr_to_inode(*inode, fattr, false);
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
37662306a36Sopenharmony_cistatic int
37762306a36Sopenharmony_cicifs_get_file_info_unix(struct file *filp)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	int rc;
38062306a36Sopenharmony_ci	unsigned int xid;
38162306a36Sopenharmony_ci	FILE_UNIX_BASIC_INFO find_data;
38262306a36Sopenharmony_ci	struct cifs_fattr fattr = {};
38362306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
38462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
38562306a36Sopenharmony_ci	struct cifsFileInfo *cfile = filp->private_data;
38662306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	xid = get_xid();
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (cfile->symlink_target) {
39162306a36Sopenharmony_ci		fattr.cf_symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
39262306a36Sopenharmony_ci		if (!fattr.cf_symlink_target) {
39362306a36Sopenharmony_ci			rc = -ENOMEM;
39462306a36Sopenharmony_ci			goto cifs_gfiunix_out;
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
39962306a36Sopenharmony_ci	if (!rc) {
40062306a36Sopenharmony_ci		cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
40162306a36Sopenharmony_ci	} else if (rc == -EREMOTE) {
40262306a36Sopenharmony_ci		cifs_create_junction_fattr(&fattr, inode->i_sb);
40362306a36Sopenharmony_ci		rc = 0;
40462306a36Sopenharmony_ci	} else
40562306a36Sopenharmony_ci		goto cifs_gfiunix_out;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	rc = cifs_fattr_to_inode(inode, &fattr, false);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_cicifs_gfiunix_out:
41062306a36Sopenharmony_ci	free_xid(xid);
41162306a36Sopenharmony_ci	return rc;
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic int cifs_get_unix_fattr(const unsigned char *full_path,
41562306a36Sopenharmony_ci			       struct super_block *sb,
41662306a36Sopenharmony_ci			       struct cifs_fattr *fattr,
41762306a36Sopenharmony_ci			       struct inode **pinode,
41862306a36Sopenharmony_ci			       const unsigned int xid)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct TCP_Server_Info *server;
42162306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
42262306a36Sopenharmony_ci	FILE_UNIX_BASIC_INFO find_data;
42362306a36Sopenharmony_ci	struct cifs_tcon *tcon;
42462306a36Sopenharmony_ci	struct tcon_link *tlink;
42562306a36Sopenharmony_ci	int rc, tmprc;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	cifs_dbg(FYI, "Getting info on %s\n", full_path);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
43062306a36Sopenharmony_ci	if (IS_ERR(tlink))
43162306a36Sopenharmony_ci		return PTR_ERR(tlink);
43262306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
43362306a36Sopenharmony_ci	server = tcon->ses->server;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* could have done a find first instead but this returns more info */
43662306a36Sopenharmony_ci	rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
43762306a36Sopenharmony_ci				  cifs_sb->local_nls, cifs_remap(cifs_sb));
43862306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: query path info: rc = %d\n", __func__, rc);
43962306a36Sopenharmony_ci	cifs_put_tlink(tlink);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	if (!rc) {
44262306a36Sopenharmony_ci		cifs_unix_basic_to_fattr(fattr, &find_data, cifs_sb);
44362306a36Sopenharmony_ci	} else if (rc == -EREMOTE) {
44462306a36Sopenharmony_ci		cifs_create_junction_fattr(fattr, sb);
44562306a36Sopenharmony_ci		rc = 0;
44662306a36Sopenharmony_ci	} else {
44762306a36Sopenharmony_ci		return rc;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (!*pinode)
45162306a36Sopenharmony_ci		cifs_fill_uniqueid(sb, fattr);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	/* check for Minshall+French symlinks */
45462306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
45562306a36Sopenharmony_ci		tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
45662306a36Sopenharmony_ci		cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (S_ISLNK(fattr->cf_mode) && !fattr->cf_symlink_target) {
46062306a36Sopenharmony_ci		if (!server->ops->query_symlink)
46162306a36Sopenharmony_ci			return -EOPNOTSUPP;
46262306a36Sopenharmony_ci		rc = server->ops->query_symlink(xid, tcon,
46362306a36Sopenharmony_ci						cifs_sb, full_path,
46462306a36Sopenharmony_ci						&fattr->cf_symlink_target);
46562306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci	return rc;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciint cifs_get_inode_info_unix(struct inode **pinode,
47162306a36Sopenharmony_ci			     const unsigned char *full_path,
47262306a36Sopenharmony_ci			     struct super_block *sb, unsigned int xid)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct cifs_fattr fattr = {};
47562306a36Sopenharmony_ci	int rc;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	rc = cifs_get_unix_fattr(full_path, sb, &fattr, pinode, xid);
47862306a36Sopenharmony_ci	if (rc)
47962306a36Sopenharmony_ci		goto out;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	rc = update_inode_info(sb, &fattr, pinode);
48262306a36Sopenharmony_ciout:
48362306a36Sopenharmony_ci	kfree(fattr.cf_symlink_target);
48462306a36Sopenharmony_ci	return rc;
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci#else
48762306a36Sopenharmony_cistatic inline int cifs_get_unix_fattr(const unsigned char *full_path,
48862306a36Sopenharmony_ci				      struct super_block *sb,
48962306a36Sopenharmony_ci				      struct cifs_fattr *fattr,
49062306a36Sopenharmony_ci				      struct inode **pinode,
49162306a36Sopenharmony_ci				      const unsigned int xid)
49262306a36Sopenharmony_ci{
49362306a36Sopenharmony_ci	return -EOPNOTSUPP;
49462306a36Sopenharmony_ci}
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ciint cifs_get_inode_info_unix(struct inode **pinode,
49762306a36Sopenharmony_ci			     const unsigned char *full_path,
49862306a36Sopenharmony_ci			     struct super_block *sb, unsigned int xid)
49962306a36Sopenharmony_ci{
50062306a36Sopenharmony_ci	return -EOPNOTSUPP;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic int
50562306a36Sopenharmony_cicifs_sfu_type(struct cifs_fattr *fattr, const char *path,
50662306a36Sopenharmony_ci	      struct cifs_sb_info *cifs_sb, unsigned int xid)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	int rc;
50962306a36Sopenharmony_ci	__u32 oplock;
51062306a36Sopenharmony_ci	struct tcon_link *tlink;
51162306a36Sopenharmony_ci	struct cifs_tcon *tcon;
51262306a36Sopenharmony_ci	struct cifs_fid fid;
51362306a36Sopenharmony_ci	struct cifs_open_parms oparms;
51462306a36Sopenharmony_ci	struct cifs_io_parms io_parms = {0};
51562306a36Sopenharmony_ci	char buf[24];
51662306a36Sopenharmony_ci	unsigned int bytes_read;
51762306a36Sopenharmony_ci	char *pbuf;
51862306a36Sopenharmony_ci	int buf_type = CIFS_NO_BUFFER;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	pbuf = buf;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	fattr->cf_mode &= ~S_IFMT;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (fattr->cf_eof == 0) {
52562306a36Sopenharmony_ci		fattr->cf_mode |= S_IFIFO;
52662306a36Sopenharmony_ci		fattr->cf_dtype = DT_FIFO;
52762306a36Sopenharmony_ci		return 0;
52862306a36Sopenharmony_ci	} else if (fattr->cf_eof < 8) {
52962306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG;
53062306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
53162306a36Sopenharmony_ci		return -EINVAL;	 /* EOPNOTSUPP? */
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
53562306a36Sopenharmony_ci	if (IS_ERR(tlink))
53662306a36Sopenharmony_ci		return PTR_ERR(tlink);
53762306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	oparms = (struct cifs_open_parms) {
54062306a36Sopenharmony_ci		.tcon = tcon,
54162306a36Sopenharmony_ci		.cifs_sb = cifs_sb,
54262306a36Sopenharmony_ci		.desired_access = GENERIC_READ,
54362306a36Sopenharmony_ci		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
54462306a36Sopenharmony_ci		.disposition = FILE_OPEN,
54562306a36Sopenharmony_ci		.path = path,
54662306a36Sopenharmony_ci		.fid = &fid,
54762306a36Sopenharmony_ci	};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (tcon->ses->server->oplocks)
55062306a36Sopenharmony_ci		oplock = REQ_OPLOCK;
55162306a36Sopenharmony_ci	else
55262306a36Sopenharmony_ci		oplock = 0;
55362306a36Sopenharmony_ci	rc = tcon->ses->server->ops->open(xid, &oparms, &oplock, NULL);
55462306a36Sopenharmony_ci	if (rc) {
55562306a36Sopenharmony_ci		cifs_dbg(FYI, "check sfu type of %s, open rc = %d\n", path, rc);
55662306a36Sopenharmony_ci		cifs_put_tlink(tlink);
55762306a36Sopenharmony_ci		return rc;
55862306a36Sopenharmony_ci	}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	/* Read header */
56162306a36Sopenharmony_ci	io_parms.netfid = fid.netfid;
56262306a36Sopenharmony_ci	io_parms.pid = current->tgid;
56362306a36Sopenharmony_ci	io_parms.tcon = tcon;
56462306a36Sopenharmony_ci	io_parms.offset = 0;
56562306a36Sopenharmony_ci	io_parms.length = 24;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	rc = tcon->ses->server->ops->sync_read(xid, &fid, &io_parms,
56862306a36Sopenharmony_ci					&bytes_read, &pbuf, &buf_type);
56962306a36Sopenharmony_ci	if ((rc == 0) && (bytes_read >= 8)) {
57062306a36Sopenharmony_ci		if (memcmp("IntxBLK", pbuf, 8) == 0) {
57162306a36Sopenharmony_ci			cifs_dbg(FYI, "Block device\n");
57262306a36Sopenharmony_ci			fattr->cf_mode |= S_IFBLK;
57362306a36Sopenharmony_ci			fattr->cf_dtype = DT_BLK;
57462306a36Sopenharmony_ci			if (bytes_read == 24) {
57562306a36Sopenharmony_ci				/* we have enough to decode dev num */
57662306a36Sopenharmony_ci				__u64 mjr; /* major */
57762306a36Sopenharmony_ci				__u64 mnr; /* minor */
57862306a36Sopenharmony_ci				mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
57962306a36Sopenharmony_ci				mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
58062306a36Sopenharmony_ci				fattr->cf_rdev = MKDEV(mjr, mnr);
58162306a36Sopenharmony_ci			}
58262306a36Sopenharmony_ci		} else if (memcmp("IntxCHR", pbuf, 8) == 0) {
58362306a36Sopenharmony_ci			cifs_dbg(FYI, "Char device\n");
58462306a36Sopenharmony_ci			fattr->cf_mode |= S_IFCHR;
58562306a36Sopenharmony_ci			fattr->cf_dtype = DT_CHR;
58662306a36Sopenharmony_ci			if (bytes_read == 24) {
58762306a36Sopenharmony_ci				/* we have enough to decode dev num */
58862306a36Sopenharmony_ci				__u64 mjr; /* major */
58962306a36Sopenharmony_ci				__u64 mnr; /* minor */
59062306a36Sopenharmony_ci				mjr = le64_to_cpu(*(__le64 *)(pbuf+8));
59162306a36Sopenharmony_ci				mnr = le64_to_cpu(*(__le64 *)(pbuf+16));
59262306a36Sopenharmony_ci				fattr->cf_rdev = MKDEV(mjr, mnr);
59362306a36Sopenharmony_ci			}
59462306a36Sopenharmony_ci		} else if (memcmp("IntxLNK", pbuf, 7) == 0) {
59562306a36Sopenharmony_ci			cifs_dbg(FYI, "Symlink\n");
59662306a36Sopenharmony_ci			fattr->cf_mode |= S_IFLNK;
59762306a36Sopenharmony_ci			fattr->cf_dtype = DT_LNK;
59862306a36Sopenharmony_ci		} else if (memcmp("LnxFIFO", pbuf, 8) == 0) {
59962306a36Sopenharmony_ci			cifs_dbg(FYI, "FIFO\n");
60062306a36Sopenharmony_ci			fattr->cf_mode |= S_IFIFO;
60162306a36Sopenharmony_ci			fattr->cf_dtype = DT_FIFO;
60262306a36Sopenharmony_ci		} else {
60362306a36Sopenharmony_ci			fattr->cf_mode |= S_IFREG; /* file? */
60462306a36Sopenharmony_ci			fattr->cf_dtype = DT_REG;
60562306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
60662306a36Sopenharmony_ci		}
60762306a36Sopenharmony_ci	} else {
60862306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG; /* then it is a file */
60962306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
61062306a36Sopenharmony_ci		rc = -EOPNOTSUPP; /* or some unknown SFU type */
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	tcon->ses->server->ops->close(xid, tcon, &fid);
61462306a36Sopenharmony_ci	cifs_put_tlink(tlink);
61562306a36Sopenharmony_ci	return rc;
61662306a36Sopenharmony_ci}
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci#define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID)  /* SETFILEBITS valid bits */
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci/*
62162306a36Sopenharmony_ci * Fetch mode bits as provided by SFU.
62262306a36Sopenharmony_ci *
62362306a36Sopenharmony_ci * FIXME: Doesn't this clobber the type bit we got from cifs_sfu_type ?
62462306a36Sopenharmony_ci */
62562306a36Sopenharmony_cistatic int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
62662306a36Sopenharmony_ci			 struct cifs_sb_info *cifs_sb, unsigned int xid)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_XATTR
62962306a36Sopenharmony_ci	ssize_t rc;
63062306a36Sopenharmony_ci	char ea_value[4];
63162306a36Sopenharmony_ci	__u32 mode;
63262306a36Sopenharmony_ci	struct tcon_link *tlink;
63362306a36Sopenharmony_ci	struct cifs_tcon *tcon;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
63662306a36Sopenharmony_ci	if (IS_ERR(tlink))
63762306a36Sopenharmony_ci		return PTR_ERR(tlink);
63862306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	if (tcon->ses->server->ops->query_all_EAs == NULL) {
64162306a36Sopenharmony_ci		cifs_put_tlink(tlink);
64262306a36Sopenharmony_ci		return -EOPNOTSUPP;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path,
64662306a36Sopenharmony_ci			"SETFILEBITS", ea_value, 4 /* size of buf */,
64762306a36Sopenharmony_ci			cifs_sb);
64862306a36Sopenharmony_ci	cifs_put_tlink(tlink);
64962306a36Sopenharmony_ci	if (rc < 0)
65062306a36Sopenharmony_ci		return (int)rc;
65162306a36Sopenharmony_ci	else if (rc > 3) {
65262306a36Sopenharmony_ci		mode = le32_to_cpu(*((__le32 *)ea_value));
65362306a36Sopenharmony_ci		fattr->cf_mode &= ~SFBITS_MASK;
65462306a36Sopenharmony_ci		cifs_dbg(FYI, "special bits 0%o org mode 0%o\n",
65562306a36Sopenharmony_ci			 mode, fattr->cf_mode);
65662306a36Sopenharmony_ci		fattr->cf_mode = (mode & SFBITS_MASK) | fattr->cf_mode;
65762306a36Sopenharmony_ci		cifs_dbg(FYI, "special mode bits 0%o\n", mode);
65862306a36Sopenharmony_ci	}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return 0;
66162306a36Sopenharmony_ci#else
66262306a36Sopenharmony_ci	return -EOPNOTSUPP;
66362306a36Sopenharmony_ci#endif
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci/* Fill a cifs_fattr struct with info from POSIX info struct */
66762306a36Sopenharmony_cistatic void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
66862306a36Sopenharmony_ci				       struct cifs_open_info_data *data,
66962306a36Sopenharmony_ci				       struct cifs_sid *owner,
67062306a36Sopenharmony_ci				       struct cifs_sid *group,
67162306a36Sopenharmony_ci				       struct super_block *sb)
67262306a36Sopenharmony_ci{
67362306a36Sopenharmony_ci	struct smb311_posix_qinfo *info = &data->posix_fi;
67462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
67562306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* no fattr->flags to set */
68062306a36Sopenharmony_ci	fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
68162306a36Sopenharmony_ci	fattr->cf_uniqueid = le64_to_cpu(info->Inode);
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	if (info->LastAccessTime)
68462306a36Sopenharmony_ci		fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
68562306a36Sopenharmony_ci	else
68662306a36Sopenharmony_ci		ktime_get_coarse_real_ts64(&fattr->cf_atime);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
68962306a36Sopenharmony_ci	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (data->adjust_tz) {
69262306a36Sopenharmony_ci		fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
69362306a36Sopenharmony_ci		fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
69462306a36Sopenharmony_ci	}
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
69762306a36Sopenharmony_ci	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
69862306a36Sopenharmony_ci	fattr->cf_createtime = le64_to_cpu(info->CreationTime);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	fattr->cf_nlink = le32_to_cpu(info->HardLinks);
70162306a36Sopenharmony_ci	fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
70262306a36Sopenharmony_ci	/* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
70362306a36Sopenharmony_ci	/* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	if (data->symlink) {
70662306a36Sopenharmony_ci		fattr->cf_mode |= S_IFLNK;
70762306a36Sopenharmony_ci		fattr->cf_dtype = DT_LNK;
70862306a36Sopenharmony_ci		fattr->cf_symlink_target = data->symlink_target;
70962306a36Sopenharmony_ci		data->symlink_target = NULL;
71062306a36Sopenharmony_ci	} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
71162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFDIR;
71262306a36Sopenharmony_ci		fattr->cf_dtype = DT_DIR;
71362306a36Sopenharmony_ci	} else { /* file */
71462306a36Sopenharmony_ci		fattr->cf_mode |= S_IFREG;
71562306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
71662306a36Sopenharmony_ci	}
71762306a36Sopenharmony_ci	/* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	sid_to_id(cifs_sb, owner, fattr, SIDOWNER);
72062306a36Sopenharmony_ci	sid_to_id(cifs_sb, group, fattr, SIDGROUP);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n",
72362306a36Sopenharmony_ci		fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic inline dev_t nfs_mkdev(struct reparse_posix_data *buf)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	u64 v = le64_to_cpu(*(__le64 *)buf->DataBuffer);
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	return MKDEV(v >> 32, v & 0xffffffff);
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cibool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
73462306a36Sopenharmony_ci				 struct cifs_fattr *fattr,
73562306a36Sopenharmony_ci				 struct cifs_open_info_data *data)
73662306a36Sopenharmony_ci{
73762306a36Sopenharmony_ci	struct reparse_posix_data *buf = data->reparse.posix;
73862306a36Sopenharmony_ci	u32 tag = data->reparse.tag;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	if (tag == IO_REPARSE_TAG_NFS && buf) {
74162306a36Sopenharmony_ci		switch (le64_to_cpu(buf->InodeType)) {
74262306a36Sopenharmony_ci		case NFS_SPECFILE_CHR:
74362306a36Sopenharmony_ci			fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
74462306a36Sopenharmony_ci			fattr->cf_dtype = DT_CHR;
74562306a36Sopenharmony_ci			fattr->cf_rdev = nfs_mkdev(buf);
74662306a36Sopenharmony_ci			break;
74762306a36Sopenharmony_ci		case NFS_SPECFILE_BLK:
74862306a36Sopenharmony_ci			fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
74962306a36Sopenharmony_ci			fattr->cf_dtype = DT_BLK;
75062306a36Sopenharmony_ci			fattr->cf_rdev = nfs_mkdev(buf);
75162306a36Sopenharmony_ci			break;
75262306a36Sopenharmony_ci		case NFS_SPECFILE_FIFO:
75362306a36Sopenharmony_ci			fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
75462306a36Sopenharmony_ci			fattr->cf_dtype = DT_FIFO;
75562306a36Sopenharmony_ci			break;
75662306a36Sopenharmony_ci		case NFS_SPECFILE_SOCK:
75762306a36Sopenharmony_ci			fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
75862306a36Sopenharmony_ci			fattr->cf_dtype = DT_SOCK;
75962306a36Sopenharmony_ci			break;
76062306a36Sopenharmony_ci		case NFS_SPECFILE_LNK:
76162306a36Sopenharmony_ci			fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
76262306a36Sopenharmony_ci			fattr->cf_dtype = DT_LNK;
76362306a36Sopenharmony_ci			break;
76462306a36Sopenharmony_ci		default:
76562306a36Sopenharmony_ci			WARN_ON_ONCE(1);
76662306a36Sopenharmony_ci			return false;
76762306a36Sopenharmony_ci		}
76862306a36Sopenharmony_ci		return true;
76962306a36Sopenharmony_ci	}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	switch (tag) {
77262306a36Sopenharmony_ci	case IO_REPARSE_TAG_LX_SYMLINK:
77362306a36Sopenharmony_ci		fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
77462306a36Sopenharmony_ci		fattr->cf_dtype = DT_LNK;
77562306a36Sopenharmony_ci		break;
77662306a36Sopenharmony_ci	case IO_REPARSE_TAG_LX_FIFO:
77762306a36Sopenharmony_ci		fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
77862306a36Sopenharmony_ci		fattr->cf_dtype = DT_FIFO;
77962306a36Sopenharmony_ci		break;
78062306a36Sopenharmony_ci	case IO_REPARSE_TAG_AF_UNIX:
78162306a36Sopenharmony_ci		fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
78262306a36Sopenharmony_ci		fattr->cf_dtype = DT_SOCK;
78362306a36Sopenharmony_ci		break;
78462306a36Sopenharmony_ci	case IO_REPARSE_TAG_LX_CHR:
78562306a36Sopenharmony_ci		fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
78662306a36Sopenharmony_ci		fattr->cf_dtype = DT_CHR;
78762306a36Sopenharmony_ci		break;
78862306a36Sopenharmony_ci	case IO_REPARSE_TAG_LX_BLK:
78962306a36Sopenharmony_ci		fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
79062306a36Sopenharmony_ci		fattr->cf_dtype = DT_BLK;
79162306a36Sopenharmony_ci		break;
79262306a36Sopenharmony_ci	case 0: /* SMB1 symlink */
79362306a36Sopenharmony_ci	case IO_REPARSE_TAG_SYMLINK:
79462306a36Sopenharmony_ci	case IO_REPARSE_TAG_NFS:
79562306a36Sopenharmony_ci		fattr->cf_mode = S_IFLNK | cifs_sb->ctx->file_mode;
79662306a36Sopenharmony_ci		fattr->cf_dtype = DT_LNK;
79762306a36Sopenharmony_ci		break;
79862306a36Sopenharmony_ci	default:
79962306a36Sopenharmony_ci		return false;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci	return true;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
80562306a36Sopenharmony_ci				    struct cifs_open_info_data *data,
80662306a36Sopenharmony_ci				    struct super_block *sb)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct smb2_file_all_info *info = &data->fi;
80962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
81062306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	memset(fattr, 0, sizeof(*fattr));
81362306a36Sopenharmony_ci	fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
81462306a36Sopenharmony_ci	if (info->DeletePending)
81562306a36Sopenharmony_ci		fattr->cf_flags |= CIFS_FATTR_DELETE_PENDING;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	if (info->LastAccessTime)
81862306a36Sopenharmony_ci		fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
81962306a36Sopenharmony_ci	else
82062306a36Sopenharmony_ci		ktime_get_coarse_real_ts64(&fattr->cf_atime);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
82362306a36Sopenharmony_ci	fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	if (data->adjust_tz) {
82662306a36Sopenharmony_ci		fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
82762306a36Sopenharmony_ci		fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	fattr->cf_eof = le64_to_cpu(info->EndOfFile);
83162306a36Sopenharmony_ci	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
83262306a36Sopenharmony_ci	fattr->cf_createtime = le64_to_cpu(info->CreationTime);
83362306a36Sopenharmony_ci	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (cifs_open_data_reparse(data) &&
83662306a36Sopenharmony_ci	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
83762306a36Sopenharmony_ci		goto out_reparse;
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_ci	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
84062306a36Sopenharmony_ci		fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
84162306a36Sopenharmony_ci		fattr->cf_dtype = DT_DIR;
84262306a36Sopenharmony_ci		/*
84362306a36Sopenharmony_ci		 * Server can return wrong NumberOfLinks value for directories
84462306a36Sopenharmony_ci		 * when Unix extensions are disabled - fake it.
84562306a36Sopenharmony_ci		 */
84662306a36Sopenharmony_ci		if (!tcon->unix_ext)
84762306a36Sopenharmony_ci			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
84862306a36Sopenharmony_ci	} else {
84962306a36Sopenharmony_ci		fattr->cf_mode = S_IFREG | cifs_sb->ctx->file_mode;
85062306a36Sopenharmony_ci		fattr->cf_dtype = DT_REG;
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_ci		/* clear write bits if ATTR_READONLY is set */
85362306a36Sopenharmony_ci		if (fattr->cf_cifsattrs & ATTR_READONLY)
85462306a36Sopenharmony_ci			fattr->cf_mode &= ~(S_IWUGO);
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		/*
85762306a36Sopenharmony_ci		 * Don't accept zero nlink from non-unix servers unless
85862306a36Sopenharmony_ci		 * delete is pending.  Instead mark it as unknown.
85962306a36Sopenharmony_ci		 */
86062306a36Sopenharmony_ci		if ((fattr->cf_nlink < 1) && !tcon->unix_ext &&
86162306a36Sopenharmony_ci		    !info->DeletePending) {
86262306a36Sopenharmony_ci			cifs_dbg(VFS, "bogus file nlink value %u\n",
86362306a36Sopenharmony_ci				 fattr->cf_nlink);
86462306a36Sopenharmony_ci			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
86562306a36Sopenharmony_ci		}
86662306a36Sopenharmony_ci	}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ciout_reparse:
86962306a36Sopenharmony_ci	if (S_ISLNK(fattr->cf_mode)) {
87062306a36Sopenharmony_ci		if (likely(data->symlink_target))
87162306a36Sopenharmony_ci			fattr->cf_eof = strnlen(data->symlink_target, PATH_MAX);
87262306a36Sopenharmony_ci		fattr->cf_symlink_target = data->symlink_target;
87362306a36Sopenharmony_ci		data->symlink_target = NULL;
87462306a36Sopenharmony_ci	}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci	fattr->cf_uid = cifs_sb->ctx->linux_uid;
87762306a36Sopenharmony_ci	fattr->cf_gid = cifs_sb->ctx->linux_gid;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int
88162306a36Sopenharmony_cicifs_get_file_info(struct file *filp)
88262306a36Sopenharmony_ci{
88362306a36Sopenharmony_ci	int rc;
88462306a36Sopenharmony_ci	unsigned int xid;
88562306a36Sopenharmony_ci	struct cifs_open_info_data data = {};
88662306a36Sopenharmony_ci	struct cifs_fattr fattr;
88762306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
88862306a36Sopenharmony_ci	struct cifsFileInfo *cfile = filp->private_data;
88962306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
89062306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (!server->ops->query_file_info)
89362306a36Sopenharmony_ci		return -ENOSYS;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	xid = get_xid();
89662306a36Sopenharmony_ci	rc = server->ops->query_file_info(xid, tcon, cfile, &data);
89762306a36Sopenharmony_ci	switch (rc) {
89862306a36Sopenharmony_ci	case 0:
89962306a36Sopenharmony_ci		/* TODO: add support to query reparse tag */
90062306a36Sopenharmony_ci		data.adjust_tz = false;
90162306a36Sopenharmony_ci		if (data.symlink_target) {
90262306a36Sopenharmony_ci			data.symlink = true;
90362306a36Sopenharmony_ci			data.reparse.tag = IO_REPARSE_TAG_SYMLINK;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci		cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
90662306a36Sopenharmony_ci		break;
90762306a36Sopenharmony_ci	case -EREMOTE:
90862306a36Sopenharmony_ci		cifs_create_junction_fattr(&fattr, inode->i_sb);
90962306a36Sopenharmony_ci		rc = 0;
91062306a36Sopenharmony_ci		break;
91162306a36Sopenharmony_ci	case -EOPNOTSUPP:
91262306a36Sopenharmony_ci	case -EINVAL:
91362306a36Sopenharmony_ci		/*
91462306a36Sopenharmony_ci		 * FIXME: legacy server -- fall back to path-based call?
91562306a36Sopenharmony_ci		 * for now, just skip revalidating and mark inode for
91662306a36Sopenharmony_ci		 * immediate reval.
91762306a36Sopenharmony_ci		 */
91862306a36Sopenharmony_ci		rc = 0;
91962306a36Sopenharmony_ci		CIFS_I(inode)->time = 0;
92062306a36Sopenharmony_ci		goto cgfi_exit;
92162306a36Sopenharmony_ci	default:
92262306a36Sopenharmony_ci		goto cgfi_exit;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/*
92662306a36Sopenharmony_ci	 * don't bother with SFU junk here -- just mark inode as needing
92762306a36Sopenharmony_ci	 * revalidation.
92862306a36Sopenharmony_ci	 */
92962306a36Sopenharmony_ci	fattr.cf_uniqueid = CIFS_I(inode)->uniqueid;
93062306a36Sopenharmony_ci	fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
93162306a36Sopenharmony_ci	/* if filetype is different, return error */
93262306a36Sopenharmony_ci	rc = cifs_fattr_to_inode(inode, &fattr, false);
93362306a36Sopenharmony_cicgfi_exit:
93462306a36Sopenharmony_ci	cifs_free_open_info(&data);
93562306a36Sopenharmony_ci	free_xid(xid);
93662306a36Sopenharmony_ci	return rc;
93762306a36Sopenharmony_ci}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci/* Simple function to return a 64 bit hash of string.  Rarely called */
94062306a36Sopenharmony_cistatic __u64 simple_hashstr(const char *str)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	const __u64 hash_mult =  1125899906842597ULL; /* a big enough prime */
94362306a36Sopenharmony_ci	__u64 hash = 0;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	while (*str)
94662306a36Sopenharmony_ci		hash = (hash + (__u64) *str++) * hash_mult;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	return hash;
94962306a36Sopenharmony_ci}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
95262306a36Sopenharmony_ci/**
95362306a36Sopenharmony_ci * cifs_backup_query_path_info - SMB1 fallback code to get ino
95462306a36Sopenharmony_ci *
95562306a36Sopenharmony_ci * Fallback code to get file metadata when we don't have access to
95662306a36Sopenharmony_ci * full_path (EACCES) and have backup creds.
95762306a36Sopenharmony_ci *
95862306a36Sopenharmony_ci * @xid:	transaction id used to identify original request in logs
95962306a36Sopenharmony_ci * @tcon:	information about the server share we have mounted
96062306a36Sopenharmony_ci * @sb:	the superblock stores info such as disk space available
96162306a36Sopenharmony_ci * @full_path:	name of the file we are getting the metadata for
96262306a36Sopenharmony_ci * @resp_buf:	will be set to cifs resp buf and needs to be freed with
96362306a36Sopenharmony_ci * 		cifs_buf_release() when done with @data
96462306a36Sopenharmony_ci * @data:	will be set to search info result buffer
96562306a36Sopenharmony_ci */
96662306a36Sopenharmony_cistatic int
96762306a36Sopenharmony_cicifs_backup_query_path_info(int xid,
96862306a36Sopenharmony_ci			    struct cifs_tcon *tcon,
96962306a36Sopenharmony_ci			    struct super_block *sb,
97062306a36Sopenharmony_ci			    const char *full_path,
97162306a36Sopenharmony_ci			    void **resp_buf,
97262306a36Sopenharmony_ci			    FILE_ALL_INFO **data)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
97562306a36Sopenharmony_ci	struct cifs_search_info info = {0};
97662306a36Sopenharmony_ci	u16 flags;
97762306a36Sopenharmony_ci	int rc;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	*resp_buf = NULL;
98062306a36Sopenharmony_ci	info.endOfSearch = false;
98162306a36Sopenharmony_ci	if (tcon->unix_ext)
98262306a36Sopenharmony_ci		info.info_level = SMB_FIND_FILE_UNIX;
98362306a36Sopenharmony_ci	else if ((tcon->ses->capabilities &
98462306a36Sopenharmony_ci		  tcon->ses->server->vals->cap_nt_find) == 0)
98562306a36Sopenharmony_ci		info.info_level = SMB_FIND_FILE_INFO_STANDARD;
98662306a36Sopenharmony_ci	else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)
98762306a36Sopenharmony_ci		info.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
98862306a36Sopenharmony_ci	else /* no srvino useful for fallback to some netapp */
98962306a36Sopenharmony_ci		info.info_level = SMB_FIND_FILE_DIRECTORY_INFO;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	flags = CIFS_SEARCH_CLOSE_ALWAYS |
99262306a36Sopenharmony_ci		CIFS_SEARCH_CLOSE_AT_END |
99362306a36Sopenharmony_ci		CIFS_SEARCH_BACKUP_SEARCH;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	rc = CIFSFindFirst(xid, tcon, full_path,
99662306a36Sopenharmony_ci			   cifs_sb, NULL, flags, &info, false);
99762306a36Sopenharmony_ci	if (rc)
99862306a36Sopenharmony_ci		return rc;
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	*resp_buf = (void *)info.ntwrk_buf_start;
100162306a36Sopenharmony_ci	*data = (FILE_ALL_INFO *)info.srch_entries_start;
100262306a36Sopenharmony_ci	return 0;
100362306a36Sopenharmony_ci}
100462306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_cistatic void cifs_set_fattr_ino(int xid, struct cifs_tcon *tcon, struct super_block *sb,
100762306a36Sopenharmony_ci			       struct inode **inode, const char *full_path,
100862306a36Sopenharmony_ci			       struct cifs_open_info_data *data, struct cifs_fattr *fattr)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
101162306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
101262306a36Sopenharmony_ci	int rc;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM)) {
101562306a36Sopenharmony_ci		if (*inode)
101662306a36Sopenharmony_ci			fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
101762306a36Sopenharmony_ci		else
101862306a36Sopenharmony_ci			fattr->cf_uniqueid = iunique(sb, ROOT_I);
101962306a36Sopenharmony_ci		return;
102062306a36Sopenharmony_ci	}
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	/*
102362306a36Sopenharmony_ci	 * If we have an inode pass a NULL tcon to ensure we don't
102462306a36Sopenharmony_ci	 * make a round trip to the server. This only works for SMB2+.
102562306a36Sopenharmony_ci	 */
102662306a36Sopenharmony_ci	rc = server->ops->get_srv_inum(xid, *inode ? NULL : tcon, cifs_sb, full_path,
102762306a36Sopenharmony_ci				       &fattr->cf_uniqueid, data);
102862306a36Sopenharmony_ci	if (rc) {
102962306a36Sopenharmony_ci		/*
103062306a36Sopenharmony_ci		 * If that fails reuse existing ino or generate one
103162306a36Sopenharmony_ci		 * and disable server ones
103262306a36Sopenharmony_ci		 */
103362306a36Sopenharmony_ci		if (*inode)
103462306a36Sopenharmony_ci			fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
103562306a36Sopenharmony_ci		else {
103662306a36Sopenharmony_ci			fattr->cf_uniqueid = iunique(sb, ROOT_I);
103762306a36Sopenharmony_ci			cifs_autodisable_serverino(cifs_sb);
103862306a36Sopenharmony_ci		}
103962306a36Sopenharmony_ci		return;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	/* If no errors, check for zero root inode (invalid) */
104362306a36Sopenharmony_ci	if (fattr->cf_uniqueid == 0 && strlen(full_path) == 0) {
104462306a36Sopenharmony_ci		cifs_dbg(FYI, "Invalid (0) inodenum\n");
104562306a36Sopenharmony_ci		if (*inode) {
104662306a36Sopenharmony_ci			/* reuse */
104762306a36Sopenharmony_ci			fattr->cf_uniqueid = CIFS_I(*inode)->uniqueid;
104862306a36Sopenharmony_ci		} else {
104962306a36Sopenharmony_ci			/* make an ino by hashing the UNC */
105062306a36Sopenharmony_ci			fattr->cf_flags |= CIFS_FATTR_FAKE_ROOT_INO;
105162306a36Sopenharmony_ci			fattr->cf_uniqueid = simple_hashstr(tcon->tree_name);
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci	}
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic inline bool is_inode_cache_good(struct inode *ino)
105762306a36Sopenharmony_ci{
105862306a36Sopenharmony_ci	return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
105962306a36Sopenharmony_ci}
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_cistatic int reparse_info_to_fattr(struct cifs_open_info_data *data,
106262306a36Sopenharmony_ci				 struct super_block *sb,
106362306a36Sopenharmony_ci				 const unsigned int xid,
106462306a36Sopenharmony_ci				 struct cifs_tcon *tcon,
106562306a36Sopenharmony_ci				 const char *full_path,
106662306a36Sopenharmony_ci				 struct cifs_fattr *fattr)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
106962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
107062306a36Sopenharmony_ci	struct kvec rsp_iov, *iov = NULL;
107162306a36Sopenharmony_ci	int rsp_buftype = CIFS_NO_BUFFER;
107262306a36Sopenharmony_ci	u32 tag = data->reparse.tag;
107362306a36Sopenharmony_ci	int rc = 0;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	if (!tag && server->ops->query_reparse_point) {
107662306a36Sopenharmony_ci		rc = server->ops->query_reparse_point(xid, tcon, cifs_sb,
107762306a36Sopenharmony_ci						      full_path, &tag,
107862306a36Sopenharmony_ci						      &rsp_iov, &rsp_buftype);
107962306a36Sopenharmony_ci		if (!rc)
108062306a36Sopenharmony_ci			iov = &rsp_iov;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	rc = -EOPNOTSUPP;
108462306a36Sopenharmony_ci	switch ((data->reparse.tag = tag)) {
108562306a36Sopenharmony_ci	case 0: /* SMB1 symlink */
108662306a36Sopenharmony_ci		if (server->ops->query_symlink) {
108762306a36Sopenharmony_ci			rc = server->ops->query_symlink(xid, tcon,
108862306a36Sopenharmony_ci							cifs_sb, full_path,
108962306a36Sopenharmony_ci							&data->symlink_target);
109062306a36Sopenharmony_ci		}
109162306a36Sopenharmony_ci		break;
109262306a36Sopenharmony_ci	case IO_REPARSE_TAG_MOUNT_POINT:
109362306a36Sopenharmony_ci		cifs_create_junction_fattr(fattr, sb);
109462306a36Sopenharmony_ci		rc = 0;
109562306a36Sopenharmony_ci		goto out;
109662306a36Sopenharmony_ci	default:
109762306a36Sopenharmony_ci		if (data->symlink_target) {
109862306a36Sopenharmony_ci			rc = 0;
109962306a36Sopenharmony_ci		} else if (server->ops->parse_reparse_point) {
110062306a36Sopenharmony_ci			rc = server->ops->parse_reparse_point(cifs_sb,
110162306a36Sopenharmony_ci							      iov, data);
110262306a36Sopenharmony_ci		}
110362306a36Sopenharmony_ci		break;
110462306a36Sopenharmony_ci	}
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	cifs_open_info_to_fattr(fattr, data, sb);
110762306a36Sopenharmony_ciout:
110862306a36Sopenharmony_ci	fattr->cf_cifstag = data->reparse.tag;
110962306a36Sopenharmony_ci	free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
111062306a36Sopenharmony_ci	return rc;
111162306a36Sopenharmony_ci}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_cistatic int cifs_get_fattr(struct cifs_open_info_data *data,
111462306a36Sopenharmony_ci			  struct super_block *sb, int xid,
111562306a36Sopenharmony_ci			  const struct cifs_fid *fid,
111662306a36Sopenharmony_ci			  struct cifs_fattr *fattr,
111762306a36Sopenharmony_ci			  struct inode **inode,
111862306a36Sopenharmony_ci			  const char *full_path)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	struct cifs_open_info_data tmp_data = {};
112162306a36Sopenharmony_ci	struct cifs_tcon *tcon;
112262306a36Sopenharmony_ci	struct TCP_Server_Info *server;
112362306a36Sopenharmony_ci	struct tcon_link *tlink;
112462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
112562306a36Sopenharmony_ci	void *smb1_backup_rsp_buf = NULL;
112662306a36Sopenharmony_ci	int rc = 0;
112762306a36Sopenharmony_ci	int tmprc = 0;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
113062306a36Sopenharmony_ci	if (IS_ERR(tlink))
113162306a36Sopenharmony_ci		return PTR_ERR(tlink);
113262306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
113362306a36Sopenharmony_ci	server = tcon->ses->server;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	/*
113662306a36Sopenharmony_ci	 * 1. Fetch file metadata if not provided (data)
113762306a36Sopenharmony_ci	 */
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	if (!data) {
114062306a36Sopenharmony_ci		rc = server->ops->query_path_info(xid, tcon, cifs_sb,
114162306a36Sopenharmony_ci						  full_path, &tmp_data);
114262306a36Sopenharmony_ci		data = &tmp_data;
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	/*
114662306a36Sopenharmony_ci	 * 2. Convert it to internal cifs metadata (fattr)
114762306a36Sopenharmony_ci	 */
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci	switch (rc) {
115062306a36Sopenharmony_ci	case 0:
115162306a36Sopenharmony_ci		/*
115262306a36Sopenharmony_ci		 * If the file is a reparse point, it is more complicated
115362306a36Sopenharmony_ci		 * since we have to check if its reparse tag matches a known
115462306a36Sopenharmony_ci		 * special file type e.g. symlink or fifo or char etc.
115562306a36Sopenharmony_ci		 */
115662306a36Sopenharmony_ci		if (cifs_open_data_reparse(data)) {
115762306a36Sopenharmony_ci			rc = reparse_info_to_fattr(data, sb, xid, tcon,
115862306a36Sopenharmony_ci						   full_path, fattr);
115962306a36Sopenharmony_ci		} else {
116062306a36Sopenharmony_ci			cifs_open_info_to_fattr(fattr, data, sb);
116162306a36Sopenharmony_ci		}
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	case -EREMOTE:
116462306a36Sopenharmony_ci		/* DFS link, no metadata available on this server */
116562306a36Sopenharmony_ci		cifs_create_junction_fattr(fattr, sb);
116662306a36Sopenharmony_ci		rc = 0;
116762306a36Sopenharmony_ci		break;
116862306a36Sopenharmony_ci	case -EACCES:
116962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
117062306a36Sopenharmony_ci		/*
117162306a36Sopenharmony_ci		 * perm errors, try again with backup flags if possible
117262306a36Sopenharmony_ci		 *
117362306a36Sopenharmony_ci		 * For SMB2 and later the backup intent flag
117462306a36Sopenharmony_ci		 * is already sent if needed on open and there
117562306a36Sopenharmony_ci		 * is no path based FindFirst operation to use
117662306a36Sopenharmony_ci		 * to retry with
117762306a36Sopenharmony_ci		 */
117862306a36Sopenharmony_ci		if (backup_cred(cifs_sb) && is_smb1_server(server)) {
117962306a36Sopenharmony_ci			/* for easier reading */
118062306a36Sopenharmony_ci			FILE_ALL_INFO *fi;
118162306a36Sopenharmony_ci			FILE_DIRECTORY_INFO *fdi;
118262306a36Sopenharmony_ci			SEARCH_ID_FULL_DIR_INFO *si;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci			rc = cifs_backup_query_path_info(xid, tcon, sb,
118562306a36Sopenharmony_ci							 full_path,
118662306a36Sopenharmony_ci							 &smb1_backup_rsp_buf,
118762306a36Sopenharmony_ci							 &fi);
118862306a36Sopenharmony_ci			if (rc)
118962306a36Sopenharmony_ci				goto out;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci			move_cifs_info_to_smb2(&data->fi, fi);
119262306a36Sopenharmony_ci			fdi = (FILE_DIRECTORY_INFO *)fi;
119362306a36Sopenharmony_ci			si = (SEARCH_ID_FULL_DIR_INFO *)fi;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci			cifs_dir_info_to_fattr(fattr, fdi, cifs_sb);
119662306a36Sopenharmony_ci			fattr->cf_uniqueid = le64_to_cpu(si->UniqueId);
119762306a36Sopenharmony_ci			/* uniqueid set, skip get inum step */
119862306a36Sopenharmony_ci			goto handle_mnt_opt;
119962306a36Sopenharmony_ci		} else {
120062306a36Sopenharmony_ci			/* nothing we can do, bail out */
120162306a36Sopenharmony_ci			goto out;
120262306a36Sopenharmony_ci		}
120362306a36Sopenharmony_ci#else
120462306a36Sopenharmony_ci		goto out;
120562306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
120662306a36Sopenharmony_ci		break;
120762306a36Sopenharmony_ci	default:
120862306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
120962306a36Sopenharmony_ci		goto out;
121062306a36Sopenharmony_ci	}
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	/*
121362306a36Sopenharmony_ci	 * 3. Get or update inode number (fattr->cf_uniqueid)
121462306a36Sopenharmony_ci	 */
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	cifs_set_fattr_ino(xid, tcon, sb, inode, full_path, data, fattr);
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	/*
121962306a36Sopenharmony_ci	 * 4. Tweak fattr based on mount options
122062306a36Sopenharmony_ci	 */
122162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
122262306a36Sopenharmony_cihandle_mnt_opt:
122362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
122462306a36Sopenharmony_ci	/* query for SFU type info if supported and needed */
122562306a36Sopenharmony_ci	if ((fattr->cf_cifsattrs & ATTR_SYSTEM) &&
122662306a36Sopenharmony_ci	    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) {
122762306a36Sopenharmony_ci		tmprc = cifs_sfu_type(fattr, full_path, cifs_sb, xid);
122862306a36Sopenharmony_ci		if (tmprc)
122962306a36Sopenharmony_ci			cifs_dbg(FYI, "cifs_sfu_type failed: %d\n", tmprc);
123062306a36Sopenharmony_ci	}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	/* fill in 0777 bits from ACL */
123362306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) {
123462306a36Sopenharmony_ci		rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode,
123562306a36Sopenharmony_ci				       true, full_path, fid);
123662306a36Sopenharmony_ci		if (rc == -EREMOTE)
123762306a36Sopenharmony_ci			rc = 0;
123862306a36Sopenharmony_ci		if (rc) {
123962306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: Get mode from SID failed. rc=%d\n",
124062306a36Sopenharmony_ci				 __func__, rc);
124162306a36Sopenharmony_ci			goto out;
124262306a36Sopenharmony_ci		}
124362306a36Sopenharmony_ci	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
124462306a36Sopenharmony_ci		rc = cifs_acl_to_fattr(cifs_sb, fattr, *inode,
124562306a36Sopenharmony_ci				       false, full_path, fid);
124662306a36Sopenharmony_ci		if (rc == -EREMOTE)
124762306a36Sopenharmony_ci			rc = 0;
124862306a36Sopenharmony_ci		if (rc) {
124962306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: Getting ACL failed with error: %d\n",
125062306a36Sopenharmony_ci				 __func__, rc);
125162306a36Sopenharmony_ci			goto out;
125262306a36Sopenharmony_ci		}
125362306a36Sopenharmony_ci	}
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* fill in remaining high mode bits e.g. SUID, VTX */
125662306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
125762306a36Sopenharmony_ci		cifs_sfu_mode(fattr, full_path, cifs_sb, xid);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	/* check for Minshall+French symlinks */
126062306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
126162306a36Sopenharmony_ci		tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
126262306a36Sopenharmony_ci		cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
126362306a36Sopenharmony_ci	}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ciout:
126662306a36Sopenharmony_ci	cifs_buf_release(smb1_backup_rsp_buf);
126762306a36Sopenharmony_ci	cifs_put_tlink(tlink);
126862306a36Sopenharmony_ci	cifs_free_open_info(&tmp_data);
126962306a36Sopenharmony_ci	return rc;
127062306a36Sopenharmony_ci}
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ciint cifs_get_inode_info(struct inode **inode,
127362306a36Sopenharmony_ci			const char *full_path,
127462306a36Sopenharmony_ci			struct cifs_open_info_data *data,
127562306a36Sopenharmony_ci			struct super_block *sb, int xid,
127662306a36Sopenharmony_ci			const struct cifs_fid *fid)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct cifs_fattr fattr = {};
127962306a36Sopenharmony_ci	int rc;
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (is_inode_cache_good(*inode)) {
128262306a36Sopenharmony_ci		cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
128362306a36Sopenharmony_ci		return 0;
128462306a36Sopenharmony_ci	}
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	rc = cifs_get_fattr(data, sb, xid, fid, &fattr, inode, full_path);
128762306a36Sopenharmony_ci	if (rc)
128862306a36Sopenharmony_ci		goto out;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	rc = update_inode_info(sb, &fattr, inode);
129162306a36Sopenharmony_ciout:
129262306a36Sopenharmony_ci	kfree(fattr.cf_symlink_target);
129362306a36Sopenharmony_ci	return rc;
129462306a36Sopenharmony_ci}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_cistatic int smb311_posix_get_fattr(struct cifs_fattr *fattr,
129762306a36Sopenharmony_ci				  const char *full_path,
129862306a36Sopenharmony_ci				  struct super_block *sb,
129962306a36Sopenharmony_ci				  const unsigned int xid)
130062306a36Sopenharmony_ci{
130162306a36Sopenharmony_ci	struct cifs_open_info_data data = {};
130262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
130362306a36Sopenharmony_ci	struct cifs_tcon *tcon;
130462306a36Sopenharmony_ci	struct tcon_link *tlink;
130562306a36Sopenharmony_ci	struct cifs_sid owner, group;
130662306a36Sopenharmony_ci	int tmprc;
130762306a36Sopenharmony_ci	int rc;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
131062306a36Sopenharmony_ci	if (IS_ERR(tlink))
131162306a36Sopenharmony_ci		return PTR_ERR(tlink);
131262306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	/*
131562306a36Sopenharmony_ci	 * 1. Fetch file metadata
131662306a36Sopenharmony_ci	 */
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
131962306a36Sopenharmony_ci					  full_path, &data,
132062306a36Sopenharmony_ci					  &owner, &group);
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	/*
132362306a36Sopenharmony_ci	 * 2. Convert it to internal cifs metadata (fattr)
132462306a36Sopenharmony_ci	 */
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	switch (rc) {
132762306a36Sopenharmony_ci	case 0:
132862306a36Sopenharmony_ci		smb311_posix_info_to_fattr(fattr, &data, &owner, &group, sb);
132962306a36Sopenharmony_ci		break;
133062306a36Sopenharmony_ci	case -EREMOTE:
133162306a36Sopenharmony_ci		/* DFS link, no metadata available on this server */
133262306a36Sopenharmony_ci		cifs_create_junction_fattr(fattr, sb);
133362306a36Sopenharmony_ci		rc = 0;
133462306a36Sopenharmony_ci		break;
133562306a36Sopenharmony_ci	case -EACCES:
133662306a36Sopenharmony_ci		/*
133762306a36Sopenharmony_ci		 * For SMB2 and later the backup intent flag
133862306a36Sopenharmony_ci		 * is already sent if needed on open and there
133962306a36Sopenharmony_ci		 * is no path based FindFirst operation to use
134062306a36Sopenharmony_ci		 * to retry with so nothing we can do, bail out
134162306a36Sopenharmony_ci		 */
134262306a36Sopenharmony_ci		goto out;
134362306a36Sopenharmony_ci	default:
134462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
134562306a36Sopenharmony_ci		goto out;
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	/*
134962306a36Sopenharmony_ci	 * 3. Tweak fattr based on mount options
135062306a36Sopenharmony_ci	 */
135162306a36Sopenharmony_ci	/* check for Minshall+French symlinks */
135262306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
135362306a36Sopenharmony_ci		tmprc = check_mf_symlink(xid, tcon, cifs_sb, fattr, full_path);
135462306a36Sopenharmony_ci		cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ciout:
135862306a36Sopenharmony_ci	cifs_put_tlink(tlink);
135962306a36Sopenharmony_ci	cifs_free_open_info(&data);
136062306a36Sopenharmony_ci	return rc;
136162306a36Sopenharmony_ci}
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ciint smb311_posix_get_inode_info(struct inode **inode, const char *full_path,
136462306a36Sopenharmony_ci				struct super_block *sb, const unsigned int xid)
136562306a36Sopenharmony_ci{
136662306a36Sopenharmony_ci	struct cifs_fattr fattr = {};
136762306a36Sopenharmony_ci	int rc;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	if (is_inode_cache_good(*inode)) {
137062306a36Sopenharmony_ci		cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
137162306a36Sopenharmony_ci		return 0;
137262306a36Sopenharmony_ci	}
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	rc = smb311_posix_get_fattr(&fattr, full_path, sb, xid);
137562306a36Sopenharmony_ci	if (rc)
137662306a36Sopenharmony_ci		goto out;
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	rc = update_inode_info(sb, &fattr, inode);
137962306a36Sopenharmony_ciout:
138062306a36Sopenharmony_ci	kfree(fattr.cf_symlink_target);
138162306a36Sopenharmony_ci	return rc;
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic const struct inode_operations cifs_ipc_inode_ops = {
138562306a36Sopenharmony_ci	.lookup = cifs_lookup,
138662306a36Sopenharmony_ci};
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_cistatic int
138962306a36Sopenharmony_cicifs_find_inode(struct inode *inode, void *opaque)
139062306a36Sopenharmony_ci{
139162306a36Sopenharmony_ci	struct cifs_fattr *fattr = opaque;
139262306a36Sopenharmony_ci
139362306a36Sopenharmony_ci	/* don't match inode with different uniqueid */
139462306a36Sopenharmony_ci	if (CIFS_I(inode)->uniqueid != fattr->cf_uniqueid)
139562306a36Sopenharmony_ci		return 0;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	/* use createtime like an i_generation field */
139862306a36Sopenharmony_ci	if (CIFS_I(inode)->createtime != fattr->cf_createtime)
139962306a36Sopenharmony_ci		return 0;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	/* don't match inode of different type */
140262306a36Sopenharmony_ci	if (inode_wrong_type(inode, fattr->cf_mode))
140362306a36Sopenharmony_ci		return 0;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci	/* if it's not a directory or has no dentries, then flag it */
140662306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode) && !hlist_empty(&inode->i_dentry))
140762306a36Sopenharmony_ci		fattr->cf_flags |= CIFS_FATTR_INO_COLLISION;
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	return 1;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic int
141362306a36Sopenharmony_cicifs_init_inode(struct inode *inode, void *opaque)
141462306a36Sopenharmony_ci{
141562306a36Sopenharmony_ci	struct cifs_fattr *fattr = opaque;
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci	CIFS_I(inode)->uniqueid = fattr->cf_uniqueid;
141862306a36Sopenharmony_ci	CIFS_I(inode)->createtime = fattr->cf_createtime;
141962306a36Sopenharmony_ci	return 0;
142062306a36Sopenharmony_ci}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci/*
142362306a36Sopenharmony_ci * walk dentry list for an inode and report whether it has aliases that
142462306a36Sopenharmony_ci * are hashed. We use this to determine if a directory inode can actually
142562306a36Sopenharmony_ci * be used.
142662306a36Sopenharmony_ci */
142762306a36Sopenharmony_cistatic bool
142862306a36Sopenharmony_ciinode_has_hashed_dentries(struct inode *inode)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	struct dentry *dentry;
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
143362306a36Sopenharmony_ci	hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) {
143462306a36Sopenharmony_ci		if (!d_unhashed(dentry) || IS_ROOT(dentry)) {
143562306a36Sopenharmony_ci			spin_unlock(&inode->i_lock);
143662306a36Sopenharmony_ci			return true;
143762306a36Sopenharmony_ci		}
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
144062306a36Sopenharmony_ci	return false;
144162306a36Sopenharmony_ci}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci/* Given fattrs, get a corresponding inode */
144462306a36Sopenharmony_cistruct inode *
144562306a36Sopenharmony_cicifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
144662306a36Sopenharmony_ci{
144762306a36Sopenharmony_ci	unsigned long hash;
144862306a36Sopenharmony_ci	struct inode *inode;
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ciretry_iget5_locked:
145162306a36Sopenharmony_ci	cifs_dbg(FYI, "looking for uniqueid=%llu\n", fattr->cf_uniqueid);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* hash down to 32-bits on 32-bit arch */
145462306a36Sopenharmony_ci	hash = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	inode = iget5_locked(sb, hash, cifs_find_inode, cifs_init_inode, fattr);
145762306a36Sopenharmony_ci	if (inode) {
145862306a36Sopenharmony_ci		/* was there a potentially problematic inode collision? */
145962306a36Sopenharmony_ci		if (fattr->cf_flags & CIFS_FATTR_INO_COLLISION) {
146062306a36Sopenharmony_ci			fattr->cf_flags &= ~CIFS_FATTR_INO_COLLISION;
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci			if (inode_has_hashed_dentries(inode)) {
146362306a36Sopenharmony_ci				cifs_autodisable_serverino(CIFS_SB(sb));
146462306a36Sopenharmony_ci				iput(inode);
146562306a36Sopenharmony_ci				fattr->cf_uniqueid = iunique(sb, ROOT_I);
146662306a36Sopenharmony_ci				goto retry_iget5_locked;
146762306a36Sopenharmony_ci			}
146862306a36Sopenharmony_ci		}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci		/* can't fail - see cifs_find_inode() */
147162306a36Sopenharmony_ci		cifs_fattr_to_inode(inode, fattr, false);
147262306a36Sopenharmony_ci		if (sb->s_flags & SB_NOATIME)
147362306a36Sopenharmony_ci			inode->i_flags |= S_NOATIME | S_NOCMTIME;
147462306a36Sopenharmony_ci		if (inode->i_state & I_NEW) {
147562306a36Sopenharmony_ci			inode->i_ino = hash;
147662306a36Sopenharmony_ci			cifs_fscache_get_inode_cookie(inode);
147762306a36Sopenharmony_ci			unlock_new_inode(inode);
147862306a36Sopenharmony_ci		}
147962306a36Sopenharmony_ci	}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	return inode;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci/* gets root inode */
148562306a36Sopenharmony_cistruct inode *cifs_root_iget(struct super_block *sb)
148662306a36Sopenharmony_ci{
148762306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
148862306a36Sopenharmony_ci	struct cifs_fattr fattr = {};
148962306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
149062306a36Sopenharmony_ci	struct inode *inode = NULL;
149162306a36Sopenharmony_ci	unsigned int xid;
149262306a36Sopenharmony_ci	char *path = NULL;
149362306a36Sopenharmony_ci	int len;
149462306a36Sopenharmony_ci	int rc;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH)
149762306a36Sopenharmony_ci	    && cifs_sb->prepath) {
149862306a36Sopenharmony_ci		len = strlen(cifs_sb->prepath);
149962306a36Sopenharmony_ci		path = kzalloc(len + 2 /* leading sep + null */, GFP_KERNEL);
150062306a36Sopenharmony_ci		if (path == NULL)
150162306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
150262306a36Sopenharmony_ci		path[0] = '/';
150362306a36Sopenharmony_ci		memcpy(path+1, cifs_sb->prepath, len);
150462306a36Sopenharmony_ci	} else {
150562306a36Sopenharmony_ci		path = kstrdup("", GFP_KERNEL);
150662306a36Sopenharmony_ci		if (path == NULL)
150762306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	xid = get_xid();
151162306a36Sopenharmony_ci	if (tcon->unix_ext) {
151262306a36Sopenharmony_ci		rc = cifs_get_unix_fattr(path, sb, &fattr, &inode, xid);
151362306a36Sopenharmony_ci		/* some servers mistakenly claim POSIX support */
151462306a36Sopenharmony_ci		if (rc != -EOPNOTSUPP)
151562306a36Sopenharmony_ci			goto iget_root;
151662306a36Sopenharmony_ci		cifs_dbg(VFS, "server does not support POSIX extensions\n");
151762306a36Sopenharmony_ci		tcon->unix_ext = false;
151862306a36Sopenharmony_ci	}
151962306a36Sopenharmony_ci
152062306a36Sopenharmony_ci	convert_delimiter(path, CIFS_DIR_SEP(cifs_sb));
152162306a36Sopenharmony_ci	if (tcon->posix_extensions)
152262306a36Sopenharmony_ci		rc = smb311_posix_get_fattr(&fattr, path, sb, xid);
152362306a36Sopenharmony_ci	else
152462306a36Sopenharmony_ci		rc = cifs_get_fattr(NULL, sb, xid, NULL, &fattr, &inode, path);
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ciiget_root:
152762306a36Sopenharmony_ci	if (!rc) {
152862306a36Sopenharmony_ci		if (fattr.cf_flags & CIFS_FATTR_JUNCTION) {
152962306a36Sopenharmony_ci			fattr.cf_flags &= ~CIFS_FATTR_JUNCTION;
153062306a36Sopenharmony_ci			cifs_autodisable_serverino(cifs_sb);
153162306a36Sopenharmony_ci		}
153262306a36Sopenharmony_ci		inode = cifs_iget(sb, &fattr);
153362306a36Sopenharmony_ci	}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci	if (!inode) {
153662306a36Sopenharmony_ci		inode = ERR_PTR(rc);
153762306a36Sopenharmony_ci		goto out;
153862306a36Sopenharmony_ci	}
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	if (rc && tcon->pipe) {
154162306a36Sopenharmony_ci		cifs_dbg(FYI, "ipc connection - fake read inode\n");
154262306a36Sopenharmony_ci		spin_lock(&inode->i_lock);
154362306a36Sopenharmony_ci		inode->i_mode |= S_IFDIR;
154462306a36Sopenharmony_ci		set_nlink(inode, 2);
154562306a36Sopenharmony_ci		inode->i_op = &cifs_ipc_inode_ops;
154662306a36Sopenharmony_ci		inode->i_fop = &simple_dir_operations;
154762306a36Sopenharmony_ci		inode->i_uid = cifs_sb->ctx->linux_uid;
154862306a36Sopenharmony_ci		inode->i_gid = cifs_sb->ctx->linux_gid;
154962306a36Sopenharmony_ci		spin_unlock(&inode->i_lock);
155062306a36Sopenharmony_ci	} else if (rc) {
155162306a36Sopenharmony_ci		iget_failed(inode);
155262306a36Sopenharmony_ci		inode = ERR_PTR(rc);
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ciout:
155662306a36Sopenharmony_ci	kfree(path);
155762306a36Sopenharmony_ci	free_xid(xid);
155862306a36Sopenharmony_ci	kfree(fattr.cf_symlink_target);
155962306a36Sopenharmony_ci	return inode;
156062306a36Sopenharmony_ci}
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ciint
156362306a36Sopenharmony_cicifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
156462306a36Sopenharmony_ci		   const char *full_path, __u32 dosattr)
156562306a36Sopenharmony_ci{
156662306a36Sopenharmony_ci	bool set_time = false;
156762306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
156862306a36Sopenharmony_ci	struct TCP_Server_Info *server;
156962306a36Sopenharmony_ci	FILE_BASIC_INFO	info_buf;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	if (attrs == NULL)
157262306a36Sopenharmony_ci		return -EINVAL;
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	server = cifs_sb_master_tcon(cifs_sb)->ses->server;
157562306a36Sopenharmony_ci	if (!server->ops->set_file_info)
157662306a36Sopenharmony_ci		return -ENOSYS;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	info_buf.Pad = 0;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_ATIME) {
158162306a36Sopenharmony_ci		set_time = true;
158262306a36Sopenharmony_ci		info_buf.LastAccessTime =
158362306a36Sopenharmony_ci			cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
158462306a36Sopenharmony_ci	} else
158562306a36Sopenharmony_ci		info_buf.LastAccessTime = 0;
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_MTIME) {
158862306a36Sopenharmony_ci		set_time = true;
158962306a36Sopenharmony_ci		info_buf.LastWriteTime =
159062306a36Sopenharmony_ci		    cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
159162306a36Sopenharmony_ci	} else
159262306a36Sopenharmony_ci		info_buf.LastWriteTime = 0;
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_ci	/*
159562306a36Sopenharmony_ci	 * Samba throws this field away, but windows may actually use it.
159662306a36Sopenharmony_ci	 * Do not set ctime unless other time stamps are changed explicitly
159762306a36Sopenharmony_ci	 * (i.e. by utimes()) since we would then have a mix of client and
159862306a36Sopenharmony_ci	 * server times.
159962306a36Sopenharmony_ci	 */
160062306a36Sopenharmony_ci	if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
160162306a36Sopenharmony_ci		cifs_dbg(FYI, "CIFS - CTIME changed\n");
160262306a36Sopenharmony_ci		info_buf.ChangeTime =
160362306a36Sopenharmony_ci		    cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
160462306a36Sopenharmony_ci	} else
160562306a36Sopenharmony_ci		info_buf.ChangeTime = 0;
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci	info_buf.CreationTime = 0;	/* don't change */
160862306a36Sopenharmony_ci	info_buf.Attributes = cpu_to_le32(dosattr);
160962306a36Sopenharmony_ci
161062306a36Sopenharmony_ci	return server->ops->set_file_info(inode, full_path, &info_buf, xid);
161162306a36Sopenharmony_ci}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
161462306a36Sopenharmony_ci/*
161562306a36Sopenharmony_ci * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
161662306a36Sopenharmony_ci * and rename it to a random name that hopefully won't conflict with
161762306a36Sopenharmony_ci * anything else.
161862306a36Sopenharmony_ci */
161962306a36Sopenharmony_ciint
162062306a36Sopenharmony_cicifs_rename_pending_delete(const char *full_path, struct dentry *dentry,
162162306a36Sopenharmony_ci			   const unsigned int xid)
162262306a36Sopenharmony_ci{
162362306a36Sopenharmony_ci	int oplock = 0;
162462306a36Sopenharmony_ci	int rc;
162562306a36Sopenharmony_ci	struct cifs_fid fid;
162662306a36Sopenharmony_ci	struct cifs_open_parms oparms;
162762306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
162862306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInode = CIFS_I(inode);
162962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
163062306a36Sopenharmony_ci	struct tcon_link *tlink;
163162306a36Sopenharmony_ci	struct cifs_tcon *tcon;
163262306a36Sopenharmony_ci	__u32 dosattr, origattr;
163362306a36Sopenharmony_ci	FILE_BASIC_INFO *info_buf = NULL;
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
163662306a36Sopenharmony_ci	if (IS_ERR(tlink))
163762306a36Sopenharmony_ci		return PTR_ERR(tlink);
163862306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_ci	/*
164162306a36Sopenharmony_ci	 * We cannot rename the file if the server doesn't support
164262306a36Sopenharmony_ci	 * CAP_INFOLEVEL_PASSTHRU
164362306a36Sopenharmony_ci	 */
164462306a36Sopenharmony_ci	if (!(tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)) {
164562306a36Sopenharmony_ci		rc = -EBUSY;
164662306a36Sopenharmony_ci		goto out;
164762306a36Sopenharmony_ci	}
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci	oparms = (struct cifs_open_parms) {
165062306a36Sopenharmony_ci		.tcon = tcon,
165162306a36Sopenharmony_ci		.cifs_sb = cifs_sb,
165262306a36Sopenharmony_ci		.desired_access = DELETE | FILE_WRITE_ATTRIBUTES,
165362306a36Sopenharmony_ci		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
165462306a36Sopenharmony_ci		.disposition = FILE_OPEN,
165562306a36Sopenharmony_ci		.path = full_path,
165662306a36Sopenharmony_ci		.fid = &fid,
165762306a36Sopenharmony_ci	};
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci	rc = CIFS_open(xid, &oparms, &oplock, NULL);
166062306a36Sopenharmony_ci	if (rc != 0)
166162306a36Sopenharmony_ci		goto out;
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	origattr = cifsInode->cifsAttrs;
166462306a36Sopenharmony_ci	if (origattr == 0)
166562306a36Sopenharmony_ci		origattr |= ATTR_NORMAL;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	dosattr = origattr & ~ATTR_READONLY;
166862306a36Sopenharmony_ci	if (dosattr == 0)
166962306a36Sopenharmony_ci		dosattr |= ATTR_NORMAL;
167062306a36Sopenharmony_ci	dosattr |= ATTR_HIDDEN;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	/* set ATTR_HIDDEN and clear ATTR_READONLY, but only if needed */
167362306a36Sopenharmony_ci	if (dosattr != origattr) {
167462306a36Sopenharmony_ci		info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
167562306a36Sopenharmony_ci		if (info_buf == NULL) {
167662306a36Sopenharmony_ci			rc = -ENOMEM;
167762306a36Sopenharmony_ci			goto out_close;
167862306a36Sopenharmony_ci		}
167962306a36Sopenharmony_ci		info_buf->Attributes = cpu_to_le32(dosattr);
168062306a36Sopenharmony_ci		rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
168162306a36Sopenharmony_ci					current->tgid);
168262306a36Sopenharmony_ci		/* although we would like to mark the file hidden
168362306a36Sopenharmony_ci 		   if that fails we will still try to rename it */
168462306a36Sopenharmony_ci		if (!rc)
168562306a36Sopenharmony_ci			cifsInode->cifsAttrs = dosattr;
168662306a36Sopenharmony_ci		else
168762306a36Sopenharmony_ci			dosattr = origattr; /* since not able to change them */
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	/* rename the file */
169162306a36Sopenharmony_ci	rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL,
169262306a36Sopenharmony_ci				   cifs_sb->local_nls,
169362306a36Sopenharmony_ci				   cifs_remap(cifs_sb));
169462306a36Sopenharmony_ci	if (rc != 0) {
169562306a36Sopenharmony_ci		rc = -EBUSY;
169662306a36Sopenharmony_ci		goto undo_setattr;
169762306a36Sopenharmony_ci	}
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	/* try to set DELETE_ON_CLOSE */
170062306a36Sopenharmony_ci	if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) {
170162306a36Sopenharmony_ci		rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid,
170262306a36Sopenharmony_ci					       current->tgid);
170362306a36Sopenharmony_ci		/*
170462306a36Sopenharmony_ci		 * some samba versions return -ENOENT when we try to set the
170562306a36Sopenharmony_ci		 * file disposition here. Likely a samba bug, but work around
170662306a36Sopenharmony_ci		 * it for now. This means that some cifsXXX files may hang
170762306a36Sopenharmony_ci		 * around after they shouldn't.
170862306a36Sopenharmony_ci		 *
170962306a36Sopenharmony_ci		 * BB: remove this hack after more servers have the fix
171062306a36Sopenharmony_ci		 */
171162306a36Sopenharmony_ci		if (rc == -ENOENT)
171262306a36Sopenharmony_ci			rc = 0;
171362306a36Sopenharmony_ci		else if (rc != 0) {
171462306a36Sopenharmony_ci			rc = -EBUSY;
171562306a36Sopenharmony_ci			goto undo_rename;
171662306a36Sopenharmony_ci		}
171762306a36Sopenharmony_ci		set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);
171862306a36Sopenharmony_ci	}
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ciout_close:
172162306a36Sopenharmony_ci	CIFSSMBClose(xid, tcon, fid.netfid);
172262306a36Sopenharmony_ciout:
172362306a36Sopenharmony_ci	kfree(info_buf);
172462306a36Sopenharmony_ci	cifs_put_tlink(tlink);
172562306a36Sopenharmony_ci	return rc;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	/*
172862306a36Sopenharmony_ci	 * reset everything back to the original state. Don't bother
172962306a36Sopenharmony_ci	 * dealing with errors here since we can't do anything about
173062306a36Sopenharmony_ci	 * them anyway.
173162306a36Sopenharmony_ci	 */
173262306a36Sopenharmony_ciundo_rename:
173362306a36Sopenharmony_ci	CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name,
173462306a36Sopenharmony_ci				cifs_sb->local_nls, cifs_remap(cifs_sb));
173562306a36Sopenharmony_ciundo_setattr:
173662306a36Sopenharmony_ci	if (dosattr != origattr) {
173762306a36Sopenharmony_ci		info_buf->Attributes = cpu_to_le32(origattr);
173862306a36Sopenharmony_ci		if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,
173962306a36Sopenharmony_ci					current->tgid))
174062306a36Sopenharmony_ci			cifsInode->cifsAttrs = origattr;
174162306a36Sopenharmony_ci	}
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	goto out_close;
174462306a36Sopenharmony_ci}
174562306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci/* copied from fs/nfs/dir.c with small changes */
174862306a36Sopenharmony_cistatic void
174962306a36Sopenharmony_cicifs_drop_nlink(struct inode *inode)
175062306a36Sopenharmony_ci{
175162306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
175262306a36Sopenharmony_ci	if (inode->i_nlink > 0)
175362306a36Sopenharmony_ci		drop_nlink(inode);
175462306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
175562306a36Sopenharmony_ci}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci/*
175862306a36Sopenharmony_ci * If d_inode(dentry) is null (usually meaning the cached dentry
175962306a36Sopenharmony_ci * is a negative dentry) then we would attempt a standard SMB delete, but
176062306a36Sopenharmony_ci * if that fails we can not attempt the fall back mechanisms on EACCES
176162306a36Sopenharmony_ci * but will return the EACCES to the caller. Note that the VFS does not call
176262306a36Sopenharmony_ci * unlink on negative dentries currently.
176362306a36Sopenharmony_ci */
176462306a36Sopenharmony_ciint cifs_unlink(struct inode *dir, struct dentry *dentry)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	int rc = 0;
176762306a36Sopenharmony_ci	unsigned int xid;
176862306a36Sopenharmony_ci	const char *full_path;
176962306a36Sopenharmony_ci	void *page;
177062306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
177162306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_inode;
177262306a36Sopenharmony_ci	struct super_block *sb = dir->i_sb;
177362306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
177462306a36Sopenharmony_ci	struct tcon_link *tlink;
177562306a36Sopenharmony_ci	struct cifs_tcon *tcon;
177662306a36Sopenharmony_ci	struct TCP_Server_Info *server;
177762306a36Sopenharmony_ci	struct iattr *attrs = NULL;
177862306a36Sopenharmony_ci	__u32 dosattr = 0, origattr = 0;
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	cifs_dbg(FYI, "cifs_unlink, dir=0x%p, dentry=0x%p\n", dir, dentry);
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
178362306a36Sopenharmony_ci		return -EIO;
178462306a36Sopenharmony_ci
178562306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
178662306a36Sopenharmony_ci	if (IS_ERR(tlink))
178762306a36Sopenharmony_ci		return PTR_ERR(tlink);
178862306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
178962306a36Sopenharmony_ci	server = tcon->ses->server;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	xid = get_xid();
179262306a36Sopenharmony_ci	page = alloc_dentry_path();
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci	if (tcon->nodelete) {
179562306a36Sopenharmony_ci		rc = -EACCES;
179662306a36Sopenharmony_ci		goto unlink_out;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	/* Unlink can be called from rename so we can not take the
180062306a36Sopenharmony_ci	 * sb->s_vfs_rename_mutex here */
180162306a36Sopenharmony_ci	full_path = build_path_from_dentry(dentry, page);
180262306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
180362306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
180462306a36Sopenharmony_ci		goto unlink_out;
180562306a36Sopenharmony_ci	}
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	cifs_close_deferred_file_under_dentry(tcon, full_path);
180862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
180962306a36Sopenharmony_ci	if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
181062306a36Sopenharmony_ci				le64_to_cpu(tcon->fsUnixInfo.Capability))) {
181162306a36Sopenharmony_ci		rc = CIFSPOSIXDelFile(xid, tcon, full_path,
181262306a36Sopenharmony_ci			SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
181362306a36Sopenharmony_ci			cifs_remap(cifs_sb));
181462306a36Sopenharmony_ci		cifs_dbg(FYI, "posix del rc %d\n", rc);
181562306a36Sopenharmony_ci		if ((rc == 0) || (rc == -ENOENT))
181662306a36Sopenharmony_ci			goto psx_del_no_retry;
181762306a36Sopenharmony_ci	}
181862306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ciretry_std_delete:
182162306a36Sopenharmony_ci	if (!server->ops->unlink) {
182262306a36Sopenharmony_ci		rc = -ENOSYS;
182362306a36Sopenharmony_ci		goto psx_del_no_retry;
182462306a36Sopenharmony_ci	}
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_cipsx_del_no_retry:
182962306a36Sopenharmony_ci	if (!rc) {
183062306a36Sopenharmony_ci		if (inode)
183162306a36Sopenharmony_ci			cifs_drop_nlink(inode);
183262306a36Sopenharmony_ci	} else if (rc == -ENOENT) {
183362306a36Sopenharmony_ci		d_drop(dentry);
183462306a36Sopenharmony_ci	} else if (rc == -EBUSY) {
183562306a36Sopenharmony_ci		if (server->ops->rename_pending_delete) {
183662306a36Sopenharmony_ci			rc = server->ops->rename_pending_delete(full_path,
183762306a36Sopenharmony_ci								dentry, xid);
183862306a36Sopenharmony_ci			if (rc == 0)
183962306a36Sopenharmony_ci				cifs_drop_nlink(inode);
184062306a36Sopenharmony_ci		}
184162306a36Sopenharmony_ci	} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
184262306a36Sopenharmony_ci		attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
184362306a36Sopenharmony_ci		if (attrs == NULL) {
184462306a36Sopenharmony_ci			rc = -ENOMEM;
184562306a36Sopenharmony_ci			goto out_reval;
184662306a36Sopenharmony_ci		}
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci		/* try to reset dos attributes */
184962306a36Sopenharmony_ci		cifs_inode = CIFS_I(inode);
185062306a36Sopenharmony_ci		origattr = cifs_inode->cifsAttrs;
185162306a36Sopenharmony_ci		if (origattr == 0)
185262306a36Sopenharmony_ci			origattr |= ATTR_NORMAL;
185362306a36Sopenharmony_ci		dosattr = origattr & ~ATTR_READONLY;
185462306a36Sopenharmony_ci		if (dosattr == 0)
185562306a36Sopenharmony_ci			dosattr |= ATTR_NORMAL;
185662306a36Sopenharmony_ci		dosattr |= ATTR_HIDDEN;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci		rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
185962306a36Sopenharmony_ci		if (rc != 0)
186062306a36Sopenharmony_ci			goto out_reval;
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci		goto retry_std_delete;
186362306a36Sopenharmony_ci	}
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	/* undo the setattr if we errored out and it's needed */
186662306a36Sopenharmony_ci	if (rc != 0 && dosattr != 0)
186762306a36Sopenharmony_ci		cifs_set_file_info(inode, attrs, xid, full_path, origattr);
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ciout_reval:
187062306a36Sopenharmony_ci	if (inode) {
187162306a36Sopenharmony_ci		cifs_inode = CIFS_I(inode);
187262306a36Sopenharmony_ci		cifs_inode->time = 0;	/* will force revalidate to get info
187362306a36Sopenharmony_ci					   when needed */
187462306a36Sopenharmony_ci		inode_set_ctime_current(inode);
187562306a36Sopenharmony_ci	}
187662306a36Sopenharmony_ci	inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
187762306a36Sopenharmony_ci	cifs_inode = CIFS_I(dir);
187862306a36Sopenharmony_ci	CIFS_I(dir)->time = 0;	/* force revalidate of dir as well */
187962306a36Sopenharmony_ciunlink_out:
188062306a36Sopenharmony_ci	free_dentry_path(page);
188162306a36Sopenharmony_ci	kfree(attrs);
188262306a36Sopenharmony_ci	free_xid(xid);
188362306a36Sopenharmony_ci	cifs_put_tlink(tlink);
188462306a36Sopenharmony_ci	return rc;
188562306a36Sopenharmony_ci}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_cistatic int
188862306a36Sopenharmony_cicifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
188962306a36Sopenharmony_ci		 const char *full_path, struct cifs_sb_info *cifs_sb,
189062306a36Sopenharmony_ci		 struct cifs_tcon *tcon, const unsigned int xid)
189162306a36Sopenharmony_ci{
189262306a36Sopenharmony_ci	int rc = 0;
189362306a36Sopenharmony_ci	struct inode *inode = NULL;
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	if (tcon->posix_extensions)
189662306a36Sopenharmony_ci		rc = smb311_posix_get_inode_info(&inode, full_path, parent->i_sb, xid);
189762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
189862306a36Sopenharmony_ci	else if (tcon->unix_ext)
189962306a36Sopenharmony_ci		rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
190062306a36Sopenharmony_ci					      xid);
190162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
190262306a36Sopenharmony_ci	else
190362306a36Sopenharmony_ci		rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
190462306a36Sopenharmony_ci					 xid, NULL);
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci	if (rc)
190762306a36Sopenharmony_ci		return rc;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	if (!S_ISDIR(inode->i_mode)) {
191062306a36Sopenharmony_ci		/*
191162306a36Sopenharmony_ci		 * mkdir succeeded, but another client has managed to remove the
191262306a36Sopenharmony_ci		 * sucker and replace it with non-directory.  Return success,
191362306a36Sopenharmony_ci		 * but don't leave the child in dcache.
191462306a36Sopenharmony_ci		 */
191562306a36Sopenharmony_ci		 iput(inode);
191662306a36Sopenharmony_ci		 d_drop(dentry);
191762306a36Sopenharmony_ci		 return 0;
191862306a36Sopenharmony_ci	}
191962306a36Sopenharmony_ci	/*
192062306a36Sopenharmony_ci	 * setting nlink not necessary except in cases where we failed to get it
192162306a36Sopenharmony_ci	 * from the server or was set bogus. Also, since this is a brand new
192262306a36Sopenharmony_ci	 * inode, no need to grab the i_lock before setting the i_nlink.
192362306a36Sopenharmony_ci	 */
192462306a36Sopenharmony_ci	if (inode->i_nlink < 2)
192562306a36Sopenharmony_ci		set_nlink(inode, 2);
192662306a36Sopenharmony_ci	mode &= ~current_umask();
192762306a36Sopenharmony_ci	/* must turn on setgid bit if parent dir has it */
192862306a36Sopenharmony_ci	if (parent->i_mode & S_ISGID)
192962306a36Sopenharmony_ci		mode |= S_ISGID;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
193262306a36Sopenharmony_ci	if (tcon->unix_ext) {
193362306a36Sopenharmony_ci		struct cifs_unix_set_info_args args = {
193462306a36Sopenharmony_ci			.mode	= mode,
193562306a36Sopenharmony_ci			.ctime	= NO_CHANGE_64,
193662306a36Sopenharmony_ci			.atime	= NO_CHANGE_64,
193762306a36Sopenharmony_ci			.mtime	= NO_CHANGE_64,
193862306a36Sopenharmony_ci			.device	= 0,
193962306a36Sopenharmony_ci		};
194062306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
194162306a36Sopenharmony_ci			args.uid = current_fsuid();
194262306a36Sopenharmony_ci			if (parent->i_mode & S_ISGID)
194362306a36Sopenharmony_ci				args.gid = parent->i_gid;
194462306a36Sopenharmony_ci			else
194562306a36Sopenharmony_ci				args.gid = current_fsgid();
194662306a36Sopenharmony_ci		} else {
194762306a36Sopenharmony_ci			args.uid = INVALID_UID; /* no change */
194862306a36Sopenharmony_ci			args.gid = INVALID_GID; /* no change */
194962306a36Sopenharmony_ci		}
195062306a36Sopenharmony_ci		CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,
195162306a36Sopenharmony_ci				       cifs_sb->local_nls,
195262306a36Sopenharmony_ci				       cifs_remap(cifs_sb));
195362306a36Sopenharmony_ci	} else {
195462306a36Sopenharmony_ci#else
195562306a36Sopenharmony_ci	{
195662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
195762306a36Sopenharmony_ci		struct TCP_Server_Info *server = tcon->ses->server;
195862306a36Sopenharmony_ci		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
195962306a36Sopenharmony_ci		    (mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
196062306a36Sopenharmony_ci			server->ops->mkdir_setinfo(inode, full_path, cifs_sb,
196162306a36Sopenharmony_ci						   tcon, xid);
196262306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
196362306a36Sopenharmony_ci			inode->i_mode = (mode | S_IFDIR);
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
196662306a36Sopenharmony_ci			inode->i_uid = current_fsuid();
196762306a36Sopenharmony_ci			if (inode->i_mode & S_ISGID)
196862306a36Sopenharmony_ci				inode->i_gid = parent->i_gid;
196962306a36Sopenharmony_ci			else
197062306a36Sopenharmony_ci				inode->i_gid = current_fsgid();
197162306a36Sopenharmony_ci		}
197262306a36Sopenharmony_ci	}
197362306a36Sopenharmony_ci	d_instantiate(dentry, inode);
197462306a36Sopenharmony_ci	return 0;
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
197862306a36Sopenharmony_cistatic int
197962306a36Sopenharmony_cicifs_posix_mkdir(struct inode *inode, struct dentry *dentry, umode_t mode,
198062306a36Sopenharmony_ci		 const char *full_path, struct cifs_sb_info *cifs_sb,
198162306a36Sopenharmony_ci		 struct cifs_tcon *tcon, const unsigned int xid)
198262306a36Sopenharmony_ci{
198362306a36Sopenharmony_ci	int rc = 0;
198462306a36Sopenharmony_ci	u32 oplock = 0;
198562306a36Sopenharmony_ci	FILE_UNIX_BASIC_INFO *info = NULL;
198662306a36Sopenharmony_ci	struct inode *newinode = NULL;
198762306a36Sopenharmony_ci	struct cifs_fattr fattr;
198862306a36Sopenharmony_ci
198962306a36Sopenharmony_ci	info = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
199062306a36Sopenharmony_ci	if (info == NULL) {
199162306a36Sopenharmony_ci		rc = -ENOMEM;
199262306a36Sopenharmony_ci		goto posix_mkdir_out;
199362306a36Sopenharmony_ci	}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	mode &= ~current_umask();
199662306a36Sopenharmony_ci	rc = CIFSPOSIXCreate(xid, tcon, SMB_O_DIRECTORY | SMB_O_CREAT, mode,
199762306a36Sopenharmony_ci			     NULL /* netfid */, info, &oplock, full_path,
199862306a36Sopenharmony_ci			     cifs_sb->local_nls, cifs_remap(cifs_sb));
199962306a36Sopenharmony_ci	if (rc == -EOPNOTSUPP)
200062306a36Sopenharmony_ci		goto posix_mkdir_out;
200162306a36Sopenharmony_ci	else if (rc) {
200262306a36Sopenharmony_ci		cifs_dbg(FYI, "posix mkdir returned 0x%x\n", rc);
200362306a36Sopenharmony_ci		d_drop(dentry);
200462306a36Sopenharmony_ci		goto posix_mkdir_out;
200562306a36Sopenharmony_ci	}
200662306a36Sopenharmony_ci
200762306a36Sopenharmony_ci	if (info->Type == cpu_to_le32(-1))
200862306a36Sopenharmony_ci		/* no return info, go query for it */
200962306a36Sopenharmony_ci		goto posix_mkdir_get_info;
201062306a36Sopenharmony_ci	/*
201162306a36Sopenharmony_ci	 * BB check (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID ) to see if
201262306a36Sopenharmony_ci	 * need to set uid/gid.
201362306a36Sopenharmony_ci	 */
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	cifs_unix_basic_to_fattr(&fattr, info, cifs_sb);
201662306a36Sopenharmony_ci	cifs_fill_uniqueid(inode->i_sb, &fattr);
201762306a36Sopenharmony_ci	newinode = cifs_iget(inode->i_sb, &fattr);
201862306a36Sopenharmony_ci	if (!newinode)
201962306a36Sopenharmony_ci		goto posix_mkdir_get_info;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	d_instantiate(dentry, newinode);
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
202462306a36Sopenharmony_ci	cifs_dbg(FYI, "instantiated dentry %p %pd to inode %p\n",
202562306a36Sopenharmony_ci		 dentry, dentry, newinode);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	if (newinode->i_nlink != 2)
202862306a36Sopenharmony_ci		cifs_dbg(FYI, "unexpected number of links %d\n",
202962306a36Sopenharmony_ci			 newinode->i_nlink);
203062306a36Sopenharmony_ci#endif
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ciposix_mkdir_out:
203362306a36Sopenharmony_ci	kfree(info);
203462306a36Sopenharmony_ci	return rc;
203562306a36Sopenharmony_ciposix_mkdir_get_info:
203662306a36Sopenharmony_ci	rc = cifs_mkdir_qinfo(inode, dentry, mode, full_path, cifs_sb, tcon,
203762306a36Sopenharmony_ci			      xid);
203862306a36Sopenharmony_ci	goto posix_mkdir_out;
203962306a36Sopenharmony_ci}
204062306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ciint cifs_mkdir(struct mnt_idmap *idmap, struct inode *inode,
204362306a36Sopenharmony_ci	       struct dentry *direntry, umode_t mode)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	int rc = 0;
204662306a36Sopenharmony_ci	unsigned int xid;
204762306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
204862306a36Sopenharmony_ci	struct tcon_link *tlink;
204962306a36Sopenharmony_ci	struct cifs_tcon *tcon;
205062306a36Sopenharmony_ci	struct TCP_Server_Info *server;
205162306a36Sopenharmony_ci	const char *full_path;
205262306a36Sopenharmony_ci	void *page;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	cifs_dbg(FYI, "In cifs_mkdir, mode = %04ho inode = 0x%p\n",
205562306a36Sopenharmony_ci		 mode, inode);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	cifs_sb = CIFS_SB(inode->i_sb);
205862306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
205962306a36Sopenharmony_ci		return -EIO;
206062306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
206162306a36Sopenharmony_ci	if (IS_ERR(tlink))
206262306a36Sopenharmony_ci		return PTR_ERR(tlink);
206362306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	xid = get_xid();
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	page = alloc_dentry_path();
206862306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
206962306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
207062306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
207162306a36Sopenharmony_ci		goto mkdir_out;
207262306a36Sopenharmony_ci	}
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	server = tcon->ses->server;
207562306a36Sopenharmony_ci
207662306a36Sopenharmony_ci	if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) {
207762306a36Sopenharmony_ci		rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path,
207862306a36Sopenharmony_ci					      cifs_sb);
207962306a36Sopenharmony_ci		d_drop(direntry); /* for time being always refresh inode info */
208062306a36Sopenharmony_ci		goto mkdir_out;
208162306a36Sopenharmony_ci	}
208262306a36Sopenharmony_ci
208362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
208462306a36Sopenharmony_ci	if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
208562306a36Sopenharmony_ci				le64_to_cpu(tcon->fsUnixInfo.Capability))) {
208662306a36Sopenharmony_ci		rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb,
208762306a36Sopenharmony_ci				      tcon, xid);
208862306a36Sopenharmony_ci		if (rc != -EOPNOTSUPP)
208962306a36Sopenharmony_ci			goto mkdir_out;
209062306a36Sopenharmony_ci	}
209162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
209262306a36Sopenharmony_ci
209362306a36Sopenharmony_ci	if (!server->ops->mkdir) {
209462306a36Sopenharmony_ci		rc = -ENOSYS;
209562306a36Sopenharmony_ci		goto mkdir_out;
209662306a36Sopenharmony_ci	}
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	/* BB add setting the equivalent of mode via CreateX w/ACLs */
209962306a36Sopenharmony_ci	rc = server->ops->mkdir(xid, inode, mode, tcon, full_path, cifs_sb);
210062306a36Sopenharmony_ci	if (rc) {
210162306a36Sopenharmony_ci		cifs_dbg(FYI, "cifs_mkdir returned 0x%x\n", rc);
210262306a36Sopenharmony_ci		d_drop(direntry);
210362306a36Sopenharmony_ci		goto mkdir_out;
210462306a36Sopenharmony_ci	}
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	/* TODO: skip this for smb2/smb3 */
210762306a36Sopenharmony_ci	rc = cifs_mkdir_qinfo(inode, direntry, mode, full_path, cifs_sb, tcon,
210862306a36Sopenharmony_ci			      xid);
210962306a36Sopenharmony_cimkdir_out:
211062306a36Sopenharmony_ci	/*
211162306a36Sopenharmony_ci	 * Force revalidate to get parent dir info when needed since cached
211262306a36Sopenharmony_ci	 * attributes are invalid now.
211362306a36Sopenharmony_ci	 */
211462306a36Sopenharmony_ci	CIFS_I(inode)->time = 0;
211562306a36Sopenharmony_ci	free_dentry_path(page);
211662306a36Sopenharmony_ci	free_xid(xid);
211762306a36Sopenharmony_ci	cifs_put_tlink(tlink);
211862306a36Sopenharmony_ci	return rc;
211962306a36Sopenharmony_ci}
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ciint cifs_rmdir(struct inode *inode, struct dentry *direntry)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	int rc = 0;
212462306a36Sopenharmony_ci	unsigned int xid;
212562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
212662306a36Sopenharmony_ci	struct tcon_link *tlink;
212762306a36Sopenharmony_ci	struct cifs_tcon *tcon;
212862306a36Sopenharmony_ci	struct TCP_Server_Info *server;
212962306a36Sopenharmony_ci	const char *full_path;
213062306a36Sopenharmony_ci	void *page = alloc_dentry_path();
213162306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInode;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci	cifs_dbg(FYI, "cifs_rmdir, inode = 0x%p\n", inode);
213462306a36Sopenharmony_ci
213562306a36Sopenharmony_ci	xid = get_xid();
213662306a36Sopenharmony_ci
213762306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
213862306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
213962306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
214062306a36Sopenharmony_ci		goto rmdir_exit;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	cifs_sb = CIFS_SB(inode->i_sb);
214462306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb))) {
214562306a36Sopenharmony_ci		rc = -EIO;
214662306a36Sopenharmony_ci		goto rmdir_exit;
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci
214962306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
215062306a36Sopenharmony_ci	if (IS_ERR(tlink)) {
215162306a36Sopenharmony_ci		rc = PTR_ERR(tlink);
215262306a36Sopenharmony_ci		goto rmdir_exit;
215362306a36Sopenharmony_ci	}
215462306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
215562306a36Sopenharmony_ci	server = tcon->ses->server;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	if (!server->ops->rmdir) {
215862306a36Sopenharmony_ci		rc = -ENOSYS;
215962306a36Sopenharmony_ci		cifs_put_tlink(tlink);
216062306a36Sopenharmony_ci		goto rmdir_exit;
216162306a36Sopenharmony_ci	}
216262306a36Sopenharmony_ci
216362306a36Sopenharmony_ci	if (tcon->nodelete) {
216462306a36Sopenharmony_ci		rc = -EACCES;
216562306a36Sopenharmony_ci		cifs_put_tlink(tlink);
216662306a36Sopenharmony_ci		goto rmdir_exit;
216762306a36Sopenharmony_ci	}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	rc = server->ops->rmdir(xid, tcon, full_path, cifs_sb);
217062306a36Sopenharmony_ci	cifs_put_tlink(tlink);
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	if (!rc) {
217362306a36Sopenharmony_ci		spin_lock(&d_inode(direntry)->i_lock);
217462306a36Sopenharmony_ci		i_size_write(d_inode(direntry), 0);
217562306a36Sopenharmony_ci		clear_nlink(d_inode(direntry));
217662306a36Sopenharmony_ci		spin_unlock(&d_inode(direntry)->i_lock);
217762306a36Sopenharmony_ci	}
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	cifsInode = CIFS_I(d_inode(direntry));
218062306a36Sopenharmony_ci	/* force revalidate to go get info when needed */
218162306a36Sopenharmony_ci	cifsInode->time = 0;
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci	cifsInode = CIFS_I(inode);
218462306a36Sopenharmony_ci	/*
218562306a36Sopenharmony_ci	 * Force revalidate to get parent dir info when needed since cached
218662306a36Sopenharmony_ci	 * attributes are invalid now.
218762306a36Sopenharmony_ci	 */
218862306a36Sopenharmony_ci	cifsInode->time = 0;
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci	inode_set_ctime_current(d_inode(direntry));
219162306a36Sopenharmony_ci	inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_cirmdir_exit:
219462306a36Sopenharmony_ci	free_dentry_path(page);
219562306a36Sopenharmony_ci	free_xid(xid);
219662306a36Sopenharmony_ci	return rc;
219762306a36Sopenharmony_ci}
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_cistatic int
220062306a36Sopenharmony_cicifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
220162306a36Sopenharmony_ci	       const char *from_path, struct dentry *to_dentry,
220262306a36Sopenharmony_ci	       const char *to_path)
220362306a36Sopenharmony_ci{
220462306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
220562306a36Sopenharmony_ci	struct tcon_link *tlink;
220662306a36Sopenharmony_ci	struct cifs_tcon *tcon;
220762306a36Sopenharmony_ci	struct TCP_Server_Info *server;
220862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
220962306a36Sopenharmony_ci	struct cifs_fid fid;
221062306a36Sopenharmony_ci	struct cifs_open_parms oparms;
221162306a36Sopenharmony_ci	int oplock;
221262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
221362306a36Sopenharmony_ci	int rc;
221462306a36Sopenharmony_ci
221562306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
221662306a36Sopenharmony_ci	if (IS_ERR(tlink))
221762306a36Sopenharmony_ci		return PTR_ERR(tlink);
221862306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
221962306a36Sopenharmony_ci	server = tcon->ses->server;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	if (!server->ops->rename)
222262306a36Sopenharmony_ci		return -ENOSYS;
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	/* try path-based rename first */
222562306a36Sopenharmony_ci	rc = server->ops->rename(xid, tcon, from_dentry,
222662306a36Sopenharmony_ci				 from_path, to_path, cifs_sb);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_ci	/*
222962306a36Sopenharmony_ci	 * Don't bother with rename by filehandle unless file is busy and
223062306a36Sopenharmony_ci	 * source. Note that cross directory moves do not work with
223162306a36Sopenharmony_ci	 * rename by filehandle to various Windows servers.
223262306a36Sopenharmony_ci	 */
223362306a36Sopenharmony_ci	if (rc == 0 || rc != -EBUSY)
223462306a36Sopenharmony_ci		goto do_rename_exit;
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	/* Don't fall back to using SMB on SMB 2+ mount */
223762306a36Sopenharmony_ci	if (server->vals->protocol_id != 0)
223862306a36Sopenharmony_ci		goto do_rename_exit;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
224162306a36Sopenharmony_ci	/* open-file renames don't work across directories */
224262306a36Sopenharmony_ci	if (to_dentry->d_parent != from_dentry->d_parent)
224362306a36Sopenharmony_ci		goto do_rename_exit;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	oparms = (struct cifs_open_parms) {
224662306a36Sopenharmony_ci		.tcon = tcon,
224762306a36Sopenharmony_ci		.cifs_sb = cifs_sb,
224862306a36Sopenharmony_ci		/* open the file to be renamed -- we need DELETE perms */
224962306a36Sopenharmony_ci		.desired_access = DELETE,
225062306a36Sopenharmony_ci		.create_options = cifs_create_options(cifs_sb, CREATE_NOT_DIR),
225162306a36Sopenharmony_ci		.disposition = FILE_OPEN,
225262306a36Sopenharmony_ci		.path = from_path,
225362306a36Sopenharmony_ci		.fid = &fid,
225462306a36Sopenharmony_ci	};
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	rc = CIFS_open(xid, &oparms, &oplock, NULL);
225762306a36Sopenharmony_ci	if (rc == 0) {
225862306a36Sopenharmony_ci		rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid,
225962306a36Sopenharmony_ci				(const char *) to_dentry->d_name.name,
226062306a36Sopenharmony_ci				cifs_sb->local_nls, cifs_remap(cifs_sb));
226162306a36Sopenharmony_ci		CIFSSMBClose(xid, tcon, fid.netfid);
226262306a36Sopenharmony_ci	}
226362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
226462306a36Sopenharmony_cido_rename_exit:
226562306a36Sopenharmony_ci	if (rc == 0)
226662306a36Sopenharmony_ci		d_move(from_dentry, to_dentry);
226762306a36Sopenharmony_ci	cifs_put_tlink(tlink);
226862306a36Sopenharmony_ci	return rc;
226962306a36Sopenharmony_ci}
227062306a36Sopenharmony_ci
227162306a36Sopenharmony_ciint
227262306a36Sopenharmony_cicifs_rename2(struct mnt_idmap *idmap, struct inode *source_dir,
227362306a36Sopenharmony_ci	     struct dentry *source_dentry, struct inode *target_dir,
227462306a36Sopenharmony_ci	     struct dentry *target_dentry, unsigned int flags)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	const char *from_name, *to_name;
227762306a36Sopenharmony_ci	void *page1, *page2;
227862306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
227962306a36Sopenharmony_ci	struct tcon_link *tlink;
228062306a36Sopenharmony_ci	struct cifs_tcon *tcon;
228162306a36Sopenharmony_ci	unsigned int xid;
228262306a36Sopenharmony_ci	int rc, tmprc;
228362306a36Sopenharmony_ci	int retry_count = 0;
228462306a36Sopenharmony_ci	FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
228562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
228662306a36Sopenharmony_ci	FILE_UNIX_BASIC_INFO *info_buf_target;
228762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	if (flags & ~RENAME_NOREPLACE)
229062306a36Sopenharmony_ci		return -EINVAL;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	cifs_sb = CIFS_SB(source_dir->i_sb);
229362306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
229462306a36Sopenharmony_ci		return -EIO;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	tlink = cifs_sb_tlink(cifs_sb);
229762306a36Sopenharmony_ci	if (IS_ERR(tlink))
229862306a36Sopenharmony_ci		return PTR_ERR(tlink);
229962306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
230062306a36Sopenharmony_ci
230162306a36Sopenharmony_ci	page1 = alloc_dentry_path();
230262306a36Sopenharmony_ci	page2 = alloc_dentry_path();
230362306a36Sopenharmony_ci	xid = get_xid();
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	from_name = build_path_from_dentry(source_dentry, page1);
230662306a36Sopenharmony_ci	if (IS_ERR(from_name)) {
230762306a36Sopenharmony_ci		rc = PTR_ERR(from_name);
230862306a36Sopenharmony_ci		goto cifs_rename_exit;
230962306a36Sopenharmony_ci	}
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	to_name = build_path_from_dentry(target_dentry, page2);
231262306a36Sopenharmony_ci	if (IS_ERR(to_name)) {
231362306a36Sopenharmony_ci		rc = PTR_ERR(to_name);
231462306a36Sopenharmony_ci		goto cifs_rename_exit;
231562306a36Sopenharmony_ci	}
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	cifs_close_deferred_file_under_dentry(tcon, from_name);
231862306a36Sopenharmony_ci	if (d_inode(target_dentry) != NULL)
231962306a36Sopenharmony_ci		cifs_close_deferred_file_under_dentry(tcon, to_name);
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
232262306a36Sopenharmony_ci			    to_name);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	if (rc == -EACCES) {
232562306a36Sopenharmony_ci		while (retry_count < 3) {
232662306a36Sopenharmony_ci			cifs_close_all_deferred_files(tcon);
232762306a36Sopenharmony_ci			rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
232862306a36Sopenharmony_ci					    to_name);
232962306a36Sopenharmony_ci			if (rc != -EACCES)
233062306a36Sopenharmony_ci				break;
233162306a36Sopenharmony_ci			retry_count++;
233262306a36Sopenharmony_ci		}
233362306a36Sopenharmony_ci	}
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	/*
233662306a36Sopenharmony_ci	 * No-replace is the natural behavior for CIFS, so skip unlink hacks.
233762306a36Sopenharmony_ci	 */
233862306a36Sopenharmony_ci	if (flags & RENAME_NOREPLACE)
233962306a36Sopenharmony_ci		goto cifs_rename_exit;
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
234262306a36Sopenharmony_ci	if (rc == -EEXIST && tcon->unix_ext) {
234362306a36Sopenharmony_ci		/*
234462306a36Sopenharmony_ci		 * Are src and dst hardlinks of same inode? We can only tell
234562306a36Sopenharmony_ci		 * with unix extensions enabled.
234662306a36Sopenharmony_ci		 */
234762306a36Sopenharmony_ci		info_buf_source =
234862306a36Sopenharmony_ci			kmalloc_array(2, sizeof(FILE_UNIX_BASIC_INFO),
234962306a36Sopenharmony_ci					GFP_KERNEL);
235062306a36Sopenharmony_ci		if (info_buf_source == NULL) {
235162306a36Sopenharmony_ci			rc = -ENOMEM;
235262306a36Sopenharmony_ci			goto cifs_rename_exit;
235362306a36Sopenharmony_ci		}
235462306a36Sopenharmony_ci
235562306a36Sopenharmony_ci		info_buf_target = info_buf_source + 1;
235662306a36Sopenharmony_ci		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name,
235762306a36Sopenharmony_ci					     info_buf_source,
235862306a36Sopenharmony_ci					     cifs_sb->local_nls,
235962306a36Sopenharmony_ci					     cifs_remap(cifs_sb));
236062306a36Sopenharmony_ci		if (tmprc != 0)
236162306a36Sopenharmony_ci			goto unlink_target;
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci		tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
236462306a36Sopenharmony_ci					     info_buf_target,
236562306a36Sopenharmony_ci					     cifs_sb->local_nls,
236662306a36Sopenharmony_ci					     cifs_remap(cifs_sb));
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci		if (tmprc == 0 && (info_buf_source->UniqueId ==
236962306a36Sopenharmony_ci				   info_buf_target->UniqueId)) {
237062306a36Sopenharmony_ci			/* same file, POSIX says that this is a noop */
237162306a36Sopenharmony_ci			rc = 0;
237262306a36Sopenharmony_ci			goto cifs_rename_exit;
237362306a36Sopenharmony_ci		}
237462306a36Sopenharmony_ci	}
237562306a36Sopenharmony_ci	/*
237662306a36Sopenharmony_ci	 * else ... BB we could add the same check for Windows by
237762306a36Sopenharmony_ci	 * checking the UniqueId via FILE_INTERNAL_INFO
237862306a36Sopenharmony_ci	 */
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ciunlink_target:
238162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
238262306a36Sopenharmony_ci
238362306a36Sopenharmony_ci	/* Try unlinking the target dentry if it's not negative */
238462306a36Sopenharmony_ci	if (d_really_is_positive(target_dentry) && (rc == -EACCES || rc == -EEXIST)) {
238562306a36Sopenharmony_ci		if (d_is_dir(target_dentry))
238662306a36Sopenharmony_ci			tmprc = cifs_rmdir(target_dir, target_dentry);
238762306a36Sopenharmony_ci		else
238862306a36Sopenharmony_ci			tmprc = cifs_unlink(target_dir, target_dentry);
238962306a36Sopenharmony_ci		if (tmprc)
239062306a36Sopenharmony_ci			goto cifs_rename_exit;
239162306a36Sopenharmony_ci		rc = cifs_do_rename(xid, source_dentry, from_name,
239262306a36Sopenharmony_ci				    target_dentry, to_name);
239362306a36Sopenharmony_ci	}
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	/* force revalidate to go get info when needed */
239662306a36Sopenharmony_ci	CIFS_I(source_dir)->time = CIFS_I(target_dir)->time = 0;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_cicifs_rename_exit:
239962306a36Sopenharmony_ci	kfree(info_buf_source);
240062306a36Sopenharmony_ci	free_dentry_path(page2);
240162306a36Sopenharmony_ci	free_dentry_path(page1);
240262306a36Sopenharmony_ci	free_xid(xid);
240362306a36Sopenharmony_ci	cifs_put_tlink(tlink);
240462306a36Sopenharmony_ci	return rc;
240562306a36Sopenharmony_ci}
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_cistatic bool
240862306a36Sopenharmony_cicifs_dentry_needs_reval(struct dentry *dentry)
240962306a36Sopenharmony_ci{
241062306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
241162306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
241262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
241362306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
241462306a36Sopenharmony_ci	struct cached_fid *cfid = NULL;
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_ci	if (cifs_i->time == 0)
241762306a36Sopenharmony_ci		return true;
241862306a36Sopenharmony_ci
241962306a36Sopenharmony_ci	if (CIFS_CACHE_READ(cifs_i))
242062306a36Sopenharmony_ci		return false;
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci	if (!lookupCacheEnabled)
242362306a36Sopenharmony_ci		return true;
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci	if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
242662306a36Sopenharmony_ci		spin_lock(&cfid->fid_lock);
242762306a36Sopenharmony_ci		if (cfid->time && cifs_i->time > cfid->time) {
242862306a36Sopenharmony_ci			spin_unlock(&cfid->fid_lock);
242962306a36Sopenharmony_ci			close_cached_dir(cfid);
243062306a36Sopenharmony_ci			return false;
243162306a36Sopenharmony_ci		}
243262306a36Sopenharmony_ci		spin_unlock(&cfid->fid_lock);
243362306a36Sopenharmony_ci		close_cached_dir(cfid);
243462306a36Sopenharmony_ci	}
243562306a36Sopenharmony_ci	/*
243662306a36Sopenharmony_ci	 * depending on inode type, check if attribute caching disabled for
243762306a36Sopenharmony_ci	 * files or directories
243862306a36Sopenharmony_ci	 */
243962306a36Sopenharmony_ci	if (S_ISDIR(inode->i_mode)) {
244062306a36Sopenharmony_ci		if (!cifs_sb->ctx->acdirmax)
244162306a36Sopenharmony_ci			return true;
244262306a36Sopenharmony_ci		if (!time_in_range(jiffies, cifs_i->time,
244362306a36Sopenharmony_ci				   cifs_i->time + cifs_sb->ctx->acdirmax))
244462306a36Sopenharmony_ci			return true;
244562306a36Sopenharmony_ci	} else { /* file */
244662306a36Sopenharmony_ci		if (!cifs_sb->ctx->acregmax)
244762306a36Sopenharmony_ci			return true;
244862306a36Sopenharmony_ci		if (!time_in_range(jiffies, cifs_i->time,
244962306a36Sopenharmony_ci				   cifs_i->time + cifs_sb->ctx->acregmax))
245062306a36Sopenharmony_ci			return true;
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	/* hardlinked files w/ noserverino get "special" treatment */
245462306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) &&
245562306a36Sopenharmony_ci	    S_ISREG(inode->i_mode) && inode->i_nlink != 1)
245662306a36Sopenharmony_ci		return true;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	return false;
245962306a36Sopenharmony_ci}
246062306a36Sopenharmony_ci
246162306a36Sopenharmony_ci/*
246262306a36Sopenharmony_ci * Zap the cache. Called when invalid_mapping flag is set.
246362306a36Sopenharmony_ci */
246462306a36Sopenharmony_ciint
246562306a36Sopenharmony_cicifs_invalidate_mapping(struct inode *inode)
246662306a36Sopenharmony_ci{
246762306a36Sopenharmony_ci	int rc = 0;
246862306a36Sopenharmony_ci
246962306a36Sopenharmony_ci	if (inode->i_mapping && inode->i_mapping->nrpages != 0) {
247062306a36Sopenharmony_ci		rc = invalidate_inode_pages2(inode->i_mapping);
247162306a36Sopenharmony_ci		if (rc)
247262306a36Sopenharmony_ci			cifs_dbg(VFS, "%s: invalidate inode %p failed with rc %d\n",
247362306a36Sopenharmony_ci				 __func__, inode, rc);
247462306a36Sopenharmony_ci	}
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci	return rc;
247762306a36Sopenharmony_ci}
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci/**
248062306a36Sopenharmony_ci * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks
248162306a36Sopenharmony_ci *
248262306a36Sopenharmony_ci * @key:	currently unused
248362306a36Sopenharmony_ci * @mode:	the task state to sleep in
248462306a36Sopenharmony_ci */
248562306a36Sopenharmony_cistatic int
248662306a36Sopenharmony_cicifs_wait_bit_killable(struct wait_bit_key *key, int mode)
248762306a36Sopenharmony_ci{
248862306a36Sopenharmony_ci	schedule();
248962306a36Sopenharmony_ci	if (signal_pending_state(mode, current))
249062306a36Sopenharmony_ci		return -ERESTARTSYS;
249162306a36Sopenharmony_ci	return 0;
249262306a36Sopenharmony_ci}
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ciint
249562306a36Sopenharmony_cicifs_revalidate_mapping(struct inode *inode)
249662306a36Sopenharmony_ci{
249762306a36Sopenharmony_ci	int rc;
249862306a36Sopenharmony_ci	unsigned long *flags = &CIFS_I(inode)->flags;
249962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
250062306a36Sopenharmony_ci
250162306a36Sopenharmony_ci	/* swapfiles are not supposed to be shared */
250262306a36Sopenharmony_ci	if (IS_SWAPFILE(inode))
250362306a36Sopenharmony_ci		return 0;
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci	rc = wait_on_bit_lock_action(flags, CIFS_INO_LOCK, cifs_wait_bit_killable,
250662306a36Sopenharmony_ci				     TASK_KILLABLE|TASK_FREEZABLE_UNSAFE);
250762306a36Sopenharmony_ci	if (rc)
250862306a36Sopenharmony_ci		return rc;
250962306a36Sopenharmony_ci
251062306a36Sopenharmony_ci	if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) {
251162306a36Sopenharmony_ci		/* for cache=singleclient, do not invalidate */
251262306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RW_CACHE)
251362306a36Sopenharmony_ci			goto skip_invalidate;
251462306a36Sopenharmony_ci
251562306a36Sopenharmony_ci		rc = cifs_invalidate_mapping(inode);
251662306a36Sopenharmony_ci		if (rc)
251762306a36Sopenharmony_ci			set_bit(CIFS_INO_INVALID_MAPPING, flags);
251862306a36Sopenharmony_ci	}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ciskip_invalidate:
252162306a36Sopenharmony_ci	clear_bit_unlock(CIFS_INO_LOCK, flags);
252262306a36Sopenharmony_ci	smp_mb__after_atomic();
252362306a36Sopenharmony_ci	wake_up_bit(flags, CIFS_INO_LOCK);
252462306a36Sopenharmony_ci
252562306a36Sopenharmony_ci	return rc;
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ciint
252962306a36Sopenharmony_cicifs_zap_mapping(struct inode *inode)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags);
253262306a36Sopenharmony_ci	return cifs_revalidate_mapping(inode);
253362306a36Sopenharmony_ci}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ciint cifs_revalidate_file_attr(struct file *filp)
253662306a36Sopenharmony_ci{
253762306a36Sopenharmony_ci	int rc = 0;
253862306a36Sopenharmony_ci	struct dentry *dentry = file_dentry(filp);
253962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
254062306a36Sopenharmony_ci	struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data;
254162306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
254262306a36Sopenharmony_ci
254362306a36Sopenharmony_ci	if (!cifs_dentry_needs_reval(dentry))
254462306a36Sopenharmony_ci		return rc;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
254762306a36Sopenharmony_ci	if (tlink_tcon(cfile->tlink)->unix_ext)
254862306a36Sopenharmony_ci		rc = cifs_get_file_info_unix(filp);
254962306a36Sopenharmony_ci	else
255062306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
255162306a36Sopenharmony_ci		rc = cifs_get_file_info(filp);
255262306a36Sopenharmony_ci
255362306a36Sopenharmony_ci	return rc;
255462306a36Sopenharmony_ci}
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ciint cifs_revalidate_dentry_attr(struct dentry *dentry)
255762306a36Sopenharmony_ci{
255862306a36Sopenharmony_ci	unsigned int xid;
255962306a36Sopenharmony_ci	int rc = 0;
256062306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
256162306a36Sopenharmony_ci	struct super_block *sb = dentry->d_sb;
256262306a36Sopenharmony_ci	const char *full_path;
256362306a36Sopenharmony_ci	void *page;
256462306a36Sopenharmony_ci	int count = 0;
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	if (inode == NULL)
256762306a36Sopenharmony_ci		return -ENOENT;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	if (!cifs_dentry_needs_reval(dentry))
257062306a36Sopenharmony_ci		return rc;
257162306a36Sopenharmony_ci
257262306a36Sopenharmony_ci	xid = get_xid();
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	page = alloc_dentry_path();
257562306a36Sopenharmony_ci	full_path = build_path_from_dentry(dentry, page);
257662306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
257762306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
257862306a36Sopenharmony_ci		goto out;
257962306a36Sopenharmony_ci	}
258062306a36Sopenharmony_ci
258162306a36Sopenharmony_ci	cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n",
258262306a36Sopenharmony_ci		 full_path, inode, inode->i_count.counter,
258362306a36Sopenharmony_ci		 dentry, cifs_get_time(dentry), jiffies);
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ciagain:
258662306a36Sopenharmony_ci	if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
258762306a36Sopenharmony_ci		rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
258862306a36Sopenharmony_ci	else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
258962306a36Sopenharmony_ci		rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
259062306a36Sopenharmony_ci	else
259162306a36Sopenharmony_ci		rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
259262306a36Sopenharmony_ci					 xid, NULL);
259362306a36Sopenharmony_ci	if (rc == -EAGAIN && count++ < 10)
259462306a36Sopenharmony_ci		goto again;
259562306a36Sopenharmony_ciout:
259662306a36Sopenharmony_ci	free_dentry_path(page);
259762306a36Sopenharmony_ci	free_xid(xid);
259862306a36Sopenharmony_ci
259962306a36Sopenharmony_ci	return rc;
260062306a36Sopenharmony_ci}
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ciint cifs_revalidate_file(struct file *filp)
260362306a36Sopenharmony_ci{
260462306a36Sopenharmony_ci	int rc;
260562306a36Sopenharmony_ci	struct inode *inode = file_inode(filp);
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ci	rc = cifs_revalidate_file_attr(filp);
260862306a36Sopenharmony_ci	if (rc)
260962306a36Sopenharmony_ci		return rc;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci	return cifs_revalidate_mapping(inode);
261262306a36Sopenharmony_ci}
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci/* revalidate a dentry's inode attributes */
261562306a36Sopenharmony_ciint cifs_revalidate_dentry(struct dentry *dentry)
261662306a36Sopenharmony_ci{
261762306a36Sopenharmony_ci	int rc;
261862306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	rc = cifs_revalidate_dentry_attr(dentry);
262162306a36Sopenharmony_ci	if (rc)
262262306a36Sopenharmony_ci		return rc;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	return cifs_revalidate_mapping(inode);
262562306a36Sopenharmony_ci}
262662306a36Sopenharmony_ci
262762306a36Sopenharmony_ciint cifs_getattr(struct mnt_idmap *idmap, const struct path *path,
262862306a36Sopenharmony_ci		 struct kstat *stat, u32 request_mask, unsigned int flags)
262962306a36Sopenharmony_ci{
263062306a36Sopenharmony_ci	struct dentry *dentry = path->dentry;
263162306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
263262306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
263362306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
263462306a36Sopenharmony_ci	int rc;
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(CIFS_SB(inode->i_sb))))
263762306a36Sopenharmony_ci		return -EIO;
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	/*
264062306a36Sopenharmony_ci	 * We need to be sure that all dirty pages are written and the server
264162306a36Sopenharmony_ci	 * has actual ctime, mtime and file length.
264262306a36Sopenharmony_ci	 */
264362306a36Sopenharmony_ci	if ((request_mask & (STATX_CTIME | STATX_MTIME | STATX_SIZE | STATX_BLOCKS)) &&
264462306a36Sopenharmony_ci	    !CIFS_CACHE_READ(CIFS_I(inode)) &&
264562306a36Sopenharmony_ci	    inode->i_mapping && inode->i_mapping->nrpages != 0) {
264662306a36Sopenharmony_ci		rc = filemap_fdatawait(inode->i_mapping);
264762306a36Sopenharmony_ci		if (rc) {
264862306a36Sopenharmony_ci			mapping_set_error(inode->i_mapping, rc);
264962306a36Sopenharmony_ci			return rc;
265062306a36Sopenharmony_ci		}
265162306a36Sopenharmony_ci	}
265262306a36Sopenharmony_ci
265362306a36Sopenharmony_ci	if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_FORCE_SYNC)
265462306a36Sopenharmony_ci		CIFS_I(inode)->time = 0; /* force revalidate */
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	/*
265762306a36Sopenharmony_ci	 * If the caller doesn't require syncing, only sync if
265862306a36Sopenharmony_ci	 * necessary (e.g. due to earlier truncate or setattr
265962306a36Sopenharmony_ci	 * invalidating the cached metadata)
266062306a36Sopenharmony_ci	 */
266162306a36Sopenharmony_ci	if (((flags & AT_STATX_SYNC_TYPE) != AT_STATX_DONT_SYNC) ||
266262306a36Sopenharmony_ci	    (CIFS_I(inode)->time == 0)) {
266362306a36Sopenharmony_ci		rc = cifs_revalidate_dentry_attr(dentry);
266462306a36Sopenharmony_ci		if (rc)
266562306a36Sopenharmony_ci			return rc;
266662306a36Sopenharmony_ci	}
266762306a36Sopenharmony_ci
266862306a36Sopenharmony_ci	generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
266962306a36Sopenharmony_ci	stat->blksize = cifs_sb->ctx->bsize;
267062306a36Sopenharmony_ci	stat->ino = CIFS_I(inode)->uniqueid;
267162306a36Sopenharmony_ci
267262306a36Sopenharmony_ci	/* old CIFS Unix Extensions doesn't return create time */
267362306a36Sopenharmony_ci	if (CIFS_I(inode)->createtime) {
267462306a36Sopenharmony_ci		stat->result_mask |= STATX_BTIME;
267562306a36Sopenharmony_ci		stat->btime =
267662306a36Sopenharmony_ci		      cifs_NTtimeToUnix(cpu_to_le64(CIFS_I(inode)->createtime));
267762306a36Sopenharmony_ci	}
267862306a36Sopenharmony_ci
267962306a36Sopenharmony_ci	stat->attributes_mask |= (STATX_ATTR_COMPRESSED | STATX_ATTR_ENCRYPTED);
268062306a36Sopenharmony_ci	if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_COMPRESSED)
268162306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_COMPRESSED;
268262306a36Sopenharmony_ci	if (CIFS_I(inode)->cifsAttrs & FILE_ATTRIBUTE_ENCRYPTED)
268362306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_ENCRYPTED;
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	/*
268662306a36Sopenharmony_ci	 * If on a multiuser mount without unix extensions or cifsacl being
268762306a36Sopenharmony_ci	 * enabled, and the admin hasn't overridden them, set the ownership
268862306a36Sopenharmony_ci	 * to the fsuid/fsgid of the current process.
268962306a36Sopenharmony_ci	 */
269062306a36Sopenharmony_ci	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
269162306a36Sopenharmony_ci	    !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
269262306a36Sopenharmony_ci	    !tcon->unix_ext) {
269362306a36Sopenharmony_ci		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID))
269462306a36Sopenharmony_ci			stat->uid = current_fsuid();
269562306a36Sopenharmony_ci		if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID))
269662306a36Sopenharmony_ci			stat->gid = current_fsgid();
269762306a36Sopenharmony_ci	}
269862306a36Sopenharmony_ci	return 0;
269962306a36Sopenharmony_ci}
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ciint cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start,
270262306a36Sopenharmony_ci		u64 len)
270362306a36Sopenharmony_ci{
270462306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
270562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->netfs.inode.i_sb);
270662306a36Sopenharmony_ci	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
270762306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
270862306a36Sopenharmony_ci	struct cifsFileInfo *cfile;
270962306a36Sopenharmony_ci	int rc;
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
271262306a36Sopenharmony_ci		return -EIO;
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_ci	/*
271562306a36Sopenharmony_ci	 * We need to be sure that all dirty pages are written as they
271662306a36Sopenharmony_ci	 * might fill holes on the server.
271762306a36Sopenharmony_ci	 */
271862306a36Sopenharmony_ci	if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping &&
271962306a36Sopenharmony_ci	    inode->i_mapping->nrpages != 0) {
272062306a36Sopenharmony_ci		rc = filemap_fdatawait(inode->i_mapping);
272162306a36Sopenharmony_ci		if (rc) {
272262306a36Sopenharmony_ci			mapping_set_error(inode->i_mapping, rc);
272362306a36Sopenharmony_ci			return rc;
272462306a36Sopenharmony_ci		}
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	cfile = find_readable_file(cifs_i, false);
272862306a36Sopenharmony_ci	if (cfile == NULL)
272962306a36Sopenharmony_ci		return -EINVAL;
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	if (server->ops->fiemap) {
273262306a36Sopenharmony_ci		rc = server->ops->fiemap(tcon, cfile, fei, start, len);
273362306a36Sopenharmony_ci		cifsFileInfo_put(cfile);
273462306a36Sopenharmony_ci		return rc;
273562306a36Sopenharmony_ci	}
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	cifsFileInfo_put(cfile);
273862306a36Sopenharmony_ci	return -EOPNOTSUPP;
273962306a36Sopenharmony_ci}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ciint cifs_truncate_page(struct address_space *mapping, loff_t from)
274262306a36Sopenharmony_ci{
274362306a36Sopenharmony_ci	pgoff_t index = from >> PAGE_SHIFT;
274462306a36Sopenharmony_ci	unsigned offset = from & (PAGE_SIZE - 1);
274562306a36Sopenharmony_ci	struct page *page;
274662306a36Sopenharmony_ci	int rc = 0;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci	page = grab_cache_page(mapping, index);
274962306a36Sopenharmony_ci	if (!page)
275062306a36Sopenharmony_ci		return -ENOMEM;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	zero_user_segment(page, offset, PAGE_SIZE);
275362306a36Sopenharmony_ci	unlock_page(page);
275462306a36Sopenharmony_ci	put_page(page);
275562306a36Sopenharmony_ci	return rc;
275662306a36Sopenharmony_ci}
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_civoid cifs_setsize(struct inode *inode, loff_t offset)
275962306a36Sopenharmony_ci{
276062306a36Sopenharmony_ci	struct cifsInodeInfo *cifs_i = CIFS_I(inode);
276162306a36Sopenharmony_ci
276262306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
276362306a36Sopenharmony_ci	i_size_write(inode, offset);
276462306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	/* Cached inode must be refreshed on truncate */
276762306a36Sopenharmony_ci	cifs_i->time = 0;
276862306a36Sopenharmony_ci	truncate_pagecache(inode, offset);
276962306a36Sopenharmony_ci}
277062306a36Sopenharmony_ci
277162306a36Sopenharmony_cistatic int
277262306a36Sopenharmony_cicifs_set_file_size(struct inode *inode, struct iattr *attrs,
277362306a36Sopenharmony_ci		   unsigned int xid, const char *full_path)
277462306a36Sopenharmony_ci{
277562306a36Sopenharmony_ci	int rc;
277662306a36Sopenharmony_ci	struct cifsFileInfo *open_file;
277762306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInode = CIFS_I(inode);
277862306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
277962306a36Sopenharmony_ci	struct tcon_link *tlink = NULL;
278062306a36Sopenharmony_ci	struct cifs_tcon *tcon = NULL;
278162306a36Sopenharmony_ci	struct TCP_Server_Info *server;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	/*
278462306a36Sopenharmony_ci	 * To avoid spurious oplock breaks from server, in the case of
278562306a36Sopenharmony_ci	 * inodes that we already have open, avoid doing path based
278662306a36Sopenharmony_ci	 * setting of file size if we can do it by handle.
278762306a36Sopenharmony_ci	 * This keeps our caching token (oplock) and avoids timeouts
278862306a36Sopenharmony_ci	 * when the local oplock break takes longer to flush
278962306a36Sopenharmony_ci	 * writebehind data than the SMB timeout for the SetPathInfo
279062306a36Sopenharmony_ci	 * request would allow
279162306a36Sopenharmony_ci	 */
279262306a36Sopenharmony_ci	open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
279362306a36Sopenharmony_ci	if (open_file) {
279462306a36Sopenharmony_ci		tcon = tlink_tcon(open_file->tlink);
279562306a36Sopenharmony_ci		server = tcon->ses->server;
279662306a36Sopenharmony_ci		if (server->ops->set_file_size)
279762306a36Sopenharmony_ci			rc = server->ops->set_file_size(xid, tcon, open_file,
279862306a36Sopenharmony_ci							attrs->ia_size, false);
279962306a36Sopenharmony_ci		else
280062306a36Sopenharmony_ci			rc = -ENOSYS;
280162306a36Sopenharmony_ci		cifsFileInfo_put(open_file);
280262306a36Sopenharmony_ci		cifs_dbg(FYI, "SetFSize for attrs rc = %d\n", rc);
280362306a36Sopenharmony_ci	} else
280462306a36Sopenharmony_ci		rc = -EINVAL;
280562306a36Sopenharmony_ci
280662306a36Sopenharmony_ci	if (!rc)
280762306a36Sopenharmony_ci		goto set_size_out;
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	if (tcon == NULL) {
281062306a36Sopenharmony_ci		tlink = cifs_sb_tlink(cifs_sb);
281162306a36Sopenharmony_ci		if (IS_ERR(tlink))
281262306a36Sopenharmony_ci			return PTR_ERR(tlink);
281362306a36Sopenharmony_ci		tcon = tlink_tcon(tlink);
281462306a36Sopenharmony_ci		server = tcon->ses->server;
281562306a36Sopenharmony_ci	}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	/*
281862306a36Sopenharmony_ci	 * Set file size by pathname rather than by handle either because no
281962306a36Sopenharmony_ci	 * valid, writeable file handle for it was found or because there was
282062306a36Sopenharmony_ci	 * an error setting it by handle.
282162306a36Sopenharmony_ci	 */
282262306a36Sopenharmony_ci	if (server->ops->set_path_size)
282362306a36Sopenharmony_ci		rc = server->ops->set_path_size(xid, tcon, full_path,
282462306a36Sopenharmony_ci						attrs->ia_size, cifs_sb, false);
282562306a36Sopenharmony_ci	else
282662306a36Sopenharmony_ci		rc = -ENOSYS;
282762306a36Sopenharmony_ci	cifs_dbg(FYI, "SetEOF by path (setattrs) rc = %d\n", rc);
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	if (tlink)
283062306a36Sopenharmony_ci		cifs_put_tlink(tlink);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ciset_size_out:
283362306a36Sopenharmony_ci	if (rc == 0) {
283462306a36Sopenharmony_ci		cifsInode->server_eof = attrs->ia_size;
283562306a36Sopenharmony_ci		cifs_setsize(inode, attrs->ia_size);
283662306a36Sopenharmony_ci		/*
283762306a36Sopenharmony_ci		 * i_blocks is not related to (i_size / i_blksize), but instead
283862306a36Sopenharmony_ci		 * 512 byte (2**9) size is required for calculating num blocks.
283962306a36Sopenharmony_ci		 * Until we can query the server for actual allocation size,
284062306a36Sopenharmony_ci		 * this is best estimate we have for blocks allocated for a file
284162306a36Sopenharmony_ci		 * Number of blocks must be rounded up so size 1 is not 0 blocks
284262306a36Sopenharmony_ci		 */
284362306a36Sopenharmony_ci		inode->i_blocks = (512 - 1 + attrs->ia_size) >> 9;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci		/*
284662306a36Sopenharmony_ci		 * The man page of truncate says if the size changed,
284762306a36Sopenharmony_ci		 * then the st_ctime and st_mtime fields for the file
284862306a36Sopenharmony_ci		 * are updated.
284962306a36Sopenharmony_ci		 */
285062306a36Sopenharmony_ci		attrs->ia_ctime = attrs->ia_mtime = current_time(inode);
285162306a36Sopenharmony_ci		attrs->ia_valid |= ATTR_CTIME | ATTR_MTIME;
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci		cifs_truncate_page(inode->i_mapping, inode->i_size);
285462306a36Sopenharmony_ci	}
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	return rc;
285762306a36Sopenharmony_ci}
285862306a36Sopenharmony_ci
285962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
286062306a36Sopenharmony_cistatic int
286162306a36Sopenharmony_cicifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
286262306a36Sopenharmony_ci{
286362306a36Sopenharmony_ci	int rc;
286462306a36Sopenharmony_ci	unsigned int xid;
286562306a36Sopenharmony_ci	const char *full_path;
286662306a36Sopenharmony_ci	void *page = alloc_dentry_path();
286762306a36Sopenharmony_ci	struct inode *inode = d_inode(direntry);
286862306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInode = CIFS_I(inode);
286962306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
287062306a36Sopenharmony_ci	struct tcon_link *tlink;
287162306a36Sopenharmony_ci	struct cifs_tcon *pTcon;
287262306a36Sopenharmony_ci	struct cifs_unix_set_info_args *args = NULL;
287362306a36Sopenharmony_ci	struct cifsFileInfo *open_file;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci	cifs_dbg(FYI, "setattr_unix on file %pd attrs->ia_valid=0x%x\n",
287662306a36Sopenharmony_ci		 direntry, attrs->ia_valid);
287762306a36Sopenharmony_ci
287862306a36Sopenharmony_ci	xid = get_xid();
287962306a36Sopenharmony_ci
288062306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
288162306a36Sopenharmony_ci		attrs->ia_valid |= ATTR_FORCE;
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci	rc = setattr_prepare(&nop_mnt_idmap, direntry, attrs);
288462306a36Sopenharmony_ci	if (rc < 0)
288562306a36Sopenharmony_ci		goto out;
288662306a36Sopenharmony_ci
288762306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
288862306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
288962306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
289062306a36Sopenharmony_ci		goto out;
289162306a36Sopenharmony_ci	}
289262306a36Sopenharmony_ci
289362306a36Sopenharmony_ci	/*
289462306a36Sopenharmony_ci	 * Attempt to flush data before changing attributes. We need to do
289562306a36Sopenharmony_ci	 * this for ATTR_SIZE and ATTR_MTIME for sure, and if we change the
289662306a36Sopenharmony_ci	 * ownership or mode then we may also need to do this. Here, we take
289762306a36Sopenharmony_ci	 * the safe way out and just do the flush on all setattr requests. If
289862306a36Sopenharmony_ci	 * the flush returns error, store it to report later and continue.
289962306a36Sopenharmony_ci	 *
290062306a36Sopenharmony_ci	 * BB: This should be smarter. Why bother flushing pages that
290162306a36Sopenharmony_ci	 * will be truncated anyway? Also, should we error out here if
290262306a36Sopenharmony_ci	 * the flush returns error?
290362306a36Sopenharmony_ci	 */
290462306a36Sopenharmony_ci	rc = filemap_write_and_wait(inode->i_mapping);
290562306a36Sopenharmony_ci	if (is_interrupt_error(rc)) {
290662306a36Sopenharmony_ci		rc = -ERESTARTSYS;
290762306a36Sopenharmony_ci		goto out;
290862306a36Sopenharmony_ci	}
290962306a36Sopenharmony_ci
291062306a36Sopenharmony_ci	mapping_set_error(inode->i_mapping, rc);
291162306a36Sopenharmony_ci	rc = 0;
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_SIZE) {
291462306a36Sopenharmony_ci		rc = cifs_set_file_size(inode, attrs, xid, full_path);
291562306a36Sopenharmony_ci		if (rc != 0)
291662306a36Sopenharmony_ci			goto out;
291762306a36Sopenharmony_ci	}
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	/* skip mode change if it's just for clearing setuid/setgid */
292062306a36Sopenharmony_ci	if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
292162306a36Sopenharmony_ci		attrs->ia_valid &= ~ATTR_MODE;
292262306a36Sopenharmony_ci
292362306a36Sopenharmony_ci	args = kmalloc(sizeof(*args), GFP_KERNEL);
292462306a36Sopenharmony_ci	if (args == NULL) {
292562306a36Sopenharmony_ci		rc = -ENOMEM;
292662306a36Sopenharmony_ci		goto out;
292762306a36Sopenharmony_ci	}
292862306a36Sopenharmony_ci
292962306a36Sopenharmony_ci	/* set up the struct */
293062306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_MODE)
293162306a36Sopenharmony_ci		args->mode = attrs->ia_mode;
293262306a36Sopenharmony_ci	else
293362306a36Sopenharmony_ci		args->mode = NO_CHANGE_64;
293462306a36Sopenharmony_ci
293562306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_UID)
293662306a36Sopenharmony_ci		args->uid = attrs->ia_uid;
293762306a36Sopenharmony_ci	else
293862306a36Sopenharmony_ci		args->uid = INVALID_UID; /* no change */
293962306a36Sopenharmony_ci
294062306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_GID)
294162306a36Sopenharmony_ci		args->gid = attrs->ia_gid;
294262306a36Sopenharmony_ci	else
294362306a36Sopenharmony_ci		args->gid = INVALID_GID; /* no change */
294462306a36Sopenharmony_ci
294562306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_ATIME)
294662306a36Sopenharmony_ci		args->atime = cifs_UnixTimeToNT(attrs->ia_atime);
294762306a36Sopenharmony_ci	else
294862306a36Sopenharmony_ci		args->atime = NO_CHANGE_64;
294962306a36Sopenharmony_ci
295062306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_MTIME)
295162306a36Sopenharmony_ci		args->mtime = cifs_UnixTimeToNT(attrs->ia_mtime);
295262306a36Sopenharmony_ci	else
295362306a36Sopenharmony_ci		args->mtime = NO_CHANGE_64;
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_CTIME)
295662306a36Sopenharmony_ci		args->ctime = cifs_UnixTimeToNT(attrs->ia_ctime);
295762306a36Sopenharmony_ci	else
295862306a36Sopenharmony_ci		args->ctime = NO_CHANGE_64;
295962306a36Sopenharmony_ci
296062306a36Sopenharmony_ci	args->device = 0;
296162306a36Sopenharmony_ci	open_file = find_writable_file(cifsInode, FIND_WR_FSUID_ONLY);
296262306a36Sopenharmony_ci	if (open_file) {
296362306a36Sopenharmony_ci		u16 nfid = open_file->fid.netfid;
296462306a36Sopenharmony_ci		u32 npid = open_file->pid;
296562306a36Sopenharmony_ci		pTcon = tlink_tcon(open_file->tlink);
296662306a36Sopenharmony_ci		rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
296762306a36Sopenharmony_ci		cifsFileInfo_put(open_file);
296862306a36Sopenharmony_ci	} else {
296962306a36Sopenharmony_ci		tlink = cifs_sb_tlink(cifs_sb);
297062306a36Sopenharmony_ci		if (IS_ERR(tlink)) {
297162306a36Sopenharmony_ci			rc = PTR_ERR(tlink);
297262306a36Sopenharmony_ci			goto out;
297362306a36Sopenharmony_ci		}
297462306a36Sopenharmony_ci		pTcon = tlink_tcon(tlink);
297562306a36Sopenharmony_ci		rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
297662306a36Sopenharmony_ci				    cifs_sb->local_nls,
297762306a36Sopenharmony_ci				    cifs_remap(cifs_sb));
297862306a36Sopenharmony_ci		cifs_put_tlink(tlink);
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	if (rc)
298262306a36Sopenharmony_ci		goto out;
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	if ((attrs->ia_valid & ATTR_SIZE) &&
298562306a36Sopenharmony_ci	    attrs->ia_size != i_size_read(inode)) {
298662306a36Sopenharmony_ci		truncate_setsize(inode, attrs->ia_size);
298762306a36Sopenharmony_ci		fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
298862306a36Sopenharmony_ci	}
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci	setattr_copy(&nop_mnt_idmap, inode, attrs);
299162306a36Sopenharmony_ci	mark_inode_dirty(inode);
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	/* force revalidate when any of these times are set since some
299462306a36Sopenharmony_ci	   of the fs types (eg ext3, fat) do not have fine enough
299562306a36Sopenharmony_ci	   time granularity to match protocol, and we do not have a
299662306a36Sopenharmony_ci	   a way (yet) to query the server fs's time granularity (and
299762306a36Sopenharmony_ci	   whether it rounds times down).
299862306a36Sopenharmony_ci	*/
299962306a36Sopenharmony_ci	if (attrs->ia_valid & (ATTR_MTIME | ATTR_CTIME))
300062306a36Sopenharmony_ci		cifsInode->time = 0;
300162306a36Sopenharmony_ciout:
300262306a36Sopenharmony_ci	kfree(args);
300362306a36Sopenharmony_ci	free_dentry_path(page);
300462306a36Sopenharmony_ci	free_xid(xid);
300562306a36Sopenharmony_ci	return rc;
300662306a36Sopenharmony_ci}
300762306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
300862306a36Sopenharmony_ci
300962306a36Sopenharmony_cistatic int
301062306a36Sopenharmony_cicifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs)
301162306a36Sopenharmony_ci{
301262306a36Sopenharmony_ci	unsigned int xid;
301362306a36Sopenharmony_ci	kuid_t uid = INVALID_UID;
301462306a36Sopenharmony_ci	kgid_t gid = INVALID_GID;
301562306a36Sopenharmony_ci	struct inode *inode = d_inode(direntry);
301662306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
301762306a36Sopenharmony_ci	struct cifsInodeInfo *cifsInode = CIFS_I(inode);
301862306a36Sopenharmony_ci	struct cifsFileInfo *wfile;
301962306a36Sopenharmony_ci	struct cifs_tcon *tcon;
302062306a36Sopenharmony_ci	const char *full_path;
302162306a36Sopenharmony_ci	void *page = alloc_dentry_path();
302262306a36Sopenharmony_ci	int rc = -EACCES;
302362306a36Sopenharmony_ci	__u32 dosattr = 0;
302462306a36Sopenharmony_ci	__u64 mode = NO_CHANGE_64;
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_ci	xid = get_xid();
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	cifs_dbg(FYI, "setattr on file %pd attrs->ia_valid 0x%x\n",
302962306a36Sopenharmony_ci		 direntry, attrs->ia_valid);
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM)
303262306a36Sopenharmony_ci		attrs->ia_valid |= ATTR_FORCE;
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	rc = setattr_prepare(&nop_mnt_idmap, direntry, attrs);
303562306a36Sopenharmony_ci	if (rc < 0)
303662306a36Sopenharmony_ci		goto cifs_setattr_exit;
303762306a36Sopenharmony_ci
303862306a36Sopenharmony_ci	full_path = build_path_from_dentry(direntry, page);
303962306a36Sopenharmony_ci	if (IS_ERR(full_path)) {
304062306a36Sopenharmony_ci		rc = PTR_ERR(full_path);
304162306a36Sopenharmony_ci		goto cifs_setattr_exit;
304262306a36Sopenharmony_ci	}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	/*
304562306a36Sopenharmony_ci	 * Attempt to flush data before changing attributes. We need to do
304662306a36Sopenharmony_ci	 * this for ATTR_SIZE and ATTR_MTIME.  If the flush of the data
304762306a36Sopenharmony_ci	 * returns error, store it to report later and continue.
304862306a36Sopenharmony_ci	 *
304962306a36Sopenharmony_ci	 * BB: This should be smarter. Why bother flushing pages that
305062306a36Sopenharmony_ci	 * will be truncated anyway? Also, should we error out here if
305162306a36Sopenharmony_ci	 * the flush returns error? Do we need to check for ATTR_MTIME_SET flag?
305262306a36Sopenharmony_ci	 */
305362306a36Sopenharmony_ci	if (attrs->ia_valid & (ATTR_MTIME | ATTR_SIZE | ATTR_CTIME)) {
305462306a36Sopenharmony_ci		rc = filemap_write_and_wait(inode->i_mapping);
305562306a36Sopenharmony_ci		if (is_interrupt_error(rc)) {
305662306a36Sopenharmony_ci			rc = -ERESTARTSYS;
305762306a36Sopenharmony_ci			goto cifs_setattr_exit;
305862306a36Sopenharmony_ci		}
305962306a36Sopenharmony_ci		mapping_set_error(inode->i_mapping, rc);
306062306a36Sopenharmony_ci	}
306162306a36Sopenharmony_ci
306262306a36Sopenharmony_ci	rc = 0;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	if ((attrs->ia_valid & ATTR_MTIME) &&
306562306a36Sopenharmony_ci	    !(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
306662306a36Sopenharmony_ci		rc = cifs_get_writable_file(cifsInode, FIND_WR_ANY, &wfile);
306762306a36Sopenharmony_ci		if (!rc) {
306862306a36Sopenharmony_ci			tcon = tlink_tcon(wfile->tlink);
306962306a36Sopenharmony_ci			rc = tcon->ses->server->ops->flush(xid, tcon, &wfile->fid);
307062306a36Sopenharmony_ci			cifsFileInfo_put(wfile);
307162306a36Sopenharmony_ci			if (rc)
307262306a36Sopenharmony_ci				goto cifs_setattr_exit;
307362306a36Sopenharmony_ci		} else if (rc != -EBADF)
307462306a36Sopenharmony_ci			goto cifs_setattr_exit;
307562306a36Sopenharmony_ci		else
307662306a36Sopenharmony_ci			rc = 0;
307762306a36Sopenharmony_ci	}
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_SIZE) {
308062306a36Sopenharmony_ci		rc = cifs_set_file_size(inode, attrs, xid, full_path);
308162306a36Sopenharmony_ci		if (rc != 0)
308262306a36Sopenharmony_ci			goto cifs_setattr_exit;
308362306a36Sopenharmony_ci	}
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_UID)
308662306a36Sopenharmony_ci		uid = attrs->ia_uid;
308762306a36Sopenharmony_ci
308862306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_GID)
308962306a36Sopenharmony_ci		gid = attrs->ia_gid;
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
309262306a36Sopenharmony_ci	    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
309362306a36Sopenharmony_ci		if (uid_valid(uid) || gid_valid(gid)) {
309462306a36Sopenharmony_ci			mode = NO_CHANGE_64;
309562306a36Sopenharmony_ci			rc = id_mode_to_cifs_acl(inode, full_path, &mode,
309662306a36Sopenharmony_ci							uid, gid);
309762306a36Sopenharmony_ci			if (rc) {
309862306a36Sopenharmony_ci				cifs_dbg(FYI, "%s: Setting id failed with error: %d\n",
309962306a36Sopenharmony_ci					 __func__, rc);
310062306a36Sopenharmony_ci				goto cifs_setattr_exit;
310162306a36Sopenharmony_ci			}
310262306a36Sopenharmony_ci		}
310362306a36Sopenharmony_ci	} else
310462306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID))
310562306a36Sopenharmony_ci		attrs->ia_valid &= ~(ATTR_UID | ATTR_GID);
310662306a36Sopenharmony_ci
310762306a36Sopenharmony_ci	/* skip mode change if it's just for clearing setuid/setgid */
310862306a36Sopenharmony_ci	if (attrs->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
310962306a36Sopenharmony_ci		attrs->ia_valid &= ~ATTR_MODE;
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	if (attrs->ia_valid & ATTR_MODE) {
311262306a36Sopenharmony_ci		mode = attrs->ia_mode;
311362306a36Sopenharmony_ci		rc = 0;
311462306a36Sopenharmony_ci		if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) ||
311562306a36Sopenharmony_ci		    (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID)) {
311662306a36Sopenharmony_ci			rc = id_mode_to_cifs_acl(inode, full_path, &mode,
311762306a36Sopenharmony_ci						INVALID_UID, INVALID_GID);
311862306a36Sopenharmony_ci			if (rc) {
311962306a36Sopenharmony_ci				cifs_dbg(FYI, "%s: Setting ACL failed with error: %d\n",
312062306a36Sopenharmony_ci					 __func__, rc);
312162306a36Sopenharmony_ci				goto cifs_setattr_exit;
312262306a36Sopenharmony_ci			}
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci			/*
312562306a36Sopenharmony_ci			 * In case of CIFS_MOUNT_CIFS_ACL, we cannot support all modes.
312662306a36Sopenharmony_ci			 * Pick up the actual mode bits that were set.
312762306a36Sopenharmony_ci			 */
312862306a36Sopenharmony_ci			if (mode != attrs->ia_mode)
312962306a36Sopenharmony_ci				attrs->ia_mode = mode;
313062306a36Sopenharmony_ci		} else
313162306a36Sopenharmony_ci		if (((mode & S_IWUGO) == 0) &&
313262306a36Sopenharmony_ci		    (cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
313362306a36Sopenharmony_ci
313462306a36Sopenharmony_ci			dosattr = cifsInode->cifsAttrs | ATTR_READONLY;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci			/* fix up mode if we're not using dynperm */
313762306a36Sopenharmony_ci			if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) == 0)
313862306a36Sopenharmony_ci				attrs->ia_mode = inode->i_mode & ~S_IWUGO;
313962306a36Sopenharmony_ci		} else if ((mode & S_IWUGO) &&
314062306a36Sopenharmony_ci			   (cifsInode->cifsAttrs & ATTR_READONLY)) {
314162306a36Sopenharmony_ci
314262306a36Sopenharmony_ci			dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY;
314362306a36Sopenharmony_ci			/* Attributes of 0 are ignored */
314462306a36Sopenharmony_ci			if (dosattr == 0)
314562306a36Sopenharmony_ci				dosattr |= ATTR_NORMAL;
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci			/* reset local inode permissions to normal */
314862306a36Sopenharmony_ci			if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
314962306a36Sopenharmony_ci				attrs->ia_mode &= ~(S_IALLUGO);
315062306a36Sopenharmony_ci				if (S_ISDIR(inode->i_mode))
315162306a36Sopenharmony_ci					attrs->ia_mode |=
315262306a36Sopenharmony_ci						cifs_sb->ctx->dir_mode;
315362306a36Sopenharmony_ci				else
315462306a36Sopenharmony_ci					attrs->ia_mode |=
315562306a36Sopenharmony_ci						cifs_sb->ctx->file_mode;
315662306a36Sopenharmony_ci			}
315762306a36Sopenharmony_ci		} else if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)) {
315862306a36Sopenharmony_ci			/* ignore mode change - ATTR_READONLY hasn't changed */
315962306a36Sopenharmony_ci			attrs->ia_valid &= ~ATTR_MODE;
316062306a36Sopenharmony_ci		}
316162306a36Sopenharmony_ci	}
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci	if (attrs->ia_valid & (ATTR_MTIME|ATTR_ATIME|ATTR_CTIME) ||
316462306a36Sopenharmony_ci	    ((attrs->ia_valid & ATTR_MODE) && dosattr)) {
316562306a36Sopenharmony_ci		rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
316662306a36Sopenharmony_ci		/* BB: check for rc = -EOPNOTSUPP and switch to legacy mode */
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci		/* Even if error on time set, no sense failing the call if
316962306a36Sopenharmony_ci		the server would set the time to a reasonable value anyway,
317062306a36Sopenharmony_ci		and this check ensures that we are not being called from
317162306a36Sopenharmony_ci		sys_utimes in which case we ought to fail the call back to
317262306a36Sopenharmony_ci		the user when the server rejects the call */
317362306a36Sopenharmony_ci		if ((rc) && (attrs->ia_valid &
317462306a36Sopenharmony_ci				(ATTR_MODE | ATTR_GID | ATTR_UID | ATTR_SIZE)))
317562306a36Sopenharmony_ci			rc = 0;
317662306a36Sopenharmony_ci	}
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	/* do not need local check to inode_check_ok since the server does
317962306a36Sopenharmony_ci	   that */
318062306a36Sopenharmony_ci	if (rc)
318162306a36Sopenharmony_ci		goto cifs_setattr_exit;
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_ci	if ((attrs->ia_valid & ATTR_SIZE) &&
318462306a36Sopenharmony_ci	    attrs->ia_size != i_size_read(inode)) {
318562306a36Sopenharmony_ci		truncate_setsize(inode, attrs->ia_size);
318662306a36Sopenharmony_ci		fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size);
318762306a36Sopenharmony_ci	}
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci	setattr_copy(&nop_mnt_idmap, inode, attrs);
319062306a36Sopenharmony_ci	mark_inode_dirty(inode);
319162306a36Sopenharmony_ci
319262306a36Sopenharmony_cicifs_setattr_exit:
319362306a36Sopenharmony_ci	free_xid(xid);
319462306a36Sopenharmony_ci	free_dentry_path(page);
319562306a36Sopenharmony_ci	return rc;
319662306a36Sopenharmony_ci}
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ciint
319962306a36Sopenharmony_cicifs_setattr(struct mnt_idmap *idmap, struct dentry *direntry,
320062306a36Sopenharmony_ci	     struct iattr *attrs)
320162306a36Sopenharmony_ci{
320262306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
320362306a36Sopenharmony_ci	int rc, retries = 0;
320462306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
320562306a36Sopenharmony_ci	struct cifs_tcon *pTcon = cifs_sb_master_tcon(cifs_sb);
320662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	if (unlikely(cifs_forced_shutdown(cifs_sb)))
320962306a36Sopenharmony_ci		return -EIO;
321062306a36Sopenharmony_ci
321162306a36Sopenharmony_ci	do {
321262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
321362306a36Sopenharmony_ci		if (pTcon->unix_ext)
321462306a36Sopenharmony_ci			rc = cifs_setattr_unix(direntry, attrs);
321562306a36Sopenharmony_ci		else
321662306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
321762306a36Sopenharmony_ci			rc = cifs_setattr_nounix(direntry, attrs);
321862306a36Sopenharmony_ci		retries++;
321962306a36Sopenharmony_ci	} while (is_retryable_error(rc) && retries < 2);
322062306a36Sopenharmony_ci
322162306a36Sopenharmony_ci	/* BB: add cifs_setattr_legacy for really old servers */
322262306a36Sopenharmony_ci	return rc;
322362306a36Sopenharmony_ci}
3224