file.c 14.2 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
L
Linus Torvalds 已提交
2 3 4 5 6 7 8 9
/*
 *  linux/fs/fat/file.c
 *
 *  Written 1992,1993 by Werner Almesberger
 *
 *  regular file handling primitives for fat-based filesystems
 */

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

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

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

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

	return put_user(attr, user_attr);
}

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

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

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

	/*
	 * 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;
67
	ia.ia_ctime = current_time(inode);
C
Christoph Hellwig 已提交
68 69 70 71 72 73
	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 已提交
74

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

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

C
Christoph Hellwig 已提交
88 89 90 91 92 93 94
	/*
	 * 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)
95
		goto out_unlock_inode;
L
Linus Torvalds 已提交
96

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

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

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

119 120 121 122 123 124
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);
}

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 155
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 已提交
156
long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
L
Linus Torvalds 已提交
157
{
A
Al Viro 已提交
158
	struct inode *inode = file_inode(filp);
L
Linus Torvalds 已提交
159 160 161 162
	u32 __user *user_attr = (u32 __user *)arg;

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

C
Chris Mason 已提交
175 176 177 178 179
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);
180
		congestion_wait(BLK_RW_ASYNC, HZ/10);
C
Chris Mason 已提交
181 182 183 184
	}
	return 0;
}

185
int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
A
Al Viro 已提交
186
{
187
	struct inode *inode = filp->f_mapping->host;
188 189 190 191 192
	int err;

	err = __generic_file_fsync(filp, start, end, datasync);
	if (err)
		return err;
A
Al Viro 已提交
193 194

	err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);
195 196
	if (err)
		return err;
A
Al Viro 已提交
197

198
	return blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL);
A
Al Viro 已提交
199 200 201
}


202
const struct file_operations fat_file_operations = {
L
Linus Torvalds 已提交
203
	.llseek		= generic_file_llseek,
204
	.read_iter	= generic_file_read_iter,
205
	.write_iter	= generic_file_write_iter,
L
Linus Torvalds 已提交
206
	.mmap		= generic_file_mmap,
C
Chris Mason 已提交
207
	.release	= fat_file_release,
A
Arnd Bergmann 已提交
208
	.unlocked_ioctl	= fat_generic_ioctl,
209
	.compat_ioctl	= compat_ptr_ioctl,
A
Al Viro 已提交
210
	.fsync		= fat_file_fsync,
211
	.splice_read	= generic_file_splice_read,
212
	.splice_write	= iter_file_splice_write,
N
Namjae Jeon 已提交
213
	.fallocate	= fat_fallocate,
L
Linus Torvalds 已提交
214 215
};

216 217 218 219 220 221 222 223 224 225
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;

226
	fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
227
	mark_inode_dirty(inode);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	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);
		}
	}
248 249 250 251
out:
	return err;
}

N
Namjae Jeon 已提交
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
/*
 * 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 已提交
278
	inode_lock(inode);
N
Namjae Jeon 已提交
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
	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 已提交
304
	inode_unlock(inode);
N
Namjae Jeon 已提交
305 306 307
	return err;
}

L
Linus Torvalds 已提交
308 309 310 311 312 313 314 315 316
/* 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;

317 318
	fat_cache_inval_inode(inode);

L
Linus Torvalds 已提交
319
	wait = IS_DIRSYNC(inode);
320 321 322 323 324 325 326 327 328
	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;
329
	fat_truncate_time(inode, NULL, S_CTIME|S_MTIME);
330 331 332 333 334 335 336 337 338 339 340
	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 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
	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 已提交
357
			fat_fs_error(sb,
L
Linus Torvalds 已提交
358
				     "%s: invalid cluster chain (i_pos %lld)",
359
				     __func__, MSDOS_I(inode)->i_pos);
L
Linus Torvalds 已提交
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
			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);
}

378
void fat_truncate_blocks(struct inode *inode, loff_t offset)
L
Linus Torvalds 已提交
379
{
380
	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
L
Linus Torvalds 已提交
381 382 383 384 385 386 387
	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.
	 */
388 389
	if (MSDOS_I(inode)->mmu_private > offset)
		MSDOS_I(inode)->mmu_private = offset;
L
Linus Torvalds 已提交
390

391
	nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits;
L
Linus Torvalds 已提交
392 393

	fat_free(inode, nr_clusters);
C
Chris Mason 已提交
394
	fat_flush_inodes(inode->i_sb, inode, NULL);
L
Linus Torvalds 已提交
395 396
}

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

	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 已提交
408 409 410 411
	return 0;
}
EXPORT_SYMBOL_GPL(fat_getattr);

412 413
static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
			     struct inode *inode, umode_t *mode_ptr)
414
{
A
Al Viro 已提交
415
	umode_t mask, perm;
416

417 418
	/*
	 * Note, the basic check is already done by a caller of
O
OGAWA Hirofumi 已提交
419
	 * (attr->ia_mode & ~FAT_VALID_MODE)
420 421 422
	 */

	if (S_ISREG(inode->i_mode))
423 424 425 426
		mask = sbi->options.fs_fmask;
	else
		mask = sbi->options.fs_dmask;

427 428
	perm = *mode_ptr & ~(S_IFMT | mask);

429 430 431
	/*
	 * 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 已提交
432 433
	 *
	 * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
434
	 */
435
	if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
436
		return -EPERM;
O
OGAWA Hirofumi 已提交
437 438 439 440 441 442 443
	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;
	}
444

445 446
	*mode_ptr &= S_IFMT | perm;

447 448 449
	return 0;
}

O
OGAWA Hirofumi 已提交
450 451
static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
{
A
Al Viro 已提交
452
	umode_t allow_utime = sbi->options.allow_utime;
O
OGAWA Hirofumi 已提交
453

454
	if (!uid_eq(current_fsuid(), inode->i_uid)) {
O
OGAWA Hirofumi 已提交
455 456 457 458 459 460 461 462 463 464
		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 已提交
465
#define TIMES_SET_FLAGS	(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
O
OGAWA Hirofumi 已提交
466 467
/* valid file mode bits */
#define FAT_VALID_MODE	(S_IFREG | S_IFDIR | S_IRWXUGO)
O
OGAWA Hirofumi 已提交
468

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

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

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

491 492 493 494 495 496 497
	/*
	 * 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) {
498 499
		inode_dio_wait(inode);

500 501 502 503 504 505 506 507
		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;
		}
	}

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

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

522 523 524 525 526 527 528 529
	/*
	 * 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;
	}
530

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

541 542 543 544 545 546 547 548 549 550 551 552
	/*
	 * setattr_copy can't truncate these appropriately, so we'll
	 * copy them ourselves
	 */
	if (attr->ia_valid & ATTR_ATIME)
		fat_truncate_time(inode, &attr->ia_atime, S_ATIME);
	if (attr->ia_valid & ATTR_CTIME)
		fat_truncate_time(inode, &attr->ia_ctime, S_CTIME);
	if (attr->ia_valid & ATTR_MTIME)
		fat_truncate_time(inode, &attr->ia_mtime, S_MTIME);
	attr->ia_valid &= ~(ATTR_ATIME|ATTR_CTIME|ATTR_MTIME);

C
Christoph Hellwig 已提交
553
	setattr_copy(inode, attr);
554
	mark_inode_dirty(inode);
555 556 557 558 559
out:
	return error;
}
EXPORT_SYMBOL_GPL(fat_setattr);

560
const struct inode_operations fat_file_inode_operations = {
561
	.setattr	= fat_setattr,
O
OGAWA Hirofumi 已提交
562
	.getattr	= fat_getattr,
563
	.update_time	= fat_update_time,
L
Linus Torvalds 已提交
564
};