Blame view

fs/fat/file.c 13.1 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
  /*
   *  linux/fs/fat/file.c
   *
   *  Written 1992,1993 by Werner Almesberger
   *
   *  regular file handling primitives for fat-based filesystems
   */
16f7e0fe2   Randy Dunlap   [PATCH] capable/c...
8
  #include <linux/capability.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
9
  #include <linux/module.h>
7845bc3e1   Arnd Bergmann   fat: convert to u...
10
  #include <linux/compat.h>
42a74f206   Dave Hansen   [PATCH] r/o bind ...
11
  #include <linux/mount.h>
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
12
  #include <linux/blkdev.h>
66114cad6   Tejun Heo   writeback: separa...
13
  #include <linux/backing-dev.h>
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
14
15
  #include <linux/fsnotify.h>
  #include <linux/security.h>
b13bb33ea   Namjae Jeon   fat: add fat_fall...
16
  #include <linux/falloc.h>
9e975dae2   OGAWA Hirofumi   fat: split includ...
17
  #include "fat.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18

b13bb33ea   Namjae Jeon   fat: add fat_fall...
19
20
  static long fat_fallocate(struct file *file, int mode,
  			  loff_t offset, loff_t len);
21bea4959   Christoph Hellwig   fat: split fat_ge...
21
22
23
  static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
  {
  	u32 attr;
5955102c9   Al Viro   wrappers for ->i_...
24
  	inode_lock(inode);
21bea4959   Christoph Hellwig   fat: split fat_ge...
25
  	attr = fat_make_attrs(inode);
5955102c9   Al Viro   wrappers for ->i_...
26
  	inode_unlock(inode);
21bea4959   Christoph Hellwig   fat: split fat_ge...
27
28
29
30
31
  
  	return put_user(attr, user_attr);
  }
  
  static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
  {
496ad9aa8   Al Viro   new helper: file_...
33
  	struct inode *inode = file_inode(file);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
  	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
21bea4959   Christoph Hellwig   fat: split fat_ge...
35
36
37
38
  	int is_dir = S_ISDIR(inode->i_mode);
  	u32 attr, oldattr;
  	struct iattr ia;
  	int err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39

21bea4959   Christoph Hellwig   fat: split fat_ge...
40
41
42
  	err = get_user(attr, user_attr);
  	if (err)
  		goto out;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
43

a561be710   Al Viro   switch a bunch of...
44
  	err = mnt_want_write_file(file);
21bea4959   Christoph Hellwig   fat: split fat_ge...
45
  	if (err)
e24f17da3   Jan Kara   fat: Push mnt_wan...
46
  		goto out;
5955102c9   Al Viro   wrappers for ->i_...
47
  	inode_lock(inode);
21bea4959   Christoph Hellwig   fat: split fat_ge...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  
  	/*
  	 * 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;
c2050a454   Deepa Dinamani   fs: Replace curre...
63
  	ia.ia_ctime = current_time(inode);
21bea4959   Christoph Hellwig   fat: split fat_ge...
64
65
66
67
68
69
  	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));
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
70

21bea4959   Christoph Hellwig   fat: split fat_ge...
71
72
73
  	/* The root directory has no attributes */
  	if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
  		err = -EINVAL;
e24f17da3   Jan Kara   fat: Push mnt_wan...
74
  		goto out_unlock_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76

21bea4959   Christoph Hellwig   fat: split fat_ge...
77
78
79
80
  	if (sbi->options.sys_immutable &&
  	    ((attr | oldattr) & ATTR_SYS) &&
  	    !capable(CAP_LINUX_IMMUTABLE)) {
  		err = -EPERM;
e24f17da3   Jan Kara   fat: Push mnt_wan...
81
  		goto out_unlock_inode;
21bea4959   Christoph Hellwig   fat: split fat_ge...
82
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
83

21bea4959   Christoph Hellwig   fat: split fat_ge...
84
85
86
87
88
89
90
  	/*
  	 * 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)
e24f17da3   Jan Kara   fat: Push mnt_wan...
91
  		goto out_unlock_inode;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
92

21bea4959   Christoph Hellwig   fat: split fat_ge...
93
94
95
  	/* This MUST be done before doing anything irreversible... */
  	err = fat_setattr(file->f_path.dentry, &ia);
  	if (err)
e24f17da3   Jan Kara   fat: Push mnt_wan...
96
  		goto out_unlock_inode;
21bea4959   Christoph Hellwig   fat: split fat_ge...
97
98
99
100
101
102
  
  	fsnotify_change(file->f_path.dentry, ia.ia_valid);
  	if (sbi->options.sys_immutable) {
  		if (attr & ATTR_SYS)
  			inode->i_flags |= S_IMMUTABLE;
  		else
1adffbae2   OGAWA Hirofumi   fat: Fix corrupt ...
103
  			inode->i_flags &= ~S_IMMUTABLE;
21bea4959   Christoph Hellwig   fat: split fat_ge...
104
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
105

21bea4959   Christoph Hellwig   fat: split fat_ge...
106
107
  	fat_save_attrs(inode, attr);
  	mark_inode_dirty(inode);
21bea4959   Christoph Hellwig   fat: split fat_ge...
108
  out_unlock_inode:
5955102c9   Al Viro   wrappers for ->i_...
109
  	inode_unlock(inode);
e24f17da3   Jan Kara   fat: Push mnt_wan...
110
  	mnt_drop_write_file(file);
21bea4959   Christoph Hellwig   fat: split fat_ge...
111
112
113
  out:
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114

6e5b93ee5   Mike Lockwood   fatfs: add FAT_IO...
115
116
117
118
119
  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);
  }
7845bc3e1   Arnd Bergmann   fat: convert to u...
120
  long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
121
  {
496ad9aa8   Al Viro   new helper: file_...
122
  	struct inode *inode = file_inode(filp);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123
  	u32 __user *user_attr = (u32 __user *)arg;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
125
126
  	switch (cmd) {
  	case FAT_IOCTL_GET_ATTRIBUTES:
21bea4959   Christoph Hellwig   fat: split fat_ge...
127
  		return fat_ioctl_get_attributes(inode, user_attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
  	case FAT_IOCTL_SET_ATTRIBUTES:
21bea4959   Christoph Hellwig   fat: split fat_ge...
129
  		return fat_ioctl_set_attributes(filp, user_attr);
6e5b93ee5   Mike Lockwood   fatfs: add FAT_IO...
130
131
  	case FAT_IOCTL_GET_VOLUME_ID:
  		return fat_ioctl_get_volume_id(inode, user_attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
  	default:
  		return -ENOTTY;	/* Inappropriate ioctl for device */
  	}
  }
7845bc3e1   Arnd Bergmann   fat: convert to u...
136
137
138
139
140
141
142
143
  #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
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
144
145
146
147
148
  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);
8aa7e847d   Jens Axboe   Fix congestion_wa...
149
  		congestion_wait(BLK_RW_ASYNC, HZ/10);
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
150
151
152
  	}
  	return 0;
  }
02c24a821   Josef Bacik   fs: push i_mutex ...
153
  int fat_file_fsync(struct file *filp, loff_t start, loff_t end, int datasync)
b522412ae   Al Viro   Sanitize ->fsync(...
154
  {
7ea808591   Christoph Hellwig   drop unused dentr...
155
  	struct inode *inode = filp->f_mapping->host;
b522412ae   Al Viro   Sanitize ->fsync(...
156
  	int res, err;
02c24a821   Josef Bacik   fs: push i_mutex ...
157
  	res = generic_file_fsync(filp, start, end, datasync);
b522412ae   Al Viro   Sanitize ->fsync(...
158
159
160
161
  	err = sync_mapping_buffers(MSDOS_SB(inode->i_sb)->fat_inode->i_mapping);
  
  	return res ? res : err;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
162
  const struct file_operations fat_file_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
163
  	.llseek		= generic_file_llseek,
aad4f8bb4   Al Viro   switch simple gen...
164
  	.read_iter	= generic_file_read_iter,
8174202b3   Al Viro   write_iter varian...
165
  	.write_iter	= generic_file_write_iter,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
166
  	.mmap		= generic_file_mmap,
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
167
  	.release	= fat_file_release,
7845bc3e1   Arnd Bergmann   fat: convert to u...
168
169
170
171
  	.unlocked_ioctl	= fat_generic_ioctl,
  #ifdef CONFIG_COMPAT
  	.compat_ioctl	= fat_generic_compat_ioctl,
  #endif
b522412ae   Al Viro   Sanitize ->fsync(...
172
  	.fsync		= fat_file_fsync,
5ffc4ef45   Jens Axboe   sendfile: remove ...
173
  	.splice_read	= generic_file_splice_read,
b13bb33ea   Namjae Jeon   fat: add fat_fall...
174
  	.fallocate	= fat_fallocate,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  };
05eb0b51f   OGAWA Hirofumi   [PATCH] fat: supp...
176
177
178
179
180
181
182
183
184
  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;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
185
  	inode->i_ctime = inode->i_mtime = current_time(inode);
05eb0b51f   OGAWA Hirofumi   [PATCH] fat: supp...
186
  	mark_inode_dirty(inode);
2f3d675bc   Jan Kara   fat: Opencode syn...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
  	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);
  		}
  	}
05eb0b51f   OGAWA Hirofumi   [PATCH] fat: supp...
207
208
209
  out:
  	return err;
  }
b13bb33ea   Namjae Jeon   fat: add fat_fall...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  /*
   * 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;
5955102c9   Al Viro   wrappers for ->i_...
235
  	inode_lock(inode);
b13bb33ea   Namjae Jeon   fat: add fat_fall...
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
  	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:
5955102c9   Al Viro   wrappers for ->i_...
261
  	inode_unlock(inode);
b13bb33ea   Namjae Jeon   fat: add fat_fall...
262
263
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
264
265
266
267
268
269
270
271
  /* 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;
3b641407a   OGAWA Hirofumi   [PATCH] fat: Fix ...
272
  	fat_cache_inval_inode(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
273
  	wait = IS_DIRSYNC(inode);
3b641407a   OGAWA Hirofumi   [PATCH] fat: Fix ...
274
275
276
277
278
279
280
281
282
  	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;
02027d42c   Deepa Dinamani   fs: Replace CURRE...
283
  	inode->i_ctime = inode->i_mtime = current_time(inode);
3b641407a   OGAWA Hirofumi   [PATCH] fat: Fix ...
284
285
286
287
288
289
290
291
292
293
294
  	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. */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  	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) {
85c785919   Denis Karpov   FAT: add 'errors'...
311
  			fat_fs_error(sb,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
312
  				     "%s: invalid cluster chain (i_pos %lld)",
8e24eea72   Harvey Harrison   fs: replace remai...
313
  				     __func__, MSDOS_I(inode)->i_pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
314
315
316
317
318
319
320
321
322
323
324
  			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;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
325
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
326
327
328
329
  	inode->i_blocks = skip << (MSDOS_SB(sb)->cluster_bits - 9);
  
  	/* Freeing the remained cluster chain */
  	return fat_free_clusters(inode, free_start);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
330
  }
459f6ed3b   npiggin@suse.de   fat: convert to u...
331
  void fat_truncate_blocks(struct inode *inode, loff_t offset)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
332
  {
9c20616c3   Jonathan Corbet   Make FAT users ha...
333
  	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
334
335
336
337
338
339
340
  	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.
  	 */
459f6ed3b   npiggin@suse.de   fat: convert to u...
341
342
  	if (MSDOS_I(inode)->mmu_private > offset)
  		MSDOS_I(inode)->mmu_private = offset;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
343

459f6ed3b   npiggin@suse.de   fat: convert to u...
344
  	nr_clusters = (offset + (cluster_size - 1)) >> sbi->cluster_bits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
345

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
346
  	fat_free(inode, nr_clusters);
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
347
  	fat_flush_inodes(inode->i_sb, inode, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
348
  }
a528d35e8   David Howells   statx: Add a syst...
349
350
  int fat_getattr(const struct path *path, struct kstat *stat,
  		u32 request_mask, unsigned int flags)
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
351
  {
a528d35e8   David Howells   statx: Add a syst...
352
  	struct inode *inode = d_inode(path->dentry);
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
353
354
  	generic_fillattr(inode, stat);
  	stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size;
ea3983ace   Namjae Jeon   fat: restructure ...
355
356
357
358
359
  
  	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);
  	}
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
360
361
362
  	return 0;
  }
  EXPORT_SYMBOL_GPL(fat_getattr);
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
363
364
  static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
  			     struct inode *inode, umode_t *mode_ptr)
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
365
  {
dacd0e7b3   Al Viro   fat: propagate um...
366
  	umode_t mask, perm;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
367

2d518f84e   OGAWA Hirofumi   fat: relax the pe...
368
369
  	/*
  	 * Note, the basic check is already done by a caller of
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
370
  	 * (attr->ia_mode & ~FAT_VALID_MODE)
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
371
372
373
  	 */
  
  	if (S_ISREG(inode->i_mode))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
374
375
376
  		mask = sbi->options.fs_fmask;
  	else
  		mask = sbi->options.fs_dmask;
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
377
  	perm = *mode_ptr & ~(S_IFMT | mask);
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
378
379
380
  	/*
  	 * 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.
dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
381
382
  	 *
  	 * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
383
  	 */
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
384
  	if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
385
  		return -EPERM;
dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
386
387
388
389
390
391
392
  	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;
  	}
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
393

2d518f84e   OGAWA Hirofumi   fat: relax the pe...
394
  	*mode_ptr &= S_IFMT | perm;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
395
396
  	return 0;
  }
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
397
398
  static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
  {
dacd0e7b3   Al Viro   fat: propagate um...
399
  	umode_t allow_utime = sbi->options.allow_utime;
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
400

170782eb8   Eric W. Biederman   userns: Convert f...
401
  	if (!uid_eq(current_fsuid(), inode->i_uid)) {
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
402
403
404
405
406
407
408
409
410
  		if (in_group_p(inode->i_gid))
  			allow_utime >>= 3;
  		if (allow_utime & MAY_WRITE)
  			return 1;
  	}
  
  	/* use a default check */
  	return 0;
  }
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
411
  #define TIMES_SET_FLAGS	(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
412
413
  /* valid file mode bits */
  #define FAT_VALID_MODE	(S_IFREG | S_IFDIR | S_IRWXUGO)
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
414

1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
415
416
417
  int fat_setattr(struct dentry *dentry, struct iattr *attr)
  {
  	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
2b0143b5c   David Howells   VFS: normal files...
418
  	struct inode *inode = d_inode(dentry);
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
419
  	unsigned int ia_valid;
dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
420
  	int error;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
421

1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
422
423
  	/* Check for setting the inode time. */
  	ia_valid = attr->ia_valid;
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
424
  	if (ia_valid & TIMES_SET_FLAGS) {
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
425
  		if (fat_allow_set_time(sbi, inode))
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
426
  			attr->ia_valid &= ~TIMES_SET_FLAGS;
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
427
  	}
31051c85b   Jan Kara   fs: Give dentry t...
428
  	error = setattr_prepare(dentry, attr);
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
429
  	attr->ia_valid = ia_valid;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
430
431
432
433
434
  	if (error) {
  		if (sbi->options.quiet)
  			error = 0;
  		goto out;
  	}
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
435

db78b877f   Christoph Hellwig   always call inode...
436
437
438
439
440
441
442
  	/*
  	 * 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) {
562c72aa5   Christoph Hellwig   fs: move inode_di...
443
  		inode_dio_wait(inode);
db78b877f   Christoph Hellwig   always call inode...
444
445
446
447
448
449
450
  		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;
  		}
  	}
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
451
  	if (((attr->ia_valid & ATTR_UID) &&
170782eb8   Eric W. Biederman   userns: Convert f...
452
  	     (!uid_eq(attr->ia_uid, sbi->options.fs_uid))) ||
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
453
  	    ((attr->ia_valid & ATTR_GID) &&
170782eb8   Eric W. Biederman   userns: Convert f...
454
  	     (!gid_eq(attr->ia_gid, sbi->options.fs_gid))) ||
e97e8de38   OGAWA Hirofumi   fat: fat_setattr(...
455
  	    ((attr->ia_valid & ATTR_MODE) &&
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
456
  	     (attr->ia_mode & ~FAT_VALID_MODE)))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
457
458
459
460
461
462
463
  		error = -EPERM;
  
  	if (error) {
  		if (sbi->options.quiet)
  			error = 0;
  		goto out;
  	}
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
464
465
466
467
468
469
470
471
  	/*
  	 * 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;
  	}
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
472

459f6ed3b   npiggin@suse.de   fat: convert to u...
473
  	if (attr->ia_valid & ATTR_SIZE) {
c0ef0cc9d   Namjae Jeon   fat: fix data pas...
474
475
476
  		error = fat_block_truncate_page(inode, attr->ia_size);
  		if (error)
  			goto out;
582686915   Christoph Hellwig   fat: remove i_all...
477
  		down_write(&MSDOS_I(inode)->truncate_lock);
2c27c65ed   Christoph Hellwig   check ATTR_SIZE c...
478
479
  		truncate_setsize(inode, attr->ia_size);
  		fat_truncate_blocks(inode, attr->ia_size);
582686915   Christoph Hellwig   fat: remove i_all...
480
  		up_write(&MSDOS_I(inode)->truncate_lock);
459f6ed3b   npiggin@suse.de   fat: convert to u...
481
  	}
6a1a90ad1   Christoph Hellwig   rename generic_se...
482
  	setattr_copy(inode, attr);
459f6ed3b   npiggin@suse.de   fat: convert to u...
483
  	mark_inode_dirty(inode);
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
484
  out:
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
485
486
487
  	return error;
  }
  EXPORT_SYMBOL_GPL(fat_setattr);
754661f14   Arjan van de Ven   [PATCH] mark stru...
488
  const struct inode_operations fat_file_inode_operations = {
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
489
  	.setattr	= fat_setattr,
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
490
  	.getattr	= fat_getattr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
491
  };