Blame view

fs/fat/file.c 9.42 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>
42a74f206   Dave Hansen   [PATCH] r/o bind ...
10
  #include <linux/mount.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
  #include <linux/time.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
12
  #include <linux/buffer_head.h>
05eb0b51f   OGAWA Hirofumi   [PATCH] fat: supp...
13
  #include <linux/writeback.h>
3fcfab16c   Andrew Morton   [PATCH] separate ...
14
  #include <linux/backing-dev.h>
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
15
  #include <linux/blkdev.h>
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
16
17
  #include <linux/fsnotify.h>
  #include <linux/security.h>
9e975dae2   OGAWA Hirofumi   fat: split includ...
18
  #include "fat.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
19

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
20
21
22
23
24
25
26
27
28
  int fat_generic_ioctl(struct inode *inode, struct file *filp,
  		      unsigned int cmd, unsigned long arg)
  {
  	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
  	u32 __user *user_attr = (u32 __user *)arg;
  
  	switch (cmd) {
  	case FAT_IOCTL_GET_ATTRIBUTES:
  	{
9183482f5   OGAWA Hirofumi   fat: Fix ATTR_RO ...
29
30
31
32
33
  		u32 attr;
  
  		mutex_lock(&inode->i_mutex);
  		attr = fat_make_attrs(inode);
  		mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34
35
36
37
38
39
40
41
42
43
44
  		return put_user(attr, user_attr);
  	}
  	case FAT_IOCTL_SET_ATTRIBUTES:
  	{
  		u32 attr, oldattr;
  		int err, is_dir = S_ISDIR(inode->i_mode);
  		struct iattr ia;
  
  		err = get_user(attr, user_attr);
  		if (err)
  			return err;
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
45
  		mutex_lock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
46

42a74f206   Dave Hansen   [PATCH] r/o bind ...
47
48
49
  		err = mnt_want_write(filp->f_path.mnt);
  		if (err)
  			goto up_no_drop_write;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
50
51
52
53
54
55
56
57
58
59
60
  
  		/*
  		 * 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);
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
61
  		oldattr = fat_make_attrs(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
63
64
  
  		/* Equivalent to a chmod() */
  		ia.ia_valid = ATTR_MODE | ATTR_CTIME;
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
65
  		ia.ia_ctime = current_fs_time(inode->i_sb);
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
66
67
68
69
70
  		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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
  		}
  
  		/* The root directory has no attributes */
  		if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) {
  			err = -EINVAL;
  			goto up;
  		}
  
  		if (sbi->options.sys_immutable) {
  			if ((attr | oldattr) & ATTR_SYS) {
  				if (!capable(CAP_LINUX_IMMUTABLE)) {
  					err = -EPERM;
  					goto up;
  				}
  			}
  		}
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
87
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(filp->f_path.dentry, &ia);
  		if (err)
  			goto up;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95
  		/* This MUST be done before doing anything irreversible... */
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
96
  		err = fat_setattr(filp->f_path.dentry, &ia);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
97
98
  		if (err)
  			goto up;
b1da47e29   Miklos Szeredi   [patch 3/4] fat: ...
99
  		fsnotify_change(filp->f_path.dentry, ia.ia_valid);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100
101
102
103
104
105
  		if (sbi->options.sys_immutable) {
  			if (attr & ATTR_SYS)
  				inode->i_flags |= S_IMMUTABLE;
  			else
  				inode->i_flags &= S_IMMUTABLE;
  		}
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
106
  		fat_save_attrs(inode, attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
  		mark_inode_dirty(inode);
42a74f206   Dave Hansen   [PATCH] r/o bind ...
108
109
110
  up:
  		mnt_drop_write(filp->f_path.mnt);
  up_no_drop_write:
1b1dcc1b5   Jes Sorensen   [PATCH] mutex sub...
111
  		mutex_unlock(&inode->i_mutex);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
112
113
114
115
116
117
  		return err;
  	}
  	default:
  		return -ENOTTY;	/* Inappropriate ioctl for device */
  	}
  }
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
118
119
120
121
122
  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);
3fcfab16c   Andrew Morton   [PATCH] separate ...
123
  		congestion_wait(WRITE, HZ/10);
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
124
125
126
  	}
  	return 0;
  }
4b6f5d20b   Arjan van de Ven   [PATCH] Make most...
127
  const struct file_operations fat_file_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
128
129
130
  	.llseek		= generic_file_llseek,
  	.read		= do_sync_read,
  	.write		= do_sync_write,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
131
  	.aio_read	= generic_file_aio_read,
ef402268f   OGAWA Hirofumi   [PATCH] FAT: miss...
132
  	.aio_write	= generic_file_aio_write,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
133
  	.mmap		= generic_file_mmap,
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
134
  	.release	= fat_file_release,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
135
136
  	.ioctl		= fat_generic_ioctl,
  	.fsync		= file_fsync,
5ffc4ef45   Jens Axboe   sendfile: remove ...
137
  	.splice_read	= generic_file_splice_read,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
138
  };
05eb0b51f   OGAWA Hirofumi   [PATCH] fat: supp...
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  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;
  
  	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
  	mark_inode_dirty(inode);
  	if (IS_SYNC(inode))
  		err = sync_page_range_nolock(inode, mapping, start, count);
  out:
  	return err;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
159
160
161
162
163
  /* 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 ...
164
  	fat_cache_inval_inode(inode);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
165
  	wait = IS_DIRSYNC(inode);
3b641407a   OGAWA Hirofumi   [PATCH] fat: Fix ...
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
  	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;
  	inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
  	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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  	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) {
  			fat_fs_panic(sb,
  				     "%s: invalid cluster chain (i_pos %lld)",
8e24eea72   Harvey Harrison   fs: replace remai...
205
  				     __func__, MSDOS_I(inode)->i_pos);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
206
207
208
209
210
211
212
213
214
215
216
  			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
217
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
218
219
220
221
  	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
222
223
224
225
  }
  
  void fat_truncate(struct inode *inode)
  {
9c20616c3   Jonathan Corbet   Make FAT users ha...
226
  	struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
227
228
229
230
231
232
233
234
235
236
237
  	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.
  	 */
  	if (MSDOS_I(inode)->mmu_private > inode->i_size)
  		MSDOS_I(inode)->mmu_private = inode->i_size;
  
  	nr_clusters = (inode->i_size + (cluster_size - 1)) >> sbi->cluster_bits;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
238
  	fat_free(inode, nr_clusters);
ae78bf9c4   Chris Mason   [PATCH] add -o fl...
239
  	fat_flush_inodes(inode->i_sb, inode, NULL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
240
  }
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
241
242
243
244
245
246
247
248
  int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
  {
  	struct inode *inode = dentry->d_inode;
  	generic_fillattr(inode, stat);
  	stat->blksize = MSDOS_SB(inode->i_sb)->cluster_size;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(fat_getattr);
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
249
250
  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...
251
  {
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
252
  	mode_t mask, perm;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
253

2d518f84e   OGAWA Hirofumi   fat: relax the pe...
254
255
  	/*
  	 * Note, the basic check is already done by a caller of
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
256
  	 * (attr->ia_mode & ~FAT_VALID_MODE)
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
257
258
259
  	 */
  
  	if (S_ISREG(inode->i_mode))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
260
261
262
  		mask = sbi->options.fs_fmask;
  	else
  		mask = sbi->options.fs_dmask;
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
263
  	perm = *mode_ptr & ~(S_IFMT | mask);
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
264
265
266
  	/*
  	 * 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 ...
267
268
  	 *
  	 * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
269
  	 */
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
270
  	if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
271
  		return -EPERM;
dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
272
273
274
275
276
277
278
  	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...
279

2d518f84e   OGAWA Hirofumi   fat: relax the pe...
280
  	*mode_ptr &= S_IFMT | perm;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
281
282
  	return 0;
  }
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode)
  {
  	mode_t allow_utime = sbi->options.allow_utime;
  
  	if (current->fsuid != inode->i_uid) {
  		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...
297
  #define TIMES_SET_FLAGS	(ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
298
299
  /* valid file mode bits */
  #define FAT_VALID_MODE	(S_IFREG | S_IFDIR | S_IRWXUGO)
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
300

1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
301
302
303
304
  int fat_setattr(struct dentry *dentry, struct iattr *attr)
  {
  	struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
  	struct inode *inode = dentry->d_inode;
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
305
  	unsigned int ia_valid;
dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
306
  	int error;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
307

1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
308
309
310
311
312
313
314
315
316
317
318
319
320
  	/*
  	 * Expand the file. Since inode_setattr() updates ->i_size
  	 * before calling the ->truncate(), but FAT needs to fill the
  	 * hole before it.
  	 */
  	if (attr->ia_valid & ATTR_SIZE) {
  		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;
  		}
  	}
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
321
322
  	/* Check for setting the inode time. */
  	ia_valid = attr->ia_valid;
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
323
  	if (ia_valid & TIMES_SET_FLAGS) {
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
324
  		if (fat_allow_set_time(sbi, inode))
17263849c   OGAWA Hirofumi   fat: Fix allow_ut...
325
  			attr->ia_valid &= ~TIMES_SET_FLAGS;
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
326
  	}
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
327
  	error = inode_change_ok(inode, attr);
1ae43f826   OGAWA Hirofumi   fat: Add allow_ut...
328
  	attr->ia_valid = ia_valid;
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
329
330
331
332
333
  	if (error) {
  		if (sbi->options.quiet)
  			error = 0;
  		goto out;
  	}
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
334

1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
335
336
337
  	if (((attr->ia_valid & ATTR_UID) &&
  	     (attr->ia_uid != sbi->options.fs_uid)) ||
  	    ((attr->ia_valid & ATTR_GID) &&
e97e8de38   OGAWA Hirofumi   fat: fat_setattr(...
338
339
  	     (attr->ia_gid != sbi->options.fs_gid)) ||
  	    ((attr->ia_valid & ATTR_MODE) &&
9c0aa1b87   OGAWA Hirofumi   fat: Cleanup FAT ...
340
  	     (attr->ia_mode & ~FAT_VALID_MODE)))
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
341
342
343
344
345
346
347
  		error = -EPERM;
  
  	if (error) {
  		if (sbi->options.quiet)
  			error = 0;
  		goto out;
  	}
2d518f84e   OGAWA Hirofumi   fat: relax the pe...
348
349
350
351
352
353
354
355
  	/*
  	 * 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...
356

dfc209c00   OGAWA Hirofumi   fat: Fix ATTR_RO ...
357
358
  	if (attr->ia_valid)
  		error = inode_setattr(inode, attr);
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
359
  out:
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
360
361
362
  	return error;
  }
  EXPORT_SYMBOL_GPL(fat_setattr);
754661f14   Arjan van de Ven   [PATCH] mark stru...
363
  const struct inode_operations fat_file_inode_operations = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
364
  	.truncate	= fat_truncate,
1278fdd34   OGAWA Hirofumi   fat: fat_notify_c...
365
  	.setattr	= fat_setattr,
da63fc7ce   OGAWA Hirofumi   [PATCH] fat: add ...
366
  	.getattr	= fat_getattr,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  };