stat.c 11.4 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 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>
#include <linux/syscalls.h>
16
#include <linux/pagemap.h>
Linus Torvalds's avatar
Linus Torvalds committed
17 18 19 20 21 22 23 24 25 26 27 28 29

#include <asm/uaccess.h>
#include <asm/unistd.h>

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;
30
	stat->size = i_size_read(inode);
Linus Torvalds's avatar
Linus Torvalds committed
31 32 33
	stat->atime = inode->i_atime;
	stat->mtime = inode->i_mtime;
	stat->ctime = inode->i_ctime;
34
	stat->blksize = (1 << inode->i_blkbits);
35
	stat->blocks = inode->i_blocks;
Linus Torvalds's avatar
Linus Torvalds committed
36 37 38 39
}

EXPORT_SYMBOL(generic_fillattr);

40
int vfs_getattr(struct path *path, struct kstat *stat)
Linus Torvalds's avatar
Linus Torvalds committed
41
{
42
	struct inode *inode = path->dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
43 44
	int retval;

45
	retval = security_inode_getattr(path->mnt, path->dentry);
Linus Torvalds's avatar
Linus Torvalds committed
46 47 48 49
	if (retval)
		return retval;

	if (inode->i_op->getattr)
50
		return inode->i_op->getattr(path->mnt, path->dentry, stat);
Linus Torvalds's avatar
Linus Torvalds committed
51 52 53 54 55 56 57 58 59

	generic_fillattr(inode, stat);
	return 0;
}

EXPORT_SYMBOL(vfs_getattr);

int vfs_fstat(unsigned int fd, struct kstat *stat)
{
60
	struct fd f = fdget_raw(fd);
Linus Torvalds's avatar
Linus Torvalds committed
61 62
	int error = -EBADF;

63
	if (f.file) {
64
		error = vfs_getattr(&f.file->f_path, stat);
65
		fdput(f);
Linus Torvalds's avatar
Linus Torvalds committed
66 67 68 69 70
	}
	return error;
}
EXPORT_SYMBOL(vfs_fstat);

71 72
int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
		int flag)
73
{
74
	struct path path;
75
	int error = -EINVAL;
76
	unsigned int lookup_flags = 0;
77

78 79
	if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
		      AT_EMPTY_PATH)) != 0)
80 81
		goto out;

82 83
	if (!(flag & AT_SYMLINK_NOFOLLOW))
		lookup_flags |= LOOKUP_FOLLOW;
84 85
	if (flag & AT_EMPTY_PATH)
		lookup_flags |= LOOKUP_EMPTY;
86
retry:
87 88 89 90
	error = user_path_at(dfd, filename, lookup_flags, &path);
	if (error)
		goto out;

91
	error = vfs_getattr(&path, stat);
92
	path_put(&path);
93 94 95 96
	if (retry_estale(error, lookup_flags)) {
		lookup_flags |= LOOKUP_REVAL;
		goto retry;
	}
97 98 99 100 101
out:
	return error;
}
EXPORT_SYMBOL(vfs_fstatat);

102
int vfs_stat(const char __user *name, struct kstat *stat)
103 104 105 106 107
{
	return vfs_fstatat(AT_FDCWD, name, stat, 0);
}
EXPORT_SYMBOL(vfs_stat);

108
int vfs_lstat(const char __user *name, struct kstat *stat)
109 110 111 112 113
{
	return vfs_fstatat(AT_FDCWD, name, stat, AT_SYMLINK_NOFOLLOW);
}
EXPORT_SYMBOL(vfs_lstat);

114

Linus Torvalds's avatar
Linus Torvalds committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
#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;
	
	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;
138 139
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
140 141 142 143
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
144 145
	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
146 147 148 149 150 151 152 153 154 155 156 157
	tmp.st_rdev = old_encode_dev(stat->rdev);
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
#endif	
	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;
}

158 159
SYSCALL_DEFINE2(stat, const char __user *, filename,
		struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
160 161
{
	struct kstat stat;
162
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
163

164 165 166
	error = vfs_stat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
167

168
	return cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
169
}
170

171 172
SYSCALL_DEFINE2(lstat, const char __user *, filename,
		struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
173 174
{
	struct kstat stat;
175
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
176

177 178 179
	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
180

181
	return cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
182
}
183 184

SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
185 186 187 188 189 190 191 192 193 194 195 196
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

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

	return error;
}

#endif /* __ARCH_WANT_OLD_STAT */

197 198 199 200 201 202 203 204 205
#if BITS_PER_LONG == 32
#  define choose_32_64(a,b) a
#else
#  define choose_32_64(a,b) b
#endif

#define valid_dev(x)  choose_32_64(old_valid_dev,new_valid_dev)(x)
#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x)

206 207 208 209
#ifndef INIT_STRUCT_STAT_PADDING
#  define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
210 211 212 213
static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
{
	struct stat tmp;

214
	if (!valid_dev(stat->dev) || !valid_dev(stat->rdev))
Linus Torvalds's avatar
Linus Torvalds committed
215
		return -EOVERFLOW;
216 217
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
Linus Torvalds's avatar
Linus Torvalds committed
218 219 220
		return -EOVERFLOW;
#endif

221
	INIT_STRUCT_STAT_PADDING(tmp);
222
	tmp.st_dev = encode_dev(stat->dev);
Linus Torvalds's avatar
Linus Torvalds committed
223
	tmp.st_ino = stat->ino;
224 225
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
226 227 228 229
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	if (tmp.st_nlink != stat->nlink)
		return -EOVERFLOW;
230 231
	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));
232
	tmp.st_rdev = encode_dev(stat->rdev);
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235 236 237 238 239 240 241 242 243 244 245 246
	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;
}

247 248
SYSCALL_DEFINE2(newstat, const char __user *, filename,
		struct stat __user *, statbuf)
249 250
{
	struct kstat stat;
251
	int error = vfs_stat(filename, &stat);
252

253 254 255
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
256 257
}

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

264 265 266
	error = vfs_lstat(filename, &stat);
	if (error)
		return error;
Linus Torvalds's avatar
Linus Torvalds committed
267

268
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
269
}
270

271
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
272
SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
273
		struct stat __user *, statbuf, int, flag)
Linus Torvalds's avatar
Linus Torvalds committed
274 275
{
	struct kstat stat;
276
	int error;
Linus Torvalds's avatar
Linus Torvalds committed
277

278 279 280 281
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
282
}
283
#endif
284

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

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

	return error;
}

296 297
SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname,
		char __user *, buf, int, bufsiz)
Linus Torvalds's avatar
Linus Torvalds committed
298
{
299
	struct path path;
Linus Torvalds's avatar
Linus Torvalds committed
300
	int error;
301
	int empty = 0;
302
	unsigned int lookup_flags = LOOKUP_EMPTY;
Linus Torvalds's avatar
Linus Torvalds committed
303 304 305 306

	if (bufsiz <= 0)
		return -EINVAL;

307 308
retry:
	error = user_path_at_empty(dfd, pathname, lookup_flags, &path, &empty);
Linus Torvalds's avatar
Linus Torvalds committed
309
	if (!error) {
310
		struct inode *inode = path.dentry->d_inode;
Linus Torvalds's avatar
Linus Torvalds committed
311

312
		error = empty ? -ENOENT : -EINVAL;
Al Viro's avatar
Al Viro committed
313
		if (inode->i_op->readlink) {
314
			error = security_inode_readlink(path.dentry);
Linus Torvalds's avatar
Linus Torvalds committed
315
			if (!error) {
316
				touch_atime(&path);
317
				error = inode->i_op->readlink(path.dentry,
318
							      buf, bufsiz);
Linus Torvalds's avatar
Linus Torvalds committed
319 320
			}
		}
321
		path_put(&path);
322 323 324 325
		if (retry_estale(error, lookup_flags)) {
			lookup_flags |= LOOKUP_REVAL;
			goto retry;
		}
Linus Torvalds's avatar
Linus Torvalds committed
326 327 328 329
	}
	return error;
}

330 331
SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
		int, bufsiz)
332 333 334 335
{
	return sys_readlinkat(AT_FDCWD, path, buf, bufsiz);
}

Linus Torvalds's avatar
Linus Torvalds committed
336 337

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

340 341 342 343
#ifndef INIT_STRUCT_STAT64_PADDING
#  define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
#endif

Linus Torvalds's avatar
Linus Torvalds committed
344 345 346 347
static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
{
	struct stat64 tmp;

348
	INIT_STRUCT_STAT64_PADDING(tmp);
Linus Torvalds's avatar
Linus Torvalds committed
349 350 351 352 353 354 355 356 357 358 359
#ifdef CONFIG_MIPS
	/* mips has weird padding, so we don't get 64 bits there */
	if (!new_valid_dev(stat->dev) || !new_valid_dev(stat->rdev))
		return -EOVERFLOW;
	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;
360 361
	if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino)
		return -EOVERFLOW;
Linus Torvalds's avatar
Linus Torvalds committed
362 363 364 365 366
#ifdef STAT64_HAS_BROKEN_ST_INO
	tmp.__st_ino = stat->ino;
#endif
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
367 368
	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
369 370 371 372 373 374 375 376 377 378 379 380
	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;
}

381 382
SYSCALL_DEFINE2(stat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
383 384 385 386 387 388 389 390 391
{
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

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

	return error;
}
392

393 394
SYSCALL_DEFINE2(lstat64, const char __user *, filename,
		struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
395 396 397 398 399 400 401 402 403
{
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);

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

	return error;
}
404 405

SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
406 407 408 409 410 411 412 413 414 415
{
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);

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

	return error;
}

416
SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
417
		struct stat64 __user *, statbuf, int, flag)
418 419
{
	struct kstat stat;
420
	int error;
421

422 423 424 425
	error = vfs_fstatat(dfd, filename, &stat, flag);
	if (error)
		return error;
	return cp_new_stat64(&stat, statbuf);
426
}
427
#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
Linus Torvalds's avatar
Linus Torvalds committed
428

429 430
/* 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
431 432 433 434 435 436 437 438
{
	inode->i_blocks += bytes >> 9;
	bytes &= 511;
	inode->i_bytes += bytes;
	if (inode->i_bytes >= 512) {
		inode->i_blocks++;
		inode->i_bytes -= 512;
	}
439 440 441 442 443 444
}

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
445 446 447 448 449
	spin_unlock(&inode->i_lock);
}

EXPORT_SYMBOL(inode_add_bytes);

450
void __inode_sub_bytes(struct inode *inode, loff_t bytes)
Linus Torvalds's avatar
Linus Torvalds committed
451 452 453 454 455 456 457 458
{
	inode->i_blocks -= bytes >> 9;
	bytes &= 511;
	if (inode->i_bytes < bytes) {
		inode->i_blocks--;
		inode->i_bytes += 512;
	}
	inode->i_bytes -= bytes;
459 460 461 462 463 464 465 466
}

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
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
	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);