file.c 13.8 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8
/*
 *  linux/fs/fat/file.c
 *
 *  Written 1992,1993 by Werner Almesberger
 *
 *  regular file handling primitives for fat-based filesystems
 */

9
#include <linux/capability.h>
L
Linus Torvalds 已提交
10
#include <linux/module.h>
A
Arnd Bergmann 已提交
11
#include <linux/compat.h>
12
#include <linux/mount.h>
C
Chris Mason 已提交
13
#include <linux/blkdev.h>
14
#include <linux/backing-dev.h>
15 16
#include <linux/fsnotify.h>
#include <linux/security.h>
N
Namjae Jeon 已提交
17
#include <linux/falloc.h>
O
OGAWA Hirofumi 已提交
18
#include "fat.h"
L
Linus Torvalds 已提交
19

N
Namjae Jeon 已提交
20 21 22
static long fat_fallocate(struct file *file, int mode,
			  loff_t offset, loff_t len);

C
Christoph Hellwig 已提交
23 24 25 26
static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
{
	u32 attr;

A
Al Viro 已提交
27
	inode_lock(inode);
C
Christoph Hellwig 已提交
28
	attr = fat_make_attrs(inode);
A
Al Viro 已提交
29
	inode_unlock(inode);
C
Christoph Hellwig 已提交
30 31 32 33 34

	return put_user(attr, user_attr);
}

static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
L
Linus Torvalds 已提交
35
{
A
Al Viro 已提交
36
	struct inode *inode = file_inode(file);
L
Linus Torvalds 已提交
37
	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
C
Christoph Hellwig 已提交
38 39 40 41
	int is_dir = S_ISDIR(inode->i_mode);
	u32 attr, oldattr;
	struct iattr ia;
	int err;
L
Linus Torvalds 已提交
42

C
Christoph Hellwig 已提交
43 44 45
	err = get_user(attr, user_attr);
	if (err)
		goto out;
L
Linus Torvalds 已提交
46

47
	err = mnt_want_write_file(file);
C
Christoph Hellwig 已提交
48
	if (err)
49
		goto out;
A
Al Viro 已提交
50
	inode_lock(inode);
C
Christoph Hellwig 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65

	/*
	 * ATTR_VOLUME and ATTR_DIR cannot be changed; this also
	 * prevents the user from turning us into a VFAT
	 * longname entry.  Also, we obviously can't set
	 * any of the NTFS attributes in the high 24 bits.
	 */
	attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR);
	/* Merge in ATTR_VOLUME and ATTR_DIR */
	attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) |
		(is_dir ? ATTR_DIR : 0);
	oldattr = fat_make_attrs(inode);

	/* Equivalent to a chmod() */
	ia.ia_valid = ATTR_MODE | ATTR_CTIME;
66
	ia.ia_ctime = current_time(inode);
C
Christoph Hellwig 已提交
67 68 69 70 71 72
	if (is_dir)
		ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO);
	else {
		ia.ia_mode = fat_make_mode(sbi, attr,
			S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO));
	}
L
Linus Torvalds 已提交
73

C
Christoph Hellwig 已提交
74 75 76
	/* The root directory has no attributes */
	if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
		err = -EINVAL;
77
		goto out_unlock_inode;
L
Linus Torvalds 已提交
78 79
	}

C
Christoph Hellwig 已提交
80 81 82 83
	if (sbi->options.sys_immutable &&
	    ((attr | oldattr) & ATTR_SYS) &&
	    !capable(CAP_LINUX_IMMUTABLE)) {
		err = -EPERM;
84
		goto out_unlock_inode;
C
Christoph Hellwig 已提交
85
	}
L
Linus Torvalds 已提交
86

C
Christoph Hellwig 已提交
87 88 89 90 91 92 93
	/*
	 * The security check is questionable...  We single
	 * out the RO attribute for checking by the security
	 * module, just because it maps to a file mode.
	 */
	err = security_inode_setattr(file->f_path.dentry, &ia);
	if (err)
94
		goto out_unlock_inode;
L
Linus Torvalds 已提交
95

C
Christoph Hellwig 已提交
96 97 98
	/* This MUST be done before doing anything irreversible... */
	err = fat_setattr(file->f_path.dentry, &ia);
	if (err)
99
		goto out_unlock_inode;
C
Christoph Hellwig 已提交
100 101 102 103 104 105

	fsnotify_change(file->f_path.dentry, ia.ia_valid);
	if (sbi->options.sys_immutable) {
		if (attr & ATTR_SYS)
			inode->i_flags |= S_IMMUTABLE;
		else
106
			inode->i_flags &= ~S_IMMUTABLE;
C
Christoph Hellwig 已提交
107
	}
L
Linus Torvalds 已提交
108

C
Christoph Hellwig 已提交
109 110 111
	fat_save_attrs(inode, attr);
	mark_inode_dirty(inode);
out_unlock_inode:
A
Al Viro 已提交
112
	inode_unlock(inode);
113
	mnt_drop_write_file(file);
C
Christoph Hellwig 已提交
114 115 116
out:
	return err;
}
L
Linus Torvalds 已提交
117

118 119 120 121 122 123
static int fat_ioctl_get_volume_id(struct inode *inode, u32 __user *user_attr)
{
	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
	return put_user(sbi->vol_id, user_attr);
}

124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
static int fat_ioctl_fitrim(struct inode *inode, unsigned long arg)
{
	struct super_block *sb = inode->i_sb;
	struct fstrim_range __user *user_range;
	struct fstrim_range range;
	struct request_queue *q = bdev_get_queue(sb->s_bdev);
	int err;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;

	if (!blk_queue_discard(q))
		return -EOPNOTSUPP;

	user_range = (struct fstrim_range __user *)arg;
	if (copy_from_user(&range, user_range, sizeof(range)))
		return -EFAULT;

	range.minlen = max_t(unsigned int, range.minlen,
			     q->limits.discard_granularity);

	err = fat_trim_fs(inode, &range);
	if (err < 0)
		return err;

	if (copy_to_user(user_range, &range, sizeof(range)))
		return -EFAULT;

	return 0;
}

A
Arnd Bergmann 已提交
155
long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
156
{
A
Al Viro 已提交
157
	struct inode *inode = file_inode(filp);
L
Linus Torvalds 已提交
158 159 160 161
	u32 __user *user_attr = (u32 __user *)arg;

	switch (cmd) {
	case FAT_IOCTL_GET_ATTRIBUTES:
C
Christoph Hellwig 已提交
162
		return fat_ioctl_get_attributes(inode, user_attr);
L
Linus Torvalds 已提交
163
	case FAT_IOCTL_SET_ATTRIBUTES:
C
Christoph Hellwig 已提交
164
		return fat_ioctl_set_attributes(filp, user_attr);
165 166
	case FAT_IOCTL_GET_VOLUME_ID:
		return fat_ioctl_get_volume_id(inode, user_attr);
167 168
	case FITRIM:
		return fat_ioctl_fitrim(inode, arg);
L
Linus Torvalds 已提交
169 170 171 172 173
	default:
		return -ENOTTY;	/* Inappropriate ioctl for device */
	}
}

A
Arnd Bergmann 已提交
174 175 176 177 178 179 180 181 182
#ifdef CONFIG_COMPAT
static long fat_generic_compat_ioctl(struct file *filp, unsigned int cmd,
				      unsigned long arg)

{
	return fat_generic_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
}
#endif

C
Chris Mason 已提交
183 184 185 186 187
static int fat_file_release(struct inode *inode, struct file *filp)
{
	if ((filp->f_mode & FMODE_WRITE) &&
	     MSDOS_SB(inode->i_sb)->options.flush) {
		fat_flush_inodes(inode->i_sb, inode, NULL);
188
		congestion_wait(BLK_RW_ASYNC, HZ/10);
C
Chris Mason 已提交
189 190 191 192
	}
	return 0;
}

193
int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
A
Al Viro 已提交
194
{
195
	struct inode *inode = filp->f_mapping->host;
A
Al Viro 已提交
196 197
	int res, err;

198
	res = generic_file_fsync(filp, start, end, datasync);
A
Al Viro 已提交
199 200 201 202 203 204
	err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);

	return res ? res : err;
}


205
const struct file_operations fat_file_operations = {
L
Linus Torvalds 已提交
206
	.llseek		= generic_file_llseek,
207
	.read_iter	= generic_file_read_iter,
208
	.write_iter	= generic_file_write_iter,
L
Linus Torvalds 已提交
209
	.mmap		= generic_file_mmap,
C
Chris Mason 已提交
210
	.release	= fat_file_release,
A
Arnd Bergmann 已提交
211 212 213 214
	.unlocked_ioctl	= fat_generic_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= fat_generic_compat_ioctl,
#endif
A
Al Viro 已提交
215
	.fsync		= fat_file_fsync,
216
	.splice_read	= generic_file_splice_read,
N
Namjae Jeon 已提交
217
	.fallocate	= fat_fallocate,
L
Linus Torvalds 已提交
218 219
};

220 221 222 223 224 225 226 227 228 229
static int fat_cont_expand(struct inode *inode, loff_t size)
{
	struct address_space *mapping = inode->i_mapping;
	loff_t start = inode->i_size, count = size - inode->i_size;
	int err;

	err = generic_cont_expand_simple(inode, size);
	if (err)
		goto out;

230
	fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
231
	mark_inode_dirty(inode);
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
	if (IS_SYNC(inode)) {
		int err2;

		/*
		 * Opencode syncing since we don't have a file open to use
		 * standard fsync path.
		 */
		err = filemap_fdatawrite_range(mapping, start,
					       start + count - 1);
		err2 = sync_mapping_buffers(mapping);
		if (!err)
			err = err2;
		err2 = write_inode_now(inode, 1);
		if (!err)
			err = err2;
		if (!err) {
			err =  filemap_fdatawait_range(mapping, start,
						       start + count - 1);
		}
	}
252 253 254 255
out:
	return err;
}

N
Namjae Jeon 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
/*
 * Preallocate space for a file. This implements fat's fallocate file
 * operation, which gets called from sys_fallocate system call. User
 * space requests len bytes at offset. If FALLOC_FL_KEEP_SIZE is set
 * we just allocate clusters without zeroing them out. Otherwise we
 * allocate and zero out clusters via an expanding truncate.
 */
static long fat_fallocate(struct file *file, int mode,
			  loff_t offset, loff_t len)
{
	int nr_cluster; /* Number of clusters to be allocated */
	loff_t mm_bytes; /* Number of bytes to be allocated for file */
	loff_t ondisksize; /* block aligned on-disk size in bytes*/
	struct inode *inode = file->f_mapping->host;
	struct super_block *sb = inode->i_sb;
	struct msdos_sb_info *sbi = MSDOS_SB(sb);
	int err = 0;

	/* No support for hole punch or other fallocate flags. */
	if (mode & ~FALLOC_FL_KEEP_SIZE)
		return -EOPNOTSUPP;

	/* No support for dir */
	if (!S_ISREG(inode->i_mode))
		return -EOPNOTSUPP;

A
Al Viro 已提交
282
	inode_lock(inode);
N
Namjae Jeon 已提交
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	if (mode & FALLOC_FL_KEEP_SIZE) {
		ondisksize = inode->i_blocks << 9;
		if ((offset + len) <= ondisksize)
			goto error;

		/* First compute the number of clusters to be allocated */
		mm_bytes = offset + len - ondisksize;
		nr_cluster = (mm_bytes + (sbi->cluster_size - 1)) >>
			sbi->cluster_bits;

		/* Start the allocation.We are not zeroing out the clusters */
		while (nr_cluster-- > 0) {
			err = fat_add_cluster(inode);
			if (err)
				goto error;
		}
	} else {
		if ((offset + len) <= i_size_read(inode))
			goto error;

		/* This is just an expanding truncate */
		err = fat_cont_expand(inode, (offset + len));
	}

error:
A
Al Viro 已提交
308
	inode_unlock(inode);
N
Namjae Jeon 已提交
309 310 311
	return err;
}

L
Linus Torvalds 已提交
312 313 314 315 316 317 318 319 320
/* Free all clusters after the skip'th cluster. */
static int fat_free(struct inode *inode, int skip)
{
	struct super_block *sb = inode->i_sb;
	int err, wait, free_start, i_start, i_logstart;

	if (MSDOS_I(inode)->i_start == 0)
		return 0;

321 322
	fat_cache_inval_inode(inode);

L
Linus Torvalds 已提交
323
	wait = IS_DIRSYNC(inode);
324 325 326 327 328 329 330 331 332
	i_start = free_start = MSDOS_I(inode)->i_start;
	i_logstart = MSDOS_I(inode)->i_logstart;

	/* First, we write the new file size. */
	if (!skip) {
		MSDOS_I(inode)->i_start = 0;
		MSDOS_I(inode)->i_logstart = 0;
	}
	MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
333
	fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
334 335 336 337 338 339 340 341 342 343 344
	if (wait) {
		err = fat_sync_inode(inode);
		if (err) {
			MSDOS_I(inode)->i_start = i_start;
			MSDOS_I(inode)->i_logstart = i_logstart;
			return err;
		}
	} else
		mark_inode_dirty(inode);

	/* Write a new EOF, and get the remaining cluster chain for freeing. */
L
Linus Torvalds 已提交
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
	if (skip) {
		struct fat_entry fatent;
		int ret, fclus, dclus;

		ret = fat_get_cluster(inode, skip - 1, &fclus, &dclus);
		if (ret < 0)
			return ret;
		else if (ret == FAT_ENT_EOF)
			return 0;

		fatent_init(&fatent);
		ret = fat_ent_read(inode, &fatent, dclus);
		if (ret == FAT_ENT_EOF) {
			fatent_brelse(&fatent);
			return 0;
		} else if (ret == FAT_ENT_FREE) {
D
Denis Karpov 已提交
361
			fat_fs_error(sb,
L
Linus Torvalds 已提交
362
				     "%s: invalid cluster chain (i_pos %lld)",
363
				     __func__, MSDOS_I(inode)->i_pos);
L
Linus Torvalds 已提交
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
			ret = -EIO;
		} else if (ret > 0) {
			err = fat_ent_write(inode, &fatent, FAT_ENT_EOF, wait);
			if (err)
				ret = err;
		}
		fatent_brelse(&fatent);
		if (ret < 0)
			return ret;

		free_start = ret;
	}
	inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9);

	/* Freeing the remained cluster chain */
	return fat_free_clusters(inode, free_start);
}

382
void fat_truncate_blocks(struct inode *inode, loff_t offset)
L
Linus Torvalds 已提交
383
{
384
	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
L
Linus Torvalds 已提交
385 386 387 388 389 390 391
	const unsigned int cluster_size = sbi->cluster_size;
	int nr_clusters;

	/*
	 * This protects against truncating a file bigger than it was then
	 * trying to write into the hole.
	 */
392 393
	if (MSDOS_I(inode)->mmu_private > offset)
		MSDOS_I(inode)->mmu_private = offset;
L
Linus Torvalds 已提交
394

395
	nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits;
L
Linus Torvalds 已提交
396 397

	fat_free(inode, nr_clusters);
C
Chris Mason 已提交
398
	fat_flush_inodes(inode->i_sb, inode, NULL);
L
Linus Torvalds 已提交
399 400
}

401 402
int fat_getattr(const struct path *path, struct kstat *stat,
		u32 request_mask, unsigned int flags)
O
OGAWA Hirofumi 已提交
403
{
404
	struct inode *inode = d_inode(path->dentry);
O
OGAWA Hirofumi 已提交
405 406
	generic_fillattr(inode, stat);
	stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size;
N
Namjae Jeon 已提交
407 408 409 410 411

	if (MSDOS_SB(inode->i_sb)->options.nfs == FAT_NFS_NOSTALE_RO) {
		/* Use i_pos for ino. This is used as fileid of nfs. */
		stat->ino = fat_i_pos_read(MSDOS_SB(inode->i_sb), inode);
	}
O
OGAWA Hirofumi 已提交
412 413 414 415
	return 0;
}
EXPORT_SYMBOL_GPL(fat_getattr);

416 417
static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
			     struct inode *inode, umode_t *mode_ptr)
418
{
A
Al Viro 已提交
419
	umode_t mask, perm;
420

421 422
	/*
	 * Note, the basic check is already done by a caller of
O
OGAWA Hirofumi 已提交
423
	 * (attr->ia_mode & ~FAT_VALID_MODE)
424 425 426
	 */

	if (S_ISREG(inode->i_mode))
427 428 429 430
		mask = sbi->options.fs_fmask;
	else
		mask = sbi->options.fs_dmask;

431 432
	perm = *mode_ptr & ~(S_IFMT | mask);

433 434 435
	/*
	 * Of the r and x bits, all (subject to umask) must be present. Of the
	 * w bits, either all (subject to umask) or none must be present.
O
OGAWA Hirofumi 已提交
436 437
	 *
	 * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
438
	 */
439
	if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
440
		return -EPERM;
O
OGAWA Hirofumi 已提交
441 442 443 444 445 446 447
	if (fat_mode_can_hold_ro(inode)) {
		if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
			return -EPERM;
	} else {
		if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
			return -EPERM;
	}
448

449 450
	*mode_ptr &= S_IFMT | perm;

451 452 453
	return 0;
}

O
OGAWA Hirofumi 已提交
454 455
static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
{
A
Al Viro 已提交
456
	umode_t allow_utime = sbi->options.allow_utime;
O
OGAWA Hirofumi 已提交
457

458
	if (!uid_eq(current_fsuid(), inode->i_uid)) {
O
OGAWA Hirofumi 已提交
459 460 461 462 463 464 465 466 467 468
		if (in_group_p(inode->i_gid))
			allow_utime >>= 3;
		if (allow_utime & MAY_WRITE)
			return 1;
	}

	/* use a default check */
	return 0;
}

O
OGAWA Hirofumi 已提交
469
#define TIMES_SET_FLAGS	(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
O
OGAWA Hirofumi 已提交
470 471
/* valid file mode bits */
#define FAT_VALID_MODE	(S_IFREG | S_IFDIR | S_IRWXUGO)
O
OGAWA Hirofumi 已提交
472

473 474 475
int fat_setattr(struct dentry *dentry, struct iattr *attr)
{
	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
476
	struct inode *inode = d_inode(dentry);
O
OGAWA Hirofumi 已提交
477
	unsigned int ia_valid;
O
OGAWA Hirofumi 已提交
478
	int error;
479

O
OGAWA Hirofumi 已提交
480 481
	/* Check for setting the inode time. */
	ia_valid = attr->ia_valid;
O
OGAWA Hirofumi 已提交
482
	if (ia_valid & TIMES_SET_FLAGS) {
O
OGAWA Hirofumi 已提交
483
		if (fat_allow_set_time(sbi, inode))
O
OGAWA Hirofumi 已提交
484
			attr->ia_valid &= ~TIMES_SET_FLAGS;
O
OGAWA Hirofumi 已提交
485 486
	}

487
	error = setattr_prepare(dentry, attr);
O
OGAWA Hirofumi 已提交
488
	attr->ia_valid = ia_valid;
489 490 491 492 493
	if (error) {
		if (sbi->options.quiet)
			error = 0;
		goto out;
	}
494

495 496 497 498 499 500 501
	/*
	 * Expand the file. Since inode_setattr() updates ->i_size
	 * before calling the ->truncate(), but FAT needs to fill the
	 * hole before it. XXX: this is no longer true with new truncate
	 * sequence.
	 */
	if (attr->ia_valid & ATTR_SIZE) {
502 503
		inode_dio_wait(inode);

504 505 506 507 508 509 510 511
		if (attr->ia_size > inode->i_size) {
			error = fat_cont_expand(inode, attr->ia_size);
			if (error || attr->ia_valid == ATTR_SIZE)
				goto out;
			attr->ia_valid &= ~ATTR_SIZE;
		}
	}

512
	if (((attr->ia_valid & ATTR_UID) &&
513
	     (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
514
	    ((attr->ia_valid & ATTR_GID) &&
515
	     (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
O
OGAWA Hirofumi 已提交
516
	    ((attr->ia_valid & ATTR_MODE) &&
O
OGAWA Hirofumi 已提交
517
	     (attr->ia_mode & ~FAT_VALID_MODE)))
518 519 520 521 522 523 524 525
		error = -EPERM;

	if (error) {
		if (sbi->options.quiet)
			error = 0;
		goto out;
	}

526 527 528 529 530 531 532 533
	/*
	 * We don't return -EPERM here. Yes, strange, but this is too
	 * old behavior.
	 */
	if (attr->ia_valid & ATTR_MODE) {
		if (fat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0)
			attr->ia_valid &= ~ATTR_MODE;
	}
534

535
	if (attr->ia_valid & ATTR_SIZE) {
536 537 538
		error = fat_block_truncate_page(inode, attr->ia_size);
		if (error)
			goto out;
539
		down_write(&MSDOS_I(inode)->truncate_lock);
540 541
		truncate_setsize(inode, attr->ia_size);
		fat_truncate_blocks(inode, attr->ia_size);
542
		up_write(&MSDOS_I(inode)->truncate_lock);
543 544
	}

C
Christoph Hellwig 已提交
545
	setattr_copy(inode, attr);
546
	mark_inode_dirty(inode);
547 548 549 550 551
out:
	return error;
}
EXPORT_SYMBOL_GPL(fat_setattr);

552
const struct inode_operations fat_file_inode_operations = {
553
	.setattr	= fat_setattr,
O
OGAWA Hirofumi 已提交
554
	.getattr	= fat_getattr,
555
	.update_time	= fat_update_time,
L
Linus Torvalds 已提交
556
};