stat.c 17.5 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
3
4
5
6
/*
 *  linux/fs/stat.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

7
#include <linux/export.h>
Linus Torvalds's avatar
Linus Torvalds committed
8
9
10
11
12
13
14
#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>
15
#include <linux/cred.h>
Linus Torvalds's avatar
Linus Torvalds committed
16
#include <linux/syscalls.h>
17
#include <linux/pagemap.h>
Linus Torvalds's avatar
Linus Torvalds committed
18

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

22
23
24
25
26
27
28
29
30
/**
 * 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
31
32
33
34
35
36
37
38
39
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;
40
	stat->size = i_size_read(inode);
Linus Torvalds's avatar
Linus Torvalds committed
41
42
43
	stat->atime = inode->i_atime;
	stat->mtime = inode->i_mtime;
	stat->ctime = inode->i_ctime;
Fabian Frederick's avatar
Fabian Frederick committed
44
	stat->blksize = i_blocksize(inode);
45
	stat->blocks = inode->i_blocks;
Linus Torvalds's avatar
Linus Torvalds committed
46

47
48
49
50
51
	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
52
53
EXPORT_SYMBOL(generic_fillattr);

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

72
73
74
75
	memset(stat, 0, sizeof(*stat));
	stat->result_mask |= STATX_BASIC_STATS;
	request_mask &= STATX_ALL;
	query_flags &= KSTAT_QUERY_FLAGS;
Linus Torvalds's avatar
Linus Torvalds committed
76
	if (inode->i_op->getattr)
77
78
		return inode->i_op->getattr(path, stat, request_mask,
					    query_flags);
Linus Torvalds's avatar
Linus Torvalds committed
79
80
81
82

	generic_fillattr(inode, stat);
	return 0;
}
83
84
EXPORT_SYMBOL(vfs_getattr_nosec);

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*
 * 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)
108
109
110
{
	int retval;

111
	retval = security_inode_getattr(path);
112
113
	if (retval)
		return retval;
114
	return vfs_getattr_nosec(path, stat, request_mask, query_flags);
115
}
Linus Torvalds's avatar
Linus Torvalds committed
116
117
EXPORT_SYMBOL(vfs_getattr);

118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
 * 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
132
{
133
	struct fd f;
Linus Torvalds's avatar
Linus Torvalds committed
134
135
	int error = -EBADF;

136
137
138
139
	if (query_flags & ~KSTAT_QUERY_FLAGS)
		return -EINVAL;

	f = fdget_raw(fd);
140
	if (f.file) {
141
142
		error = vfs_getattr(&f.file->f_path, stat,
				    request_mask, query_flags);
143
		fdput(f);
Linus Torvalds's avatar
Linus Torvalds committed
144
145
146
	}
	return error;
}
147
EXPORT_SYMBOL(vfs_statx_fd);
Linus Torvalds's avatar
Linus Torvalds committed
148

149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/**
 * 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)
166
{
167
	struct path path;
168
	int error = -EINVAL;
169
	unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT;
170

171
172
173
	if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
		       AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0)
		return -EINVAL;
174

175
176
177
178
179
	if (flags & AT_SYMLINK_NOFOLLOW)
		lookup_flags &= ~LOOKUP_FOLLOW;
	if (flags & AT_NO_AUTOMOUNT)
		lookup_flags &= ~LOOKUP_AUTOMOUNT;
	if (flags & AT_EMPTY_PATH)
180
		lookup_flags |= LOOKUP_EMPTY;
181

182
retry:
183
184
185
186
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (error)
		goto out;

187
	error = vfs_getattr(&path, stat, request_mask, flags);
188
	path_put(&path);
189
190
191
192
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
193
194
195
out:
	return error;
}
196
EXPORT_SYMBOL(vfs_statx);
197

198

Linus Torvalds's avatar
Linus Torvalds committed
199
200
201
202
203
204
205
206
207
208
#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;
209

Linus Torvalds's avatar
Linus Torvalds committed
210
211
212
213
214
215
216
217
218
219
220
221
	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;
222
223
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
224
225
226
227
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
228
229
	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
230
231
232
233
	tmp.st_rdev = old_encode_dev(stat->rdev);
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
234
#endif
Linus Torvalds's avatar
Linus Torvalds committed
235
236
237
238
239
240
241
	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;
}

242
243
SYSCALL_DEFINE2(stat, const char __user *, filename,
		struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
244
245
{
	struct kstat stat;
246
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
247

248
249
250
	error = vfs_stat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
251

252
	return cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
253
}
254

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

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

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

SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
269
270
271
272
273
274
275
276
277
278
279
280
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

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

	return error;
}

#endif /* __ARCH_WANT_OLD_STAT */

281
282
283
284
285
286
#if BITS_PER_LONG == 32
#  define choose_32_64(a,b) a
#else
#  define choose_32_64(a,b) b
#endif

287
#define valid_dev(x)  choose_32_64(old_valid_dev(x),true)
288
289
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)

290
291
292
293
#ifndef INIT_STRUCT_STAT_PADDING
#  define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
294
295
296
297
static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
{
	struct stat tmp;

298
	if (!valid_dev(stat->dev) || !valid_dev(stat->rdev))
Linus Torvalds's avatar
Linus Torvalds committed
299
		return -EOVERFLOW;
300
301
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
		return -EOVERFLOW;
#endif

305
	INIT_STRUCT_STAT_PADDING(tmp);
306
	tmp.st_dev = encode_dev(stat->dev);
Linus Torvalds's avatar
Linus Torvalds committed
307
	tmp.st_ino = stat->ino;
308
309
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
310
311
312
313
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
314
315
	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));
316
	tmp.st_rdev = encode_dev(stat->rdev);
Linus Torvalds's avatar
Linus Torvalds committed
317
318
319
320
321
322
323
324
325
326
327
328
329
330
	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;
}

331
332
SYSCALL_DEFINE2(newstat, const char __user *, filename,
		struct stat __user *, statbuf)
333
334
{
	struct kstat stat;
335
	int error = vfs_stat(filename, &stat);
336

337
338
339
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
340
341
}

342
343
SYSCALL_DEFINE2(newlstat, const char __user *, filename,
		struct stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
344
345
{
	struct kstat stat;
346
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
347

348
349
350
	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
351

352
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
353
}
354

355
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
356
SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
357
		struct stat __user *, statbuf, int, flag)
Linus Torvalds's avatar
Linus Torvalds committed
358
359
{
	struct kstat stat;
360
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
361

362
363
364
365
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
366
}
Ulrich Drepper's avatar
Ulrich Drepper committed
367
#endif
368

369
SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
370
371
372
373
374
375
376
377
378
379
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

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

	return error;
}

380
381
SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
		char __user *, buf, int, bufsiz)
Linus Torvalds's avatar
Linus Torvalds committed
382
{
383
	struct path path;
Linus Torvalds's avatar
Linus Torvalds committed
384
	int error;
385
	int empty = 0;
386
	unsigned int lookup_flags = LOOKUP_EMPTY;
Linus Torvalds's avatar
Linus Torvalds committed
387
388
389
390

	if (bufsiz <= 0)
		return -EINVAL;

391
392
retry:
	error = user_path_at_empty(dfd, pathname, lookup_flags, &path, &empty);
Linus Torvalds's avatar
Linus Torvalds committed
393
	if (!error) {
394
		struct inode *inode = d_backing_inode(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
395

396
		error = empty ? -ENOENT : -EINVAL;
397
398
399
400
		/*
		 * AFS mountpoints allow readlink(2) but are not symlinks
		 */
		if (d_is_symlink(path.dentry) || inode->i_op->readlink) {
401
			error = security_inode_readlink(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
402
			if (!error) {
Al Viro's avatar
Al Viro committed
403
				touch_atime(&path);
404
				error = vfs_readlink(path.dentry, buf, bufsiz);
Linus Torvalds's avatar
Linus Torvalds committed
405
406
			}
		}
407
		path_put(&path);
408
409
410
411
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
Linus Torvalds's avatar
Linus Torvalds committed
412
413
414
415
	}
	return error;
}

416
417
SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
		int, bufsiz)
418
419
420
421
{
	return sys_readlinkat(AT_FDCWD, path, buf, bufsiz);
}

Linus Torvalds's avatar
Linus Torvalds committed
422
423

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

426
427
428
429
#ifndef INIT_STRUCT_STAT64_PADDING
#  define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
430
431
432
433
static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
{
	struct stat64 tmp;

434
	INIT_STRUCT_STAT64_PADDING(tmp);
Linus Torvalds's avatar
Linus Torvalds committed
435
436
437
438
439
440
441
442
443
#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;
444
445
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
446
447
448
449
450
#ifdef STAT64_HAS_BROKEN_ST_INO
	tmp.__st_ino = stat->ino;
#endif
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
451
452
	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
453
454
455
456
457
458
459
460
461
462
463
464
	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;
}

465
466
SYSCALL_DEFINE2(stat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
467
468
469
470
471
472
473
474
475
{
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

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

	return error;
}
476

477
478
SYSCALL_DEFINE2(lstat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
479
480
481
482
483
484
485
486
487
{
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);

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

	return error;
}
488
489

SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494
495
496
497
498
499
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

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

	return error;
}

500
SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
501
		struct stat64 __user *, statbuf, int, flag)
Ulrich Drepper's avatar
Ulrich Drepper committed
502
503
{
	struct kstat stat;
504
	int error;
Ulrich Drepper's avatar
Ulrich Drepper committed
505

506
507
508
509
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat64(&stat, statbuf);
Ulrich Drepper's avatar
Ulrich Drepper committed
510
}
511
#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
Linus Torvalds's avatar
Linus Torvalds committed
512

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
static inline int __put_timestamp(struct timespec *kts,
				  struct statx_timestamp __user *uts)
{
	return (__put_user(kts->tv_sec,		&uts->tv_sec		) ||
		__put_user(kts->tv_nsec,	&uts->tv_nsec		) ||
		__put_user(0,			&uts->__reserved	));
}

/*
 * Set the statx results.
 */
static long statx_set_result(struct kstat *stat, struct statx __user *buffer)
{
	uid_t uid = from_kuid_munged(current_user_ns(), stat->uid);
	gid_t gid = from_kgid_munged(current_user_ns(), stat->gid);

	if (__put_user(stat->result_mask,	&buffer->stx_mask	) ||
	    __put_user(stat->mode,		&buffer->stx_mode	) ||
	    __clear_user(&buffer->__spare0, sizeof(buffer->__spare0))	  ||
	    __put_user(stat->nlink,		&buffer->stx_nlink	) ||
	    __put_user(uid,			&buffer->stx_uid	) ||
	    __put_user(gid,			&buffer->stx_gid	) ||
	    __put_user(stat->attributes,	&buffer->stx_attributes	) ||
	    __put_user(stat->blksize,		&buffer->stx_blksize	) ||
	    __put_user(MAJOR(stat->rdev),	&buffer->stx_rdev_major	) ||
	    __put_user(MINOR(stat->rdev),	&buffer->stx_rdev_minor	) ||
	    __put_user(MAJOR(stat->dev),	&buffer->stx_dev_major	) ||
	    __put_user(MINOR(stat->dev),	&buffer->stx_dev_minor	) ||
	    __put_timestamp(&stat->atime,	&buffer->stx_atime	) ||
	    __put_timestamp(&stat->btime,	&buffer->stx_btime	) ||
	    __put_timestamp(&stat->ctime,	&buffer->stx_ctime	) ||
	    __put_timestamp(&stat->mtime,	&buffer->stx_mtime	) ||
	    __put_user(stat->ino,		&buffer->stx_ino	) ||
	    __put_user(stat->size,		&buffer->stx_size	) ||
	    __put_user(stat->blocks,		&buffer->stx_blocks	) ||
	    __clear_user(&buffer->__spare1, sizeof(buffer->__spare1))	  ||
	    __clear_user(&buffer->__spare2, sizeof(buffer->__spare2)))
		return -EFAULT;

	return 0;
}

/**
 * sys_statx - System call to get enhanced stats
 * @dfd: Base directory to pathwalk from *or* fd to stat.
 * @filename: File to stat *or* NULL.
 * @flags: AT_* flags to control pathwalk.
 * @mask: Parts of statx struct actually required.
 * @buffer: Result buffer.
 *
 * Note that if filename is NULL, then it does the equivalent of fstat() using
 * dfd to indicate the file of interest.
 */
SYSCALL_DEFINE5(statx,
		int, dfd, const char __user *, filename, unsigned, flags,
		unsigned int, mask,
		struct statx __user *, buffer)
{
	struct kstat stat;
	int error;

	if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE)
		return -EINVAL;
	if (!access_ok(VERIFY_WRITE, buffer, sizeof(*buffer)))
		return -EFAULT;

	if (filename)
		error = vfs_statx(dfd, filename, flags, &stat, mask);
	else
		error = vfs_statx_fd(dfd, &stat, mask, flags);
	if (error)
		return error;
	return statx_set_result(&stat, buffer);
}

588
589
/* 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
590
591
592
593
594
595
596
597
{
	inode->i_blocks += bytes >> 9;
	bytes &= 511;
	inode->i_bytes += bytes;
	if (inode->i_bytes >= 512) {
		inode->i_blocks++;
		inode->i_bytes -= 512;
	}
598
599
600
601
602
603
}

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
604
605
606
607
608
	spin_unlock(&inode->i_lock);
}

EXPORT_SYMBOL(inode_add_bytes);

609
void __inode_sub_bytes(struct inode *inode, loff_t bytes)
Linus Torvalds's avatar
Linus Torvalds committed
610
611
612
613
614
615
616
617
{
	inode->i_blocks -= bytes >> 9;
	bytes &= 511;
	if (inode->i_bytes < bytes) {
		inode->i_blocks--;
		inode->i_bytes += 512;
	}
	inode->i_bytes -= bytes;
618
619
620
621
622
623
624
625
}

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
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
	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);
	ret = (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
	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);