162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  linux/fs/stat.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 1991, 1992  Linus Torvalds
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/blkdev.h>
962306a36Sopenharmony_ci#include <linux/export.h>
1062306a36Sopenharmony_ci#include <linux/mm.h>
1162306a36Sopenharmony_ci#include <linux/errno.h>
1262306a36Sopenharmony_ci#include <linux/file.h>
1362306a36Sopenharmony_ci#include <linux/highuid.h>
1462306a36Sopenharmony_ci#include <linux/fs.h>
1562306a36Sopenharmony_ci#include <linux/namei.h>
1662306a36Sopenharmony_ci#include <linux/security.h>
1762306a36Sopenharmony_ci#include <linux/cred.h>
1862306a36Sopenharmony_ci#include <linux/syscalls.h>
1962306a36Sopenharmony_ci#include <linux/pagemap.h>
2062306a36Sopenharmony_ci#include <linux/compat.h>
2162306a36Sopenharmony_ci#include <linux/iversion.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <linux/uaccess.h>
2462306a36Sopenharmony_ci#include <asm/unistd.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include "internal.h"
2762306a36Sopenharmony_ci#include "mount.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/**
3062306a36Sopenharmony_ci * generic_fillattr - Fill in the basic attributes from the inode struct
3162306a36Sopenharmony_ci * @idmap:		idmap of the mount the inode was found from
3262306a36Sopenharmony_ci * @request_mask:	statx request_mask
3362306a36Sopenharmony_ci * @inode:		Inode to use as the source
3462306a36Sopenharmony_ci * @stat:		Where to fill in the attributes
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * Fill in the basic attributes in the kstat structure from data that's to be
3762306a36Sopenharmony_ci * found on the VFS inode structure.  This is the default if no getattr inode
3862306a36Sopenharmony_ci * operation is supplied.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * If the inode has been found through an idmapped mount the idmap of
4162306a36Sopenharmony_ci * the vfsmount must be passed through @idmap. This function will then
4262306a36Sopenharmony_ci * take care to map the inode according to @idmap before filling in the
4362306a36Sopenharmony_ci * uid and gid filds. On non-idmapped mounts or if permission checking is to be
4462306a36Sopenharmony_ci * performed on the raw inode simply passs @nop_mnt_idmap.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_civoid generic_fillattr(struct mnt_idmap *idmap, u32 request_mask,
4762306a36Sopenharmony_ci		      struct inode *inode, struct kstat *stat)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode);
5062306a36Sopenharmony_ci	vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	stat->dev = inode->i_sb->s_dev;
5362306a36Sopenharmony_ci	stat->ino = inode->i_ino;
5462306a36Sopenharmony_ci	stat->mode = inode->i_mode;
5562306a36Sopenharmony_ci	stat->nlink = inode->i_nlink;
5662306a36Sopenharmony_ci	stat->uid = vfsuid_into_kuid(vfsuid);
5762306a36Sopenharmony_ci	stat->gid = vfsgid_into_kgid(vfsgid);
5862306a36Sopenharmony_ci	stat->rdev = inode->i_rdev;
5962306a36Sopenharmony_ci	stat->size = i_size_read(inode);
6062306a36Sopenharmony_ci	stat->atime = inode->i_atime;
6162306a36Sopenharmony_ci	stat->mtime = inode->i_mtime;
6262306a36Sopenharmony_ci	stat->ctime = inode_get_ctime(inode);
6362306a36Sopenharmony_ci	stat->blksize = i_blocksize(inode);
6462306a36Sopenharmony_ci	stat->blocks = inode->i_blocks;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if ((request_mask & STATX_CHANGE_COOKIE) && IS_I_VERSION(inode)) {
6762306a36Sopenharmony_ci		stat->result_mask |= STATX_CHANGE_COOKIE;
6862306a36Sopenharmony_ci		stat->change_cookie = inode_query_iversion(inode);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ciEXPORT_SYMBOL(generic_fillattr);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/**
7562306a36Sopenharmony_ci * generic_fill_statx_attr - Fill in the statx attributes from the inode flags
7662306a36Sopenharmony_ci * @inode:	Inode to use as the source
7762306a36Sopenharmony_ci * @stat:	Where to fill in the attribute flags
7862306a36Sopenharmony_ci *
7962306a36Sopenharmony_ci * Fill in the STATX_ATTR_* flags in the kstat structure for properties of the
8062306a36Sopenharmony_ci * inode that are published on i_flags and enforced by the VFS.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_civoid generic_fill_statx_attr(struct inode *inode, struct kstat *stat)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	if (inode->i_flags & S_IMMUTABLE)
8562306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_IMMUTABLE;
8662306a36Sopenharmony_ci	if (inode->i_flags & S_APPEND)
8762306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_APPEND;
8862306a36Sopenharmony_ci	stat->attributes_mask |= KSTAT_ATTR_VFS_FLAGS;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ciEXPORT_SYMBOL(generic_fill_statx_attr);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci/**
9362306a36Sopenharmony_ci * vfs_getattr_nosec - getattr without security checks
9462306a36Sopenharmony_ci * @path: file to get attributes from
9562306a36Sopenharmony_ci * @stat: structure to return attributes in
9662306a36Sopenharmony_ci * @request_mask: STATX_xxx flags indicating what the caller wants
9762306a36Sopenharmony_ci * @query_flags: Query mode (AT_STATX_SYNC_TYPE)
9862306a36Sopenharmony_ci *
9962306a36Sopenharmony_ci * Get attributes without calling security_inode_getattr.
10062306a36Sopenharmony_ci *
10162306a36Sopenharmony_ci * Currently the only caller other than vfs_getattr is internal to the
10262306a36Sopenharmony_ci * filehandle lookup code, which uses only the inode number and returns no
10362306a36Sopenharmony_ci * attributes to any user.  Any other code probably wants vfs_getattr.
10462306a36Sopenharmony_ci */
10562306a36Sopenharmony_ciint vfs_getattr_nosec(const struct path *path, struct kstat *stat,
10662306a36Sopenharmony_ci		      u32 request_mask, unsigned int query_flags)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct mnt_idmap *idmap;
10962306a36Sopenharmony_ci	struct inode *inode = d_backing_inode(path->dentry);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	memset(stat, 0, sizeof(*stat));
11262306a36Sopenharmony_ci	stat->result_mask |= STATX_BASIC_STATS;
11362306a36Sopenharmony_ci	query_flags &= AT_STATX_SYNC_TYPE;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* allow the fs to override these if it really wants to */
11662306a36Sopenharmony_ci	/* SB_NOATIME means filesystem supplies dummy atime value */
11762306a36Sopenharmony_ci	if (inode->i_sb->s_flags & SB_NOATIME)
11862306a36Sopenharmony_ci		stat->result_mask &= ~STATX_ATIME;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/*
12162306a36Sopenharmony_ci	 * Note: If you add another clause to set an attribute flag, please
12262306a36Sopenharmony_ci	 * update attributes_mask below.
12362306a36Sopenharmony_ci	 */
12462306a36Sopenharmony_ci	if (IS_AUTOMOUNT(inode))
12562306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_AUTOMOUNT;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (IS_DAX(inode))
12862306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_DAX;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT |
13162306a36Sopenharmony_ci				  STATX_ATTR_DAX);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	idmap = mnt_idmap(path->mnt);
13462306a36Sopenharmony_ci	if (inode->i_op->getattr)
13562306a36Sopenharmony_ci		return inode->i_op->getattr(idmap, path, stat,
13662306a36Sopenharmony_ci					    request_mask,
13762306a36Sopenharmony_ci					    query_flags | AT_GETATTR_NOSEC);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	generic_fillattr(idmap, request_mask, inode, stat);
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_getattr_nosec);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/*
14562306a36Sopenharmony_ci * vfs_getattr - Get the enhanced basic attributes of a file
14662306a36Sopenharmony_ci * @path: The file of interest
14762306a36Sopenharmony_ci * @stat: Where to return the statistics
14862306a36Sopenharmony_ci * @request_mask: STATX_xxx flags indicating what the caller wants
14962306a36Sopenharmony_ci * @query_flags: Query mode (AT_STATX_SYNC_TYPE)
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * Ask the filesystem for a file's attributes.  The caller must indicate in
15262306a36Sopenharmony_ci * request_mask and query_flags to indicate what they want.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * If the file is remote, the filesystem can be forced to update the attributes
15562306a36Sopenharmony_ci * from the backing store by passing AT_STATX_FORCE_SYNC in query_flags or can
15662306a36Sopenharmony_ci * suppress the update by passing AT_STATX_DONT_SYNC.
15762306a36Sopenharmony_ci *
15862306a36Sopenharmony_ci * Bits must have been set in request_mask to indicate which attributes the
15962306a36Sopenharmony_ci * caller wants retrieving.  Any such attribute not requested may be returned
16062306a36Sopenharmony_ci * anyway, but the value may be approximate, and, if remote, may not have been
16162306a36Sopenharmony_ci * synchronised with the server.
16262306a36Sopenharmony_ci *
16362306a36Sopenharmony_ci * 0 will be returned on success, and a -ve error code if unsuccessful.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_ciint vfs_getattr(const struct path *path, struct kstat *stat,
16662306a36Sopenharmony_ci		u32 request_mask, unsigned int query_flags)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	int retval;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (WARN_ON_ONCE(query_flags & AT_GETATTR_NOSEC))
17162306a36Sopenharmony_ci		return -EPERM;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	retval = security_inode_getattr(path);
17462306a36Sopenharmony_ci	if (retval)
17562306a36Sopenharmony_ci		return retval;
17662306a36Sopenharmony_ci	return vfs_getattr_nosec(path, stat, request_mask, query_flags);
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ciEXPORT_SYMBOL(vfs_getattr);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/**
18162306a36Sopenharmony_ci * vfs_fstat - Get the basic attributes by file descriptor
18262306a36Sopenharmony_ci * @fd: The file descriptor referring to the file of interest
18362306a36Sopenharmony_ci * @stat: The result structure to fill in.
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * This function is a wrapper around vfs_getattr().  The main difference is
18662306a36Sopenharmony_ci * that it uses a file descriptor to determine the file location.
18762306a36Sopenharmony_ci *
18862306a36Sopenharmony_ci * 0 will be returned on success, and a -ve error code if unsuccessful.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_ciint vfs_fstat(int fd, struct kstat *stat)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct fd f;
19362306a36Sopenharmony_ci	int error;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	f = fdget_raw(fd);
19662306a36Sopenharmony_ci	if (!f.file)
19762306a36Sopenharmony_ci		return -EBADF;
19862306a36Sopenharmony_ci	error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0);
19962306a36Sopenharmony_ci	fdput(f);
20062306a36Sopenharmony_ci	return error;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ciint getname_statx_lookup_flags(int flags)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	int lookup_flags = 0;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	if (!(flags & AT_SYMLINK_NOFOLLOW))
20862306a36Sopenharmony_ci		lookup_flags |= LOOKUP_FOLLOW;
20962306a36Sopenharmony_ci	if (!(flags & AT_NO_AUTOMOUNT))
21062306a36Sopenharmony_ci		lookup_flags |= LOOKUP_AUTOMOUNT;
21162306a36Sopenharmony_ci	if (flags & AT_EMPTY_PATH)
21262306a36Sopenharmony_ci		lookup_flags |= LOOKUP_EMPTY;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return lookup_flags;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci/**
21862306a36Sopenharmony_ci * vfs_statx - Get basic and extra attributes by filename
21962306a36Sopenharmony_ci * @dfd: A file descriptor representing the base dir for a relative filename
22062306a36Sopenharmony_ci * @filename: The name of the file of interest
22162306a36Sopenharmony_ci * @flags: Flags to control the query
22262306a36Sopenharmony_ci * @stat: The result structure to fill in.
22362306a36Sopenharmony_ci * @request_mask: STATX_xxx flags indicating what the caller wants
22462306a36Sopenharmony_ci *
22562306a36Sopenharmony_ci * This function is a wrapper around vfs_getattr().  The main difference is
22662306a36Sopenharmony_ci * that it uses a filename and base directory to determine the file location.
22762306a36Sopenharmony_ci * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink
22862306a36Sopenharmony_ci * at the given name from being referenced.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * 0 will be returned on success, and a -ve error code if unsuccessful.
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_cistatic int vfs_statx(int dfd, struct filename *filename, int flags,
23362306a36Sopenharmony_ci	      struct kstat *stat, u32 request_mask)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct path path;
23662306a36Sopenharmony_ci	unsigned int lookup_flags = getname_statx_lookup_flags(flags);
23762306a36Sopenharmony_ci	int error;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
24062306a36Sopenharmony_ci		      AT_STATX_SYNC_TYPE))
24162306a36Sopenharmony_ci		return -EINVAL;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ciretry:
24462306a36Sopenharmony_ci	error = filename_lookup(dfd, filename, lookup_flags, &path, NULL);
24562306a36Sopenharmony_ci	if (error)
24662306a36Sopenharmony_ci		goto out;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	error = vfs_getattr(&path, stat, request_mask, flags);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	stat->mnt_id = real_mount(path.mnt)->mnt_id;
25162306a36Sopenharmony_ci	stat->result_mask |= STATX_MNT_ID;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	if (path.mnt->mnt_root == path.dentry)
25462306a36Sopenharmony_ci		stat->attributes |= STATX_ATTR_MOUNT_ROOT;
25562306a36Sopenharmony_ci	stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	/* Handle STATX_DIOALIGN for block devices. */
25862306a36Sopenharmony_ci	if (request_mask & STATX_DIOALIGN) {
25962306a36Sopenharmony_ci		struct inode *inode = d_backing_inode(path.dentry);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci		if (S_ISBLK(inode->i_mode))
26262306a36Sopenharmony_ci			bdev_statx_dioalign(inode, stat);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	path_put(&path);
26662306a36Sopenharmony_ci	if (retry_estale(error, lookup_flags)) {
26762306a36Sopenharmony_ci		lookup_flags |= LOOKUP_REVAL;
26862306a36Sopenharmony_ci		goto retry;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ciout:
27162306a36Sopenharmony_ci	return error;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ciint vfs_fstatat(int dfd, const char __user *filename,
27562306a36Sopenharmony_ci			      struct kstat *stat, int flags)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	int ret;
27862306a36Sopenharmony_ci	int statx_flags = flags | AT_NO_AUTOMOUNT;
27962306a36Sopenharmony_ci	struct filename *name;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/*
28262306a36Sopenharmony_ci	 * Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH)
28362306a36Sopenharmony_ci	 *
28462306a36Sopenharmony_ci	 * If AT_EMPTY_PATH is set, we expect the common case to be that
28562306a36Sopenharmony_ci	 * empty path, and avoid doing all the extra pathname work.
28662306a36Sopenharmony_ci	 */
28762306a36Sopenharmony_ci	if (dfd >= 0 && flags == AT_EMPTY_PATH) {
28862306a36Sopenharmony_ci		char c;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		ret = get_user(c, filename);
29162306a36Sopenharmony_ci		if (unlikely(ret))
29262306a36Sopenharmony_ci			return ret;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci		if (likely(!c))
29562306a36Sopenharmony_ci			return vfs_fstat(dfd, stat);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	name = getname_flags(filename, getname_statx_lookup_flags(statx_flags), NULL);
29962306a36Sopenharmony_ci	ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS);
30062306a36Sopenharmony_ci	putname(name);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return ret;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci#ifdef __ARCH_WANT_OLD_STAT
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/*
30862306a36Sopenharmony_ci * For backward compatibility?  Maybe this should be moved
30962306a36Sopenharmony_ci * into arch/i386 instead?
31062306a36Sopenharmony_ci */
31162306a36Sopenharmony_cistatic int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf)
31262306a36Sopenharmony_ci{
31362306a36Sopenharmony_ci	static int warncount = 5;
31462306a36Sopenharmony_ci	struct __old_kernel_stat tmp;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	if (warncount > 0) {
31762306a36Sopenharmony_ci		warncount--;
31862306a36Sopenharmony_ci		printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n",
31962306a36Sopenharmony_ci			current->comm);
32062306a36Sopenharmony_ci	} else if (warncount < 0) {
32162306a36Sopenharmony_ci		/* it's laughable, but... */
32262306a36Sopenharmony_ci		warncount = 0;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(struct __old_kernel_stat));
32662306a36Sopenharmony_ci	tmp.st_dev = old_encode_dev(stat->dev);
32762306a36Sopenharmony_ci	tmp.st_ino = stat->ino;
32862306a36Sopenharmony_ci	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
32962306a36Sopenharmony_ci		return -EOVERFLOW;
33062306a36Sopenharmony_ci	tmp.st_mode = stat->mode;
33162306a36Sopenharmony_ci	tmp.st_nlink = stat->nlink;
33262306a36Sopenharmony_ci	if (tmp.st_nlink != stat->nlink)
33362306a36Sopenharmony_ci		return -EOVERFLOW;
33462306a36Sopenharmony_ci	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
33562306a36Sopenharmony_ci	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
33662306a36Sopenharmony_ci	tmp.st_rdev = old_encode_dev(stat->rdev);
33762306a36Sopenharmony_ci#if BITS_PER_LONG == 32
33862306a36Sopenharmony_ci	if (stat->size > MAX_NON_LFS)
33962306a36Sopenharmony_ci		return -EOVERFLOW;
34062306a36Sopenharmony_ci#endif
34162306a36Sopenharmony_ci	tmp.st_size = stat->size;
34262306a36Sopenharmony_ci	tmp.st_atime = stat->atime.tv_sec;
34362306a36Sopenharmony_ci	tmp.st_mtime = stat->mtime.tv_sec;
34462306a36Sopenharmony_ci	tmp.st_ctime = stat->ctime.tv_sec;
34562306a36Sopenharmony_ci	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ciSYSCALL_DEFINE2(stat, const char __user *, filename,
34962306a36Sopenharmony_ci		struct __old_kernel_stat __user *, statbuf)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	struct kstat stat;
35262306a36Sopenharmony_ci	int error;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	error = vfs_stat(filename, &stat);
35562306a36Sopenharmony_ci	if (error)
35662306a36Sopenharmony_ci		return error;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	return cp_old_stat(&stat, statbuf);
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ciSYSCALL_DEFINE2(lstat, const char __user *, filename,
36262306a36Sopenharmony_ci		struct __old_kernel_stat __user *, statbuf)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	struct kstat stat;
36562306a36Sopenharmony_ci	int error;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	error = vfs_lstat(filename, &stat);
36862306a36Sopenharmony_ci	if (error)
36962306a36Sopenharmony_ci		return error;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return cp_old_stat(&stat, statbuf);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ciSYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, statbuf)
37562306a36Sopenharmony_ci{
37662306a36Sopenharmony_ci	struct kstat stat;
37762306a36Sopenharmony_ci	int error = vfs_fstat(fd, &stat);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (!error)
38062306a36Sopenharmony_ci		error = cp_old_stat(&stat, statbuf);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	return error;
38362306a36Sopenharmony_ci}
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci#endif /* __ARCH_WANT_OLD_STAT */
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci#ifdef __ARCH_WANT_NEW_STAT
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci#ifndef INIT_STRUCT_STAT_PADDING
39062306a36Sopenharmony_ci#  define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
39162306a36Sopenharmony_ci#endif
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_cistatic int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
39462306a36Sopenharmony_ci{
39562306a36Sopenharmony_ci	struct stat tmp;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev))
39862306a36Sopenharmony_ci		return -EOVERFLOW;
39962306a36Sopenharmony_ci	if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev))
40062306a36Sopenharmony_ci		return -EOVERFLOW;
40162306a36Sopenharmony_ci#if BITS_PER_LONG == 32
40262306a36Sopenharmony_ci	if (stat->size > MAX_NON_LFS)
40362306a36Sopenharmony_ci		return -EOVERFLOW;
40462306a36Sopenharmony_ci#endif
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	INIT_STRUCT_STAT_PADDING(tmp);
40762306a36Sopenharmony_ci	tmp.st_dev = new_encode_dev(stat->dev);
40862306a36Sopenharmony_ci	tmp.st_ino = stat->ino;
40962306a36Sopenharmony_ci	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
41062306a36Sopenharmony_ci		return -EOVERFLOW;
41162306a36Sopenharmony_ci	tmp.st_mode = stat->mode;
41262306a36Sopenharmony_ci	tmp.st_nlink = stat->nlink;
41362306a36Sopenharmony_ci	if (tmp.st_nlink != stat->nlink)
41462306a36Sopenharmony_ci		return -EOVERFLOW;
41562306a36Sopenharmony_ci	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
41662306a36Sopenharmony_ci	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
41762306a36Sopenharmony_ci	tmp.st_rdev = new_encode_dev(stat->rdev);
41862306a36Sopenharmony_ci	tmp.st_size = stat->size;
41962306a36Sopenharmony_ci	tmp.st_atime = stat->atime.tv_sec;
42062306a36Sopenharmony_ci	tmp.st_mtime = stat->mtime.tv_sec;
42162306a36Sopenharmony_ci	tmp.st_ctime = stat->ctime.tv_sec;
42262306a36Sopenharmony_ci#ifdef STAT_HAVE_NSEC
42362306a36Sopenharmony_ci	tmp.st_atime_nsec = stat->atime.tv_nsec;
42462306a36Sopenharmony_ci	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
42562306a36Sopenharmony_ci	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
42662306a36Sopenharmony_ci#endif
42762306a36Sopenharmony_ci	tmp.st_blocks = stat->blocks;
42862306a36Sopenharmony_ci	tmp.st_blksize = stat->blksize;
42962306a36Sopenharmony_ci	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ciSYSCALL_DEFINE2(newstat, const char __user *, filename,
43362306a36Sopenharmony_ci		struct stat __user *, statbuf)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct kstat stat;
43662306a36Sopenharmony_ci	int error = vfs_stat(filename, &stat);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	if (error)
43962306a36Sopenharmony_ci		return error;
44062306a36Sopenharmony_ci	return cp_new_stat(&stat, statbuf);
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ciSYSCALL_DEFINE2(newlstat, const char __user *, filename,
44462306a36Sopenharmony_ci		struct stat __user *, statbuf)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct kstat stat;
44762306a36Sopenharmony_ci	int error;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	error = vfs_lstat(filename, &stat);
45062306a36Sopenharmony_ci	if (error)
45162306a36Sopenharmony_ci		return error;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	return cp_new_stat(&stat, statbuf);
45462306a36Sopenharmony_ci}
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
45762306a36Sopenharmony_ciSYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
45862306a36Sopenharmony_ci		struct stat __user *, statbuf, int, flag)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	struct kstat stat;
46162306a36Sopenharmony_ci	int error;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	error = vfs_fstatat(dfd, filename, &stat, flag);
46462306a36Sopenharmony_ci	if (error)
46562306a36Sopenharmony_ci		return error;
46662306a36Sopenharmony_ci	return cp_new_stat(&stat, statbuf);
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci#endif
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ciSYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct kstat stat;
47362306a36Sopenharmony_ci	int error = vfs_fstat(fd, &stat);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	if (!error)
47662306a36Sopenharmony_ci		error = cp_new_stat(&stat, statbuf);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	return error;
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci#endif
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int do_readlinkat(int dfd, const char __user *pathname,
48362306a36Sopenharmony_ci			 char __user *buf, int bufsiz)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	struct path path;
48662306a36Sopenharmony_ci	int error;
48762306a36Sopenharmony_ci	int empty = 0;
48862306a36Sopenharmony_ci	unsigned int lookup_flags = LOOKUP_EMPTY;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (bufsiz <= 0)
49162306a36Sopenharmony_ci		return -EINVAL;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ciretry:
49462306a36Sopenharmony_ci	error = user_path_at_empty(dfd, pathname, lookup_flags, &path, &empty);
49562306a36Sopenharmony_ci	if (!error) {
49662306a36Sopenharmony_ci		struct inode *inode = d_backing_inode(path.dentry);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		error = empty ? -ENOENT : -EINVAL;
49962306a36Sopenharmony_ci		/*
50062306a36Sopenharmony_ci		 * AFS mountpoints allow readlink(2) but are not symlinks
50162306a36Sopenharmony_ci		 */
50262306a36Sopenharmony_ci		if (d_is_symlink(path.dentry) || inode->i_op->readlink) {
50362306a36Sopenharmony_ci			error = security_inode_readlink(path.dentry);
50462306a36Sopenharmony_ci			if (!error) {
50562306a36Sopenharmony_ci				touch_atime(&path);
50662306a36Sopenharmony_ci				error = vfs_readlink(path.dentry, buf, bufsiz);
50762306a36Sopenharmony_ci			}
50862306a36Sopenharmony_ci		}
50962306a36Sopenharmony_ci		path_put(&path);
51062306a36Sopenharmony_ci		if (retry_estale(error, lookup_flags)) {
51162306a36Sopenharmony_ci			lookup_flags |= LOOKUP_REVAL;
51262306a36Sopenharmony_ci			goto retry;
51362306a36Sopenharmony_ci		}
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci	return error;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ciSYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
51962306a36Sopenharmony_ci		char __user *, buf, int, bufsiz)
52062306a36Sopenharmony_ci{
52162306a36Sopenharmony_ci	return do_readlinkat(dfd, pathname, buf, bufsiz);
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ciSYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
52562306a36Sopenharmony_ci		int, bufsiz)
52662306a36Sopenharmony_ci{
52762306a36Sopenharmony_ci	return do_readlinkat(AT_FDCWD, path, buf, bufsiz);
52862306a36Sopenharmony_ci}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/* ---------- LFS-64 ----------- */
53262306a36Sopenharmony_ci#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci#ifndef INIT_STRUCT_STAT64_PADDING
53562306a36Sopenharmony_ci#  define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
53662306a36Sopenharmony_ci#endif
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct stat64 tmp;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	INIT_STRUCT_STAT64_PADDING(tmp);
54362306a36Sopenharmony_ci#ifdef CONFIG_MIPS
54462306a36Sopenharmony_ci	/* mips has weird padding, so we don't get 64 bits there */
54562306a36Sopenharmony_ci	tmp.st_dev = new_encode_dev(stat->dev);
54662306a36Sopenharmony_ci	tmp.st_rdev = new_encode_dev(stat->rdev);
54762306a36Sopenharmony_ci#else
54862306a36Sopenharmony_ci	tmp.st_dev = huge_encode_dev(stat->dev);
54962306a36Sopenharmony_ci	tmp.st_rdev = huge_encode_dev(stat->rdev);
55062306a36Sopenharmony_ci#endif
55162306a36Sopenharmony_ci	tmp.st_ino = stat->ino;
55262306a36Sopenharmony_ci	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
55362306a36Sopenharmony_ci		return -EOVERFLOW;
55462306a36Sopenharmony_ci#ifdef STAT64_HAS_BROKEN_ST_INO
55562306a36Sopenharmony_ci	tmp.__st_ino = stat->ino;
55662306a36Sopenharmony_ci#endif
55762306a36Sopenharmony_ci	tmp.st_mode = stat->mode;
55862306a36Sopenharmony_ci	tmp.st_nlink = stat->nlink;
55962306a36Sopenharmony_ci	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
56062306a36Sopenharmony_ci	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
56162306a36Sopenharmony_ci	tmp.st_atime = stat->atime.tv_sec;
56262306a36Sopenharmony_ci	tmp.st_atime_nsec = stat->atime.tv_nsec;
56362306a36Sopenharmony_ci	tmp.st_mtime = stat->mtime.tv_sec;
56462306a36Sopenharmony_ci	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
56562306a36Sopenharmony_ci	tmp.st_ctime = stat->ctime.tv_sec;
56662306a36Sopenharmony_ci	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
56762306a36Sopenharmony_ci	tmp.st_size = stat->size;
56862306a36Sopenharmony_ci	tmp.st_blocks = stat->blocks;
56962306a36Sopenharmony_ci	tmp.st_blksize = stat->blksize;
57062306a36Sopenharmony_ci	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ciSYSCALL_DEFINE2(stat64, const char __user *, filename,
57462306a36Sopenharmony_ci		struct stat64 __user *, statbuf)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	struct kstat stat;
57762306a36Sopenharmony_ci	int error = vfs_stat(filename, &stat);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (!error)
58062306a36Sopenharmony_ci		error = cp_new_stat64(&stat, statbuf);
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	return error;
58362306a36Sopenharmony_ci}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ciSYSCALL_DEFINE2(lstat64, const char __user *, filename,
58662306a36Sopenharmony_ci		struct stat64 __user *, statbuf)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	struct kstat stat;
58962306a36Sopenharmony_ci	int error = vfs_lstat(filename, &stat);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci	if (!error)
59262306a36Sopenharmony_ci		error = cp_new_stat64(&stat, statbuf);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return error;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ciSYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct kstat stat;
60062306a36Sopenharmony_ci	int error = vfs_fstat(fd, &stat);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	if (!error)
60362306a36Sopenharmony_ci		error = cp_new_stat64(&stat, statbuf);
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	return error;
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ciSYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
60962306a36Sopenharmony_ci		struct stat64 __user *, statbuf, int, flag)
61062306a36Sopenharmony_ci{
61162306a36Sopenharmony_ci	struct kstat stat;
61262306a36Sopenharmony_ci	int error;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	error = vfs_fstatat(dfd, filename, &stat, flag);
61562306a36Sopenharmony_ci	if (error)
61662306a36Sopenharmony_ci		return error;
61762306a36Sopenharmony_ci	return cp_new_stat64(&stat, statbuf);
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_cistatic noinline_for_stack int
62262306a36Sopenharmony_cicp_statx(const struct kstat *stat, struct statx __user *buffer)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct statx tmp;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* STATX_CHANGE_COOKIE is kernel-only for now */
62962306a36Sopenharmony_ci	tmp.stx_mask = stat->result_mask & ~STATX_CHANGE_COOKIE;
63062306a36Sopenharmony_ci	tmp.stx_blksize = stat->blksize;
63162306a36Sopenharmony_ci	/* STATX_ATTR_CHANGE_MONOTONIC is kernel-only for now */
63262306a36Sopenharmony_ci	tmp.stx_attributes = stat->attributes & ~STATX_ATTR_CHANGE_MONOTONIC;
63362306a36Sopenharmony_ci	tmp.stx_nlink = stat->nlink;
63462306a36Sopenharmony_ci	tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid);
63562306a36Sopenharmony_ci	tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid);
63662306a36Sopenharmony_ci	tmp.stx_mode = stat->mode;
63762306a36Sopenharmony_ci	tmp.stx_ino = stat->ino;
63862306a36Sopenharmony_ci	tmp.stx_size = stat->size;
63962306a36Sopenharmony_ci	tmp.stx_blocks = stat->blocks;
64062306a36Sopenharmony_ci	tmp.stx_attributes_mask = stat->attributes_mask;
64162306a36Sopenharmony_ci	tmp.stx_atime.tv_sec = stat->atime.tv_sec;
64262306a36Sopenharmony_ci	tmp.stx_atime.tv_nsec = stat->atime.tv_nsec;
64362306a36Sopenharmony_ci	tmp.stx_btime.tv_sec = stat->btime.tv_sec;
64462306a36Sopenharmony_ci	tmp.stx_btime.tv_nsec = stat->btime.tv_nsec;
64562306a36Sopenharmony_ci	tmp.stx_ctime.tv_sec = stat->ctime.tv_sec;
64662306a36Sopenharmony_ci	tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec;
64762306a36Sopenharmony_ci	tmp.stx_mtime.tv_sec = stat->mtime.tv_sec;
64862306a36Sopenharmony_ci	tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec;
64962306a36Sopenharmony_ci	tmp.stx_rdev_major = MAJOR(stat->rdev);
65062306a36Sopenharmony_ci	tmp.stx_rdev_minor = MINOR(stat->rdev);
65162306a36Sopenharmony_ci	tmp.stx_dev_major = MAJOR(stat->dev);
65262306a36Sopenharmony_ci	tmp.stx_dev_minor = MINOR(stat->dev);
65362306a36Sopenharmony_ci	tmp.stx_mnt_id = stat->mnt_id;
65462306a36Sopenharmony_ci	tmp.stx_dio_mem_align = stat->dio_mem_align;
65562306a36Sopenharmony_ci	tmp.stx_dio_offset_align = stat->dio_offset_align;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ciint do_statx(int dfd, struct filename *filename, unsigned int flags,
66162306a36Sopenharmony_ci	     unsigned int mask, struct statx __user *buffer)
66262306a36Sopenharmony_ci{
66362306a36Sopenharmony_ci	struct kstat stat;
66462306a36Sopenharmony_ci	int error;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (mask & STATX__RESERVED)
66762306a36Sopenharmony_ci		return -EINVAL;
66862306a36Sopenharmony_ci	if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
66962306a36Sopenharmony_ci		return -EINVAL;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests
67262306a36Sopenharmony_ci	 * from userland.
67362306a36Sopenharmony_ci	 */
67462306a36Sopenharmony_ci	mask &= ~STATX_CHANGE_COOKIE;
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	error = vfs_statx(dfd, filename, flags, &stat, mask);
67762306a36Sopenharmony_ci	if (error)
67862306a36Sopenharmony_ci		return error;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	return cp_statx(&stat, buffer);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci/**
68462306a36Sopenharmony_ci * sys_statx - System call to get enhanced stats
68562306a36Sopenharmony_ci * @dfd: Base directory to pathwalk from *or* fd to stat.
68662306a36Sopenharmony_ci * @filename: File to stat or "" with AT_EMPTY_PATH
68762306a36Sopenharmony_ci * @flags: AT_* flags to control pathwalk.
68862306a36Sopenharmony_ci * @mask: Parts of statx struct actually required.
68962306a36Sopenharmony_ci * @buffer: Result buffer.
69062306a36Sopenharmony_ci *
69162306a36Sopenharmony_ci * Note that fstat() can be emulated by setting dfd to the fd of interest,
69262306a36Sopenharmony_ci * supplying "" as the filename and setting AT_EMPTY_PATH in the flags.
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_ciSYSCALL_DEFINE5(statx,
69562306a36Sopenharmony_ci		int, dfd, const char __user *, filename, unsigned, flags,
69662306a36Sopenharmony_ci		unsigned int, mask,
69762306a36Sopenharmony_ci		struct statx __user *, buffer)
69862306a36Sopenharmony_ci{
69962306a36Sopenharmony_ci	int ret;
70062306a36Sopenharmony_ci	struct filename *name;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	name = getname_flags(filename, getname_statx_lookup_flags(flags), NULL);
70362306a36Sopenharmony_ci	ret = do_statx(dfd, name, flags, mask, buffer);
70462306a36Sopenharmony_ci	putname(name);
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return ret;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci#if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_STAT)
71062306a36Sopenharmony_cistatic int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct compat_stat tmp;
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev))
71562306a36Sopenharmony_ci		return -EOVERFLOW;
71662306a36Sopenharmony_ci	if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev))
71762306a36Sopenharmony_ci		return -EOVERFLOW;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
72062306a36Sopenharmony_ci	tmp.st_dev = new_encode_dev(stat->dev);
72162306a36Sopenharmony_ci	tmp.st_ino = stat->ino;
72262306a36Sopenharmony_ci	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
72362306a36Sopenharmony_ci		return -EOVERFLOW;
72462306a36Sopenharmony_ci	tmp.st_mode = stat->mode;
72562306a36Sopenharmony_ci	tmp.st_nlink = stat->nlink;
72662306a36Sopenharmony_ci	if (tmp.st_nlink != stat->nlink)
72762306a36Sopenharmony_ci		return -EOVERFLOW;
72862306a36Sopenharmony_ci	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
72962306a36Sopenharmony_ci	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
73062306a36Sopenharmony_ci	tmp.st_rdev = new_encode_dev(stat->rdev);
73162306a36Sopenharmony_ci	if ((u64) stat->size > MAX_NON_LFS)
73262306a36Sopenharmony_ci		return -EOVERFLOW;
73362306a36Sopenharmony_ci	tmp.st_size = stat->size;
73462306a36Sopenharmony_ci	tmp.st_atime = stat->atime.tv_sec;
73562306a36Sopenharmony_ci	tmp.st_atime_nsec = stat->atime.tv_nsec;
73662306a36Sopenharmony_ci	tmp.st_mtime = stat->mtime.tv_sec;
73762306a36Sopenharmony_ci	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
73862306a36Sopenharmony_ci	tmp.st_ctime = stat->ctime.tv_sec;
73962306a36Sopenharmony_ci	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
74062306a36Sopenharmony_ci	tmp.st_blocks = stat->blocks;
74162306a36Sopenharmony_ci	tmp.st_blksize = stat->blksize;
74262306a36Sopenharmony_ci	return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
74362306a36Sopenharmony_ci}
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE2(newstat, const char __user *, filename,
74662306a36Sopenharmony_ci		       struct compat_stat __user *, statbuf)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct kstat stat;
74962306a36Sopenharmony_ci	int error;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	error = vfs_stat(filename, &stat);
75262306a36Sopenharmony_ci	if (error)
75362306a36Sopenharmony_ci		return error;
75462306a36Sopenharmony_ci	return cp_compat_stat(&stat, statbuf);
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE2(newlstat, const char __user *, filename,
75862306a36Sopenharmony_ci		       struct compat_stat __user *, statbuf)
75962306a36Sopenharmony_ci{
76062306a36Sopenharmony_ci	struct kstat stat;
76162306a36Sopenharmony_ci	int error;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	error = vfs_lstat(filename, &stat);
76462306a36Sopenharmony_ci	if (error)
76562306a36Sopenharmony_ci		return error;
76662306a36Sopenharmony_ci	return cp_compat_stat(&stat, statbuf);
76762306a36Sopenharmony_ci}
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci#ifndef __ARCH_WANT_STAT64
77062306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE4(newfstatat, unsigned int, dfd,
77162306a36Sopenharmony_ci		       const char __user *, filename,
77262306a36Sopenharmony_ci		       struct compat_stat __user *, statbuf, int, flag)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct kstat stat;
77562306a36Sopenharmony_ci	int error;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	error = vfs_fstatat(dfd, filename, &stat, flag);
77862306a36Sopenharmony_ci	if (error)
77962306a36Sopenharmony_ci		return error;
78062306a36Sopenharmony_ci	return cp_compat_stat(&stat, statbuf);
78162306a36Sopenharmony_ci}
78262306a36Sopenharmony_ci#endif
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ciCOMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd,
78562306a36Sopenharmony_ci		       struct compat_stat __user *, statbuf)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct kstat stat;
78862306a36Sopenharmony_ci	int error = vfs_fstat(fd, &stat);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (!error)
79162306a36Sopenharmony_ci		error = cp_compat_stat(&stat, statbuf);
79262306a36Sopenharmony_ci	return error;
79362306a36Sopenharmony_ci}
79462306a36Sopenharmony_ci#endif
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci/* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
79762306a36Sopenharmony_civoid __inode_add_bytes(struct inode *inode, loff_t bytes)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	inode->i_blocks += bytes >> 9;
80062306a36Sopenharmony_ci	bytes &= 511;
80162306a36Sopenharmony_ci	inode->i_bytes += bytes;
80262306a36Sopenharmony_ci	if (inode->i_bytes >= 512) {
80362306a36Sopenharmony_ci		inode->i_blocks++;
80462306a36Sopenharmony_ci		inode->i_bytes -= 512;
80562306a36Sopenharmony_ci	}
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ciEXPORT_SYMBOL(__inode_add_bytes);
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_civoid inode_add_bytes(struct inode *inode, loff_t bytes)
81062306a36Sopenharmony_ci{
81162306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
81262306a36Sopenharmony_ci	__inode_add_bytes(inode, bytes);
81362306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ciEXPORT_SYMBOL(inode_add_bytes);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_civoid __inode_sub_bytes(struct inode *inode, loff_t bytes)
81962306a36Sopenharmony_ci{
82062306a36Sopenharmony_ci	inode->i_blocks -= bytes >> 9;
82162306a36Sopenharmony_ci	bytes &= 511;
82262306a36Sopenharmony_ci	if (inode->i_bytes < bytes) {
82362306a36Sopenharmony_ci		inode->i_blocks--;
82462306a36Sopenharmony_ci		inode->i_bytes += 512;
82562306a36Sopenharmony_ci	}
82662306a36Sopenharmony_ci	inode->i_bytes -= bytes;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ciEXPORT_SYMBOL(__inode_sub_bytes);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_civoid inode_sub_bytes(struct inode *inode, loff_t bytes)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
83462306a36Sopenharmony_ci	__inode_sub_bytes(inode, bytes);
83562306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
83662306a36Sopenharmony_ci}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ciEXPORT_SYMBOL(inode_sub_bytes);
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ciloff_t inode_get_bytes(struct inode *inode)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci	loff_t ret;
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	spin_lock(&inode->i_lock);
84562306a36Sopenharmony_ci	ret = __inode_get_bytes(inode);
84662306a36Sopenharmony_ci	spin_unlock(&inode->i_lock);
84762306a36Sopenharmony_ci	return ret;
84862306a36Sopenharmony_ci}
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ciEXPORT_SYMBOL(inode_get_bytes);
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_civoid inode_set_bytes(struct inode *inode, loff_t bytes)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	/* Caller is here responsible for sufficient locking
85562306a36Sopenharmony_ci	 * (ie. inode->i_lock) */
85662306a36Sopenharmony_ci	inode->i_blocks = bytes >> 9;
85762306a36Sopenharmony_ci	inode->i_bytes = bytes & 511;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ciEXPORT_SYMBOL(inode_set_bytes);
861