stat.c 8.24 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
 */

Linus Torvalds's avatar
Linus Torvalds committed
7
#include <linux/config.h>
Linus Torvalds's avatar
Linus Torvalds committed
8 9 10 11 12
#include <linux/mm.h>
#include <linux/errno.h>
#include <linux/file.h>
#include <linux/smp_lock.h>
#include <linux/highuid.h>
13
#include <linux/fs.h>
14
#include <linux/namei.h>
15
#include <linux/security.h>
Linus Torvalds's avatar
Linus Torvalds committed
16 17 18

#include <asm/uaccess.h>

19
void generic_fillattr(struct inode *inode, struct kstat *stat)
Linus Torvalds's avatar
Linus Torvalds committed
20
{
Andries E. Brouwer's avatar
Andries E. Brouwer committed
21
	stat->dev = inode->i_sb->s_dev;
22 23 24 25 26 27 28 29 30
	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 = kdev_t_to_nr(inode->i_rdev);
	stat->atime = inode->i_atime;
	stat->mtime = inode->i_mtime;
	stat->ctime = inode->i_ctime;
Andrew Morton's avatar
Andrew Morton committed
31
	stat->size = i_size_read(inode);
32 33 34
	stat->blocks = inode->i_blocks;
	stat->blksize = inode->i_blksize;
}
Linus Torvalds's avatar
Linus Torvalds committed
35

Alexander Viro's avatar
Alexander Viro committed
36
int vfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
37 38
{
	struct inode *inode = dentry->d_inode;
39 40
	int retval;

41 42
	retval = security_inode_getattr(mnt, dentry);
	if (retval)
43
		return retval;
44 45 46 47 48 49 50 51 52 53 54

	if (inode->i_op->getattr)
		return inode->i_op->getattr(mnt, dentry, stat);

	generic_fillattr(inode, stat);
	if (!stat->blksize) {
		struct super_block *s = inode->i_sb;
		unsigned blocks;
		blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits;
		stat->blocks = (s->s_blocksize / 512) * blocks;
		stat->blksize = s->s_blocksize;
Linus Torvalds's avatar
Linus Torvalds committed
55
	}
56
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
57 58
}

59
int vfs_stat(char __user *name, struct kstat *stat)
Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63
{
	struct nameidata nd;
	int error;

64
	error = user_path_walk(name, &nd);
Linus Torvalds's avatar
Linus Torvalds committed
65
	if (!error) {
Alexander Viro's avatar
Alexander Viro committed
66
		error = vfs_getattr(nd.mnt, nd.dentry, stat);
Linus Torvalds's avatar
Linus Torvalds committed
67 68 69 70 71
		path_release(&nd);
	}
	return error;
}

72
int vfs_lstat(char __user *name, struct kstat *stat)
Linus Torvalds's avatar
Linus Torvalds committed
73 74 75 76
{
	struct nameidata nd;
	int error;

77
	error = user_path_walk_link(name, &nd);
Linus Torvalds's avatar
Linus Torvalds committed
78
	if (!error) {
Alexander Viro's avatar
Alexander Viro committed
79
		error = vfs_getattr(nd.mnt, nd.dentry, stat);
Linus Torvalds's avatar
Linus Torvalds committed
80 81 82 83 84
		path_release(&nd);
	}
	return error;
}

85 86 87 88 89 90
int vfs_fstat(unsigned int fd, struct kstat *stat)
{
	struct file *f = fget(fd);
	int error = -EBADF;

	if (f) {
Alexander Viro's avatar
Alexander Viro committed
91
		error = vfs_getattr(f->f_vfsmnt, f->f_dentry, stat);
92 93 94 95 96
		fput(f);
	}
	return error;
}

97
#if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) \
98
  && !defined(CONFIG_ARCH_S390) && !defined(__hppa__) && !defined(__x86_64__) \
99
  && !defined(__arm__) && !defined(CONFIG_V850) && !defined(__powerpc64__)
Linus Torvalds's avatar
Linus Torvalds committed
100 101 102 103 104

/*
 * For backward compatibility?  Maybe this should be moved
 * into arch/i386 instead?
 */
105
static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
106
{
107 108
	static int warncount = 5;
	struct __old_kernel_stat tmp;
109
	
110 111 112 113 114 115 116
	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;
Linus Torvalds's avatar
Linus Torvalds committed
117 118
	}

119
	memset(&tmp, 0, sizeof(struct __old_kernel_stat));
120 121 122 123 124 125 126 127 128 129 130 131
	tmp.st_dev = stat->dev;
	tmp.st_ino = stat->ino;
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	SET_OLDSTAT_UID(tmp, stat->uid);
	SET_OLDSTAT_GID(tmp, stat->gid);
	tmp.st_rdev = stat->rdev;
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
#endif	
	tmp.st_size = stat->size;
132 133 134
	tmp.st_atime = stat->atime.tv_sec;
	tmp.st_mtime = stat->mtime.tv_sec;
	tmp.st_ctime = stat->ctime.tv_sec;
135 136
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}
Linus Torvalds's avatar
Linus Torvalds committed
137

138
asmlinkage long sys_stat(char __user * filename, struct __old_kernel_stat __user * statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
139
{
140 141 142 143 144
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

	if (!error)
		error = cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
145 146 147

	return error;
}
148
asmlinkage long sys_lstat(char __user * filename, struct __old_kernel_stat __user * statbuf)
149 150 151
{
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);
Linus Torvalds's avatar
Linus Torvalds committed
152

153 154
	if (!error)
		error = cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
155

156 157
	return error;
}
158
asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user * statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
159
{
160 161
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);
Linus Torvalds's avatar
Linus Torvalds committed
162

163 164
	if (!error)
		error = cp_old_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
165

166
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
167 168 169 170
}

#endif

171
static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf)
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
{
	struct stat tmp;

	memset(&tmp, 0, sizeof(tmp));
	tmp.st_dev = stat->dev;
	tmp.st_ino = stat->ino;
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	SET_STAT_UID(tmp, stat->uid);
	SET_STAT_GID(tmp, stat->gid);
	tmp.st_rdev = stat->rdev;
#if BITS_PER_LONG == 32
	if (stat->size > MAX_NON_LFS)
		return -EOVERFLOW;
#endif	
	tmp.st_size = stat->size;
188 189 190 191 192 193 194 195
	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
196 197 198 199 200
	tmp.st_blocks = stat->blocks;
	tmp.st_blksize = stat->blksize;
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}

201
asmlinkage long sys_newstat(char __user * filename, struct stat __user * statbuf)
202 203 204 205 206 207 208 209 210
{
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

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

	return error;
}
211
asmlinkage long sys_newlstat(char __user * filename, struct stat __user * statbuf)
212 213 214 215 216 217 218 219 220
{
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);

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

	return error;
}
221
asmlinkage long sys_newfstat(unsigned int fd, struct stat __user * statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
222
{
223 224
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);
Linus Torvalds's avatar
Linus Torvalds committed
225

226 227
	if (!error)
		error = cp_new_stat(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
228

229
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
230 231
}

232
asmlinkage long sys_readlink(const char __user * path, char __user * buf, int bufsiz)
Linus Torvalds's avatar
Linus Torvalds committed
233 234 235 236 237 238 239 240 241 242 243 244
{
	struct nameidata nd;
	int error;

	if (bufsiz <= 0)
		return -EINVAL;

	error = user_path_walk_link(path, &nd);
	if (!error) {
		struct inode * inode = nd.dentry->d_inode;

		error = -EINVAL;
245
		if (inode->i_op && inode->i_op->readlink) {
246 247
			error = security_inode_readlink(nd.dentry);
			if (!error) {
248
				update_atime(inode);
249 250
				error = inode->i_op->readlink(nd.dentry, buf, bufsiz);
			}
Linus Torvalds's avatar
Linus Torvalds committed
251 252 253 254 255 256 257 258
		}
		path_release(&nd);
	}
	return error;
}


/* ---------- LFS-64 ----------- */
Linus Torvalds's avatar
Linus Torvalds committed
259
#if !defined(__alpha__) && !defined(__ia64__) && !defined(__mips64) && !defined(__x86_64__) && !defined(CONFIG_ARCH_S390X)
Linus Torvalds's avatar
Linus Torvalds committed
260

261
static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf)
Linus Torvalds's avatar
Linus Torvalds committed
262 263 264
{
	struct stat64 tmp;

265
	memset(&tmp, 0, sizeof(struct stat64));
266 267
	tmp.st_dev = stat->dev;
	tmp.st_ino = stat->ino;
Linus Torvalds's avatar
Linus Torvalds committed
268
#ifdef STAT64_HAS_BROKEN_ST_INO
269
	tmp.__st_ino = stat->ino;
Linus Torvalds's avatar
Linus Torvalds committed
270
#endif
271 272 273 274 275
	tmp.st_mode = stat->mode;
	tmp.st_nlink = stat->nlink;
	tmp.st_uid = stat->uid;
	tmp.st_gid = stat->gid;
	tmp.st_rdev = stat->rdev;
276 277 278 279 280 281
	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;
282 283 284
	tmp.st_size = stat->size;
	tmp.st_blocks = stat->blocks;
	tmp.st_blksize = stat->blksize;
Linus Torvalds's avatar
Linus Torvalds committed
285 286 287
	return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
}

288
asmlinkage long sys_stat64(char __user * filename, struct stat64 __user * statbuf, long flags)
Linus Torvalds's avatar
Linus Torvalds committed
289
{
290 291 292 293 294
	struct kstat stat;
	int error = vfs_stat(filename, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
295 296 297

	return error;
}
298
asmlinkage long sys_lstat64(char __user * filename, struct stat64 __user * statbuf, long flags)
Linus Torvalds's avatar
Linus Torvalds committed
299
{
300 301 302 303 304
	struct kstat stat;
	int error = vfs_lstat(filename, &stat);

	if (!error)
		error = cp_new_stat64(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
305 306 307

	return error;
}
308
asmlinkage long sys_fstat64(unsigned long fd, struct stat64 __user * statbuf, long flags)
Linus Torvalds's avatar
Linus Torvalds committed
309
{
310 311
	struct kstat stat;
	int error = vfs_fstat(fd, &stat);
Linus Torvalds's avatar
Linus Torvalds committed
312

313 314
	if (!error)
		error = cp_new_stat64(&stat, statbuf);
Linus Torvalds's avatar
Linus Torvalds committed
315

316
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
317 318 319
}

#endif /* LFS-64 */
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361

void inode_add_bytes(struct inode *inode, loff_t bytes)
{
	spin_lock(&inode->i_lock);
	inode->i_blocks += bytes >> 9;
	bytes &= 511;
	inode->i_bytes += bytes;
	if (inode->i_bytes >= 512) {
		inode->i_blocks++;
		inode->i_bytes -= 512;
	}
	spin_unlock(&inode->i_lock);
}

void inode_sub_bytes(struct inode *inode, loff_t bytes)
{
	spin_lock(&inode->i_lock);
	inode->i_blocks -= bytes >> 9;
	bytes &= 511;
	if (inode->i_bytes < bytes) {
		inode->i_blocks--;
		inode->i_bytes += 512;
	}
	inode->i_bytes -= bytes;
	spin_unlock(&inode->i_lock);
}

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;
}

void inode_set_bytes(struct inode *inode, loff_t bytes)
{
	inode->i_blocks = bytes >> 9;
	inode->i_bytes = bytes & 511;
}