stat.c 19.6 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4 5 6 7
/*
 *  linux/fs/stat.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

8
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
9 10 11 12 13 14 15
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/highuid.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/security.h>
16
#include <linux/cred.h>
Linus Torvalds's avatar
Linus Torvalds committed
17
#include <linux/syscalls.h>
18
#include <linux/pagemap.h>
19
#include <linux/compat.h>
Linus Torvalds's avatar
Linus Torvalds committed
20

21
#include <linux/uaccess.h>
Linus Torvalds's avatar
Linus Torvalds committed
22 23
#include <asm/unistd.h>

24 25
#include "internal.h"

26 27 28 29 30 31 32 33 34
/**
 * generic_fillattr - Fill in the basic attributes from the inode struct
 * @inode: Inode to use as the source
 * @stat: Where to fill in the attributes
 *
 * Fill in the basic attributes in the kstat structure from data that's to be
 * found on the VFS inode structure.  This is the default if no getattr inode
 * operation is supplied.
 */
Linus Torvalds's avatar
Linus Torvalds committed
35 36 37 38 39 40 41 42 43
void generic_fillattr(struct inode *inode, struct kstat *stat)
{
	stat->dev = inode->i_sb->s_dev;
	stat->ino = inode->i_ino;
	stat->mode = inode->i_mode;
	stat->nlink = inode->i_nlink;
	stat->uid = inode->i_uid;
	stat->gid = inode->i_gid;
	stat->rdev = inode->i_rdev;
44
	stat->size = i_size_read(inode);
Linus Torvalds's avatar
Linus Torvalds committed
45 46 47
	stat->atime = inode->i_atime;
	stat->mtime = inode->i_mtime;
	stat->ctime = inode->i_ctime;
Fabian Frederick's avatar
Fabian Frederick committed
48
	stat->blksize = i_blocksize(inode);
49
	stat->blocks = inode->i_blocks;
50
}
Linus Torvalds's avatar
Linus Torvalds committed
51 52
EXPORT_SYMBOL(generic_fillattr);

53 54 55 56
/**
 * vfs_getattr_nosec - getattr without security checks
 * @path: file to get attributes from
 * @stat: structure to return attributes in
57 58
 * @request_mask: STATX_xxx flags indicating what the caller wants
 * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
59 60 61 62
 *
 * Get attributes without calling security_inode_getattr.
 *
 * Currently the only caller other than vfs_getattr is internal to the
63 64
 * filehandle lookup code, which uses only the inode number and returns no
 * attributes to any user.  Any other code probably wants vfs_getattr.
65
 */
66 67
int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
		      u32 request_mask, unsigned int query_flags)
Linus Torvalds's avatar
Linus Torvalds committed
68
{
69
	struct inode *inode = d_backing_inode(path->dentry);
Linus Torvalds's avatar
Linus Torvalds committed
70

71 72 73 74
	memset(stat, 0, sizeof(*stat));
	stat->result_mask |= STATX_BASIC_STATS;
	request_mask &= STATX_ALL;
	query_flags &= KSTAT_QUERY_FLAGS;
75 76 77 78 79 80 81

	/* allow the fs to override these if it really wants to */
	if (IS_NOATIME(inode))
		stat->result_mask &= ~STATX_ATIME;
	if (IS_AUTOMOUNT(inode))
		stat->attributes |= STATX_ATTR_AUTOMOUNT;

Linus Torvalds's avatar
Linus Torvalds committed
82
	if (inode->i_op->getattr)
83 84
		return inode->i_op->getattr(path, stat, request_mask,
					    query_flags);
Linus Torvalds's avatar
Linus Torvalds committed
85 86 87 88

	generic_fillattr(inode, stat);
	return 0;
}
89 90
EXPORT_SYMBOL(vfs_getattr_nosec);

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
/*
 * vfs_getattr - Get the enhanced basic attributes of a file
 * @path: The file of interest
 * @stat: Where to return the statistics
 * @request_mask: STATX_xxx flags indicating what the caller wants
 * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
 *
 * Ask the filesystem for a file's attributes.  The caller must indicate in
 * request_mask and query_flags to indicate what they want.
 *
 * If the file is remote, the filesystem can be forced to update the attributes
 * from the backing store by passing AT_STATX_FORCE_SYNC in query_flags or can
 * suppress the update by passing AT_STATX_DONT_SYNC.
 *
 * Bits must have been set in request_mask to indicate which attributes the
 * caller wants retrieving.  Any such attribute not requested may be returned
 * anyway, but the value may be approximate, and, if remote, may not have been
 * synchronised with the server.
 *
 * 0 will be returned on success, and a -ve error code if unsuccessful.
 */
int vfs_getattr(const struct path *path, struct kstat *stat,
		u32 request_mask, unsigned int query_flags)
114 115 116
{
	int retval;

117
	retval = security_inode_getattr(path);
118 119
	if (retval)
		return retval;
120
	return vfs_getattr_nosec(path, stat, request_mask, query_flags);
121
}
Linus Torvalds's avatar
Linus Torvalds committed
122 123
EXPORT_SYMBOL(vfs_getattr);

124 125 126 127 128 129 130 131 132 133 134 135 136 137
/**
 * vfs_statx_fd - Get the enhanced basic attributes by file descriptor
 * @fd: The file descriptor referring to the file of interest
 * @stat: The result structure to fill in.
 * @request_mask: STATX_xxx flags indicating what the caller wants
 * @query_flags: Query mode (KSTAT_QUERY_FLAGS)
 *
 * This function is a wrapper around vfs_getattr().  The main difference is
 * that it uses a file descriptor to determine the file location.
 *
 * 0 will be returned on success, and a -ve error code if unsuccessful.
 */
int vfs_statx_fd(unsigned int fd, struct kstat *stat,
		 u32 request_mask, unsigned int query_flags)
Linus Torvalds's avatar
Linus Torvalds committed
138
{
139
	struct fd f;
Linus Torvalds's avatar
Linus Torvalds committed
140 141
	int error = -EBADF;

142 143 144 145
	if (query_flags & ~KSTAT_QUERY_FLAGS)
		return -EINVAL;

	f = fdget_raw(fd);
146
	if (f.file) {
147 148
		error = vfs_getattr(&f.file->f_path, stat,
				    request_mask, query_flags);
149
		fdput(f);
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152
	}
	return error;
}
153
EXPORT_SYMBOL(vfs_statx_fd);
Linus Torvalds's avatar
Linus Torvalds committed
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
inline unsigned vfs_stat_set_lookup_flags(unsigned *lookup_flags, int flags)
{
	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
		return -EINVAL;

	*lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
	if (flags & AT_SYMLINK_NOFOLLOW)
		*lookup_flags &= ~LOOKUP_FOLLOW;
	if (flags & AT_NO_AUTOMOUNT)
		*lookup_flags &= ~LOOKUP_AUTOMOUNT;
	if (flags & AT_EMPTY_PATH)
		*lookup_flags |= LOOKUP_EMPTY;

	return 0;
}

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
/**
 * vfs_statx - Get basic and extra attributes by filename
 * @dfd: A file descriptor representing the base dir for a relative filename
 * @filename: The name of the file of interest
 * @flags: Flags to control the query
 * @stat: The result structure to fill in.
 * @request_mask: STATX_xxx flags indicating what the caller wants
 *
 * This function is a wrapper around vfs_getattr().  The main difference is
 * that it uses a filename and base directory to determine the file location.
 * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink
 * at the given name from being referenced.
 *
 * 0 will be returned on success, and a -ve error code if unsuccessful.
 */
int vfs_statx(int dfd, const char __user *filename, int flags,
	      struct kstat *stat, u32 request_mask)
189
{
190
	struct path path;
191
	int error = -EINVAL;
192
	unsigned lookup_flags;
193

194
	if (vfs_stat_set_lookup_flags(&lookup_flags, flags))
195
		return -EINVAL;
196
retry:
197 198 199 200
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (error)
		goto out;

201
	error = vfs_getattr(&path, stat, request_mask, flags);
202
	path_put(&path);
203 204 205 206
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
207 208 209
out:
	return error;
}
210
EXPORT_SYMBOL(vfs_statx);
211

212

Linus Torvalds's avatar
Linus Torvalds committed
213 214 215 216 217 218 219 220 221 222
#ifdef __ARCH_WANT_OLD_STAT

/*
 * For backward compatibility?  Maybe this should be moved
 * into arch/i386 instead?
 */
static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf)
{
	static int warncount = 5;
	struct __old_kernel_stat tmp;
223

Linus Torvalds's avatar
Linus Torvalds committed
224 225 226 227 228 229 230 231 232 233 234 235
	if (warncount > 0) {
		warncount--;
		printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n",
			current->comm);
	} else if (warncount < 0) {
		/* it's laughable, but... */
		warncount = 0;
	}

	memset(&tmp, 0, sizeof(struct __old_kernel_stat));
	tmp.st_dev = old_encode_dev(stat->dev);
	tmp.st_ino = stat->ino;
236 237
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
238 239 240 241
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
242 243
	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
Linus Torvalds's avatar
Linus Torvalds committed
244 245 246 247
	tmp.st_rdev = old_encode_dev(stat->rdev);
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
248
#endif
Linus Torvalds's avatar
Linus Torvalds committed
249 250 251 252 253 254 255
	tmp.st_size = stat->size;
	tmp.st_atime = stat->atime.tv_sec;
	tmp.st_mtime = stat->mtime.tv_sec;
	tmp.st_ctime = stat->ctime.tv_sec;
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}

256 257
SYSCALL_DEFINE2(stat, const char __user *, filename,
		struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
258 259
{
	struct kstat stat;
260
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
261

262 263 264
	error = vfs_stat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
265

266
	return cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
267
}
268

269 270
SYSCALL_DEFINE2(lstat, const char __user *, filename,
		struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
271 272
{
	struct kstat stat;
273
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
274

275 276 277
	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
278

279
	return cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
280
}
281 282

SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
283 284 285 286 287 288 289 290 291 292 293 294
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_old_stat(&stat, statbuf);

	return error;
}

#endif /* __ARCH_WANT_OLD_STAT */

295 296
#ifdef __ARCH_WANT_NEW_STAT

297 298 299 300 301 302
#if BITS_PER_LONG == 32
#  define choose_32_64(a,b) a
#else
#  define choose_32_64(a,b) b
#endif

303
#define valid_dev(x)  choose_32_64(old_valid_dev(x),true)
304 305
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)

306 307 308 309
#ifndef INIT_STRUCT_STAT_PADDING
#  define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
310 311 312 313
static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
{
	struct stat tmp;

314
	if (!valid_dev(stat->dev) || !valid_dev(stat->rdev))
Linus Torvalds's avatar
Linus Torvalds committed
315
		return -EOVERFLOW;
316 317
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
Linus Torvalds's avatar
Linus Torvalds committed
318 319 320
		return -EOVERFLOW;
#endif

321
	INIT_STRUCT_STAT_PADDING(tmp);
322
	tmp.st_dev = encode_dev(stat->dev);
Linus Torvalds's avatar
Linus Torvalds committed
323
	tmp.st_ino = stat->ino;
324 325
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
326 327 328 329
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
330 331
	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
332
	tmp.st_rdev = encode_dev(stat->rdev);
Linus Torvalds's avatar
Linus Torvalds committed
333 334 335 336 337 338 339 340 341 342 343 344 345 346
	tmp.st_size = stat->size;
	tmp.st_atime = stat->atime.tv_sec;
	tmp.st_mtime = stat->mtime.tv_sec;
	tmp.st_ctime = stat->ctime.tv_sec;
#ifdef STAT_HAVE_NSEC
	tmp.st_atime_nsec = stat->atime.tv_nsec;
	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
#endif
	tmp.st_blocks = stat->blocks;
	tmp.st_blksize = stat->blksize;
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}

347 348
SYSCALL_DEFINE2(newstat, const char __user *, filename,
		struct stat __user *, statbuf)
349 350
{
	struct kstat stat;
351
	int error = vfs_stat(filename, &stat);
352

353 354 355
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
356 357
}

358 359
SYSCALL_DEFINE2(newlstat, const char __user *, filename,
		struct stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
360 361
{
	struct kstat stat;
362
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
363

364 365 366
	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
367

368
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
369
}
370

371
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
372
SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
373
		struct stat __user *, statbuf, int, flag)
Linus Torvalds's avatar
Linus Torvalds committed
374 375
{
	struct kstat stat;
376
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
377

378 379 380 381
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
382
}
Ulrich Drepper's avatar
Ulrich Drepper committed
383
#endif
384

385
SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
386 387 388 389 390 391 392 393 394
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_new_stat(&stat, statbuf);

	return error;
}
395
#endif
Linus Torvalds's avatar
Linus Torvalds committed
396

397 398
static int do_readlinkat(int dfd, const char __user *pathname,
			 char __user *buf, int bufsiz)
Linus Torvalds's avatar
Linus Torvalds committed
399
{
400
	struct path path;
Linus Torvalds's avatar
Linus Torvalds committed
401
	int error;
402
	int empty = 0;
403
	unsigned int lookup_flags = LOOKUP_EMPTY;
Linus Torvalds's avatar
Linus Torvalds committed
404 405 406 407

	if (bufsiz <= 0)
		return -EINVAL;

408 409
retry:
	error = user_path_at_empty(dfd, pathname, lookup_flags, &path, &empty);
Linus Torvalds's avatar
Linus Torvalds committed
410
	if (!error) {
411
		struct inode *inode = d_backing_inode(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
412

413
		error = empty ? -ENOENT : -EINVAL;
414 415 416 417
		/*
		 * AFS mountpoints allow readlink(2) but are not symlinks
		 */
		if (d_is_symlink(path.dentry) || inode->i_op->readlink) {
418
			error = security_inode_readlink(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
419
			if (!error) {
Al Viro's avatar
Al Viro committed
420
				touch_atime(&path);
421
				error = vfs_readlink(path.dentry, buf, bufsiz);
Linus Torvalds's avatar
Linus Torvalds committed
422 423
			}
		}
424
		path_put(&path);
425 426 427 428
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
Linus Torvalds's avatar
Linus Torvalds committed
429 430 431 432
	}
	return error;
}

433 434 435 436 437 438
SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
		char __user *, buf, int, bufsiz)
{
	return do_readlinkat(dfd, pathname, buf, bufsiz);
}

439 440
SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
		int, bufsiz)
441
{
442
	return do_readlinkat(AT_FDCWD, path, buf, bufsiz);
443 444
}

Linus Torvalds's avatar
Linus Torvalds committed
445 446

/* ---------- LFS-64 ----------- */
447
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
Linus Torvalds's avatar
Linus Torvalds committed
448

449 450 451 452
#ifndef INIT_STRUCT_STAT64_PADDING
#  define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
453 454 455 456
static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
{
	struct stat64 tmp;

457
	INIT_STRUCT_STAT64_PADDING(tmp);
Linus Torvalds's avatar
Linus Torvalds committed
458 459 460 461 462 463 464 465 466
#ifdef CONFIG_MIPS
	/* mips has weird padding, so we don't get 64 bits there */
	tmp.st_dev = new_encode_dev(stat->dev);
	tmp.st_rdev = new_encode_dev(stat->rdev);
#else
	tmp.st_dev = huge_encode_dev(stat->dev);
	tmp.st_rdev = huge_encode_dev(stat->rdev);
#endif
	tmp.st_ino = stat->ino;
467 468
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
469 470 471 472 473
#ifdef STAT64_HAS_BROKEN_ST_INO
	tmp.__st_ino = stat->ino;
#endif
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
474 475
	tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid);
	tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid);
Linus Torvalds's avatar
Linus Torvalds committed
476 477 478 479 480 481 482 483 484 485 486 487
	tmp.st_atime = stat->atime.tv_sec;
	tmp.st_atime_nsec = stat->atime.tv_nsec;
	tmp.st_mtime = stat->mtime.tv_sec;
	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
	tmp.st_ctime = stat->ctime.tv_sec;
	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
	tmp.st_size = stat->size;
	tmp.st_blocks = stat->blocks;
	tmp.st_blksize = stat->blksize;
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}

488 489
SYSCALL_DEFINE2(stat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
490 491 492 493 494 495 496 497 498
{
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);

	return error;
}
499

500 501
SYSCALL_DEFINE2(lstat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
502 503 504 505 506 507 508 509 510
{
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);

	return error;
}
511 512

SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
513 514 515 516 517 518 519 520 521 522
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);

	return error;
}

523
SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
524
		struct stat64 __user *, statbuf, int, flag)
Ulrich Drepper's avatar
Ulrich Drepper committed
525 526
{
	struct kstat stat;
527
	int error;
Ulrich Drepper's avatar
Ulrich Drepper committed
528

529 530 531 532
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat64(&stat, statbuf);
Ulrich Drepper's avatar
Ulrich Drepper committed
533
}
534
#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
Linus Torvalds's avatar
Linus Torvalds committed
535

536
noinline_for_stack int
537
cp_statx(const struct kstat *stat, struct statx __user *buffer)
538
{
539 540 541 542 543 544 545 546 547 548 549 550 551 552
	struct statx tmp;

	memset(&tmp, 0, sizeof(tmp));

	tmp.stx_mask = stat->result_mask;
	tmp.stx_blksize = stat->blksize;
	tmp.stx_attributes = stat->attributes;
	tmp.stx_nlink = stat->nlink;
	tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid);
	tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid);
	tmp.stx_mode = stat->mode;
	tmp.stx_ino = stat->ino;
	tmp.stx_size = stat->size;
	tmp.stx_blocks = stat->blocks;
553
	tmp.stx_attributes_mask = stat->attributes_mask;
554 555 556 557 558 559 560 561 562 563 564 565 566 567
	tmp.stx_atime.tv_sec = stat->atime.tv_sec;
	tmp.stx_atime.tv_nsec = stat->atime.tv_nsec;
	tmp.stx_btime.tv_sec = stat->btime.tv_sec;
	tmp.stx_btime.tv_nsec = stat->btime.tv_nsec;
	tmp.stx_ctime.tv_sec = stat->ctime.tv_sec;
	tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec;
	tmp.stx_mtime.tv_sec = stat->mtime.tv_sec;
	tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec;
	tmp.stx_rdev_major = MAJOR(stat->rdev);
	tmp.stx_rdev_minor = MINOR(stat->rdev);
	tmp.stx_dev_major = MAJOR(stat->dev);
	tmp.stx_dev_minor = MINOR(stat->dev);

	return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0;
568 569 570 571 572
}

/**
 * sys_statx - System call to get enhanced stats
 * @dfd: Base directory to pathwalk from *or* fd to stat.
573
 * @filename: File to stat or "" with AT_EMPTY_PATH
574 575 576 577
 * @flags: AT_* flags to control pathwalk.
 * @mask: Parts of statx struct actually required.
 * @buffer: Result buffer.
 *
578 579
 * Note that fstat() can be emulated by setting dfd to the fd of interest,
 * supplying "" as the filename and setting AT_EMPTY_PATH in the flags.
580 581 582 583 584 585 586 587 588
 */
SYSCALL_DEFINE5(statx,
		int, dfd, const char __user *, filename, unsigned, flags,
		unsigned int, mask,
		struct statx __user *, buffer)
{
	struct kstat stat;
	int error;

589 590
	if (mask & STATX__RESERVED)
		return -EINVAL;
591 592 593
	if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
		return -EINVAL;

594
	error = vfs_statx(dfd, filename, flags, &stat, mask);
595 596
	if (error)
		return error;
597 598

	return cp_statx(&stat, buffer);
599 600
}

601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
#ifdef CONFIG_COMPAT
static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf)
{
	struct compat_stat tmp;

	if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev))
		return -EOVERFLOW;

	memset(&tmp, 0, sizeof(tmp));
	tmp.st_dev = old_encode_dev(stat->dev);
	tmp.st_ino = stat->ino;
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
	SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid));
	SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid));
	tmp.st_rdev = old_encode_dev(stat->rdev);
	if ((u64) stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
	tmp.st_size = stat->size;
	tmp.st_atime = stat->atime.tv_sec;
	tmp.st_atime_nsec = stat->atime.tv_nsec;
	tmp.st_mtime = stat->mtime.tv_sec;
	tmp.st_mtime_nsec = stat->mtime.tv_nsec;
	tmp.st_ctime = stat->ctime.tv_sec;
	tmp.st_ctime_nsec = stat->ctime.tv_nsec;
	tmp.st_blocks = stat->blocks;
	tmp.st_blksize = stat->blksize;
	return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}

COMPAT_SYSCALL_DEFINE2(newstat, const char __user *, filename,
		       struct compat_stat __user *, statbuf)
{
	struct kstat stat;
	int error;

	error = vfs_stat(filename, &stat);
	if (error)
		return error;
	return cp_compat_stat(&stat, statbuf);
}

COMPAT_SYSCALL_DEFINE2(newlstat, const char __user *, filename,
		       struct compat_stat __user *, statbuf)
{
	struct kstat stat;
	int error;

	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
	return cp_compat_stat(&stat, statbuf);
}

#ifndef __ARCH_WANT_STAT64
COMPAT_SYSCALL_DEFINE4(newfstatat, unsigned int, dfd,
		       const char __user *, filename,
		       struct compat_stat __user *, statbuf, int, flag)
{
	struct kstat stat;
	int error;

	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_compat_stat(&stat, statbuf);
}
#endif

COMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd,
		       struct compat_stat __user *, statbuf)
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

	if (!error)
		error = cp_compat_stat(&stat, statbuf);
	return error;
}
#endif

686 687
/* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
void __inode_add_bytes(struct inode *inode, loff_t bytes)
Linus Torvalds's avatar
Linus Torvalds committed
688 689 690 691 692 693 694 695
{
	inode->i_blocks += bytes >> 9;
	bytes &= 511;
	inode->i_bytes += bytes;
	if (inode->i_bytes >= 512) {
		inode->i_blocks++;
		inode->i_bytes -= 512;
	}
696
}
Al Viro's avatar
Al Viro committed
697
EXPORT_SYMBOL(__inode_add_bytes);
698 699 700 701 702

void inode_add_bytes(struct inode *inode, loff_t bytes)
{
	spin_lock(&inode->i_lock);
	__inode_add_bytes(inode, bytes);
Linus Torvalds's avatar
Linus Torvalds committed
703 704 705 706 707
	spin_unlock(&inode->i_lock);
}

EXPORT_SYMBOL(inode_add_bytes);

708
void __inode_sub_bytes(struct inode *inode, loff_t bytes)
Linus Torvalds's avatar
Linus Torvalds committed
709 710 711 712 713 714 715 716
{
	inode->i_blocks -= bytes >> 9;
	bytes &= 511;
	if (inode->i_bytes < bytes) {
		inode->i_blocks--;
		inode->i_bytes += 512;
	}
	inode->i_bytes -= bytes;
717 718 719 720 721 722 723 724
}

EXPORT_SYMBOL(__inode_sub_bytes);

void inode_sub_bytes(struct inode *inode, loff_t bytes)
{
	spin_lock(&inode->i_lock);
	__inode_sub_bytes(inode, bytes);
Linus Torvalds's avatar
Linus Torvalds committed
725 726 727 728 729 730 731 732 733 734
	spin_unlock(&inode->i_lock);
}

EXPORT_SYMBOL(inode_sub_bytes);

loff_t inode_get_bytes(struct inode *inode)
{
	loff_t ret;

	spin_lock(&inode->i_lock);
Jan Kara's avatar
Jan Kara committed
735
	ret = __inode_get_bytes(inode);
Linus Torvalds's avatar
Linus Torvalds committed
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
	spin_unlock(&inode->i_lock);
	return ret;
}

EXPORT_SYMBOL(inode_get_bytes);

void inode_set_bytes(struct inode *inode, loff_t bytes)
{
	/* Caller is here responsible for sufficient locking
	 * (ie. inode->i_lock) */
	inode->i_blocks = bytes >> 9;
	inode->i_bytes = bytes & 511;
}

EXPORT_SYMBOL(inode_set_bytes);