Commit 23059a0df5fad3d83b9a21fc2696a39148f49617
Exists in
master
and in
4 other branches
Merge git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/hirofumi/fatfs-2.6: fat: split fat_generic_ioctl FAT: add 'errors' mount option
Showing 10 changed files Side-by-side Diff
Documentation/filesystems/vfat.txt
... | ... | @@ -132,6 +132,11 @@ |
132 | 132 | If you want to use ATTR_RO as read-only flag even for |
133 | 133 | the directory, set this option. |
134 | 134 | |
135 | +errors=panic|continue|remount-ro | |
136 | + -- specify FAT behavior on critical errors: panic, continue | |
137 | + without doing anything or remount the partition in | |
138 | + read-only mode (default behavior). | |
139 | + | |
135 | 140 | <bool>: 0,1,yes,no,true,false |
136 | 141 | |
137 | 142 | TODO |
fs/fat/cache.c
... | ... | @@ -241,7 +241,7 @@ |
241 | 241 | while (*fclus < cluster) { |
242 | 242 | /* prevent the infinite loop of cluster chain */ |
243 | 243 | if (*fclus > limit) { |
244 | - fat_fs_panic(sb, "%s: detected the cluster chain loop" | |
244 | + fat_fs_error(sb, "%s: detected the cluster chain loop" | |
245 | 245 | " (i_pos %lld)", __func__, |
246 | 246 | MSDOS_I(inode)->i_pos); |
247 | 247 | nr = -EIO; |
... | ... | @@ -252,7 +252,7 @@ |
252 | 252 | if (nr < 0) |
253 | 253 | goto out; |
254 | 254 | else if (nr == FAT_ENT_FREE) { |
255 | - fat_fs_panic(sb, "%s: invalid cluster chain" | |
255 | + fat_fs_error(sb, "%s: invalid cluster chain" | |
256 | 256 | " (i_pos %lld)", __func__, |
257 | 257 | MSDOS_I(inode)->i_pos); |
258 | 258 | nr = -EIO; |
... | ... | @@ -285,7 +285,7 @@ |
285 | 285 | if (ret < 0) |
286 | 286 | return ret; |
287 | 287 | else if (ret == FAT_ENT_EOF) { |
288 | - fat_fs_panic(sb, "%s: request beyond EOF (i_pos %lld)", | |
288 | + fat_fs_error(sb, "%s: request beyond EOF (i_pos %lld)", | |
289 | 289 | __func__, MSDOS_I(inode)->i_pos); |
290 | 290 | return -EIO; |
291 | 291 | } |
fs/fat/dir.c
... | ... | @@ -1334,7 +1334,7 @@ |
1334 | 1334 | goto error_remove; |
1335 | 1335 | } |
1336 | 1336 | if (dir->i_size & (sbi->cluster_size - 1)) { |
1337 | - fat_fs_panic(sb, "Odd directory size"); | |
1337 | + fat_fs_error(sb, "Odd directory size"); | |
1338 | 1338 | dir->i_size = (dir->i_size + sbi->cluster_size - 1) |
1339 | 1339 | & ~((loff_t)sbi->cluster_size - 1); |
1340 | 1340 | } |
fs/fat/fat.h
... | ... | @@ -17,6 +17,10 @@ |
17 | 17 | #define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ |
18 | 18 | #define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ |
19 | 19 | |
20 | +#define FAT_ERRORS_CONT 1 /* ignore error and continue */ | |
21 | +#define FAT_ERRORS_PANIC 2 /* panic on error */ | |
22 | +#define FAT_ERRORS_RO 3 /* remount r/o on error */ | |
23 | + | |
20 | 24 | struct fat_mount_options { |
21 | 25 | uid_t fs_uid; |
22 | 26 | gid_t fs_gid; |
... | ... | @@ -26,6 +30,7 @@ |
26 | 30 | char *iocharset; /* Charset used for filename input/display */ |
27 | 31 | unsigned short shortname; /* flags for shortname display/create rule */ |
28 | 32 | unsigned char name_check; /* r = relaxed, n = normal, s = strict */ |
33 | + unsigned char errors; /* On error: continue, panic, remount-ro */ | |
29 | 34 | unsigned short allow_utime;/* permission for setting the [am]time */ |
30 | 35 | unsigned quiet:1, /* set = fake successful chmods and chowns */ |
31 | 36 | showexec:1, /* set = only set x bit for com/exe/bat */ |
... | ... | @@ -316,7 +321,7 @@ |
316 | 321 | extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, |
317 | 322 | struct inode *i2); |
318 | 323 | /* fat/misc.c */ |
319 | -extern void fat_fs_panic(struct super_block *s, const char *fmt, ...) | |
324 | +extern void fat_fs_error(struct super_block *s, const char *fmt, ...) | |
320 | 325 | __attribute__ ((format (printf, 2, 3))) __cold; |
321 | 326 | extern void fat_clusters_flush(struct super_block *sb); |
322 | 327 | extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); |
fs/fat/fatent.c
... | ... | @@ -348,7 +348,7 @@ |
348 | 348 | |
349 | 349 | if (entry < FAT_START_ENT || sbi->max_cluster <= entry) { |
350 | 350 | fatent_brelse(fatent); |
351 | - fat_fs_panic(sb, "invalid access to FAT (entry 0x%08x)", entry); | |
351 | + fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry); | |
352 | 352 | return -EIO; |
353 | 353 | } |
354 | 354 | |
... | ... | @@ -560,7 +560,7 @@ |
560 | 560 | err = cluster; |
561 | 561 | goto error; |
562 | 562 | } else if (cluster == FAT_ENT_FREE) { |
563 | - fat_fs_panic(sb, "%s: deleting FAT entry beyond EOF", | |
563 | + fat_fs_error(sb, "%s: deleting FAT entry beyond EOF", | |
564 | 564 | __func__); |
565 | 565 | err = -EIO; |
566 | 566 | goto error; |
fs/fat/file.c
... | ... | @@ -18,106 +18,112 @@ |
18 | 18 | #include <linux/security.h> |
19 | 19 | #include "fat.h" |
20 | 20 | |
21 | -int fat_generic_ioctl(struct inode *inode, struct file *filp, | |
22 | - unsigned int cmd, unsigned long arg) | |
21 | +static int fat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr) | |
23 | 22 | { |
24 | - struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | |
25 | - u32 __user *user_attr = (u32 __user *)arg; | |
23 | + u32 attr; | |
26 | 24 | |
27 | - switch (cmd) { | |
28 | - case FAT_IOCTL_GET_ATTRIBUTES: | |
29 | - { | |
30 | - u32 attr; | |
25 | + mutex_lock(&inode->i_mutex); | |
26 | + attr = fat_make_attrs(inode); | |
27 | + mutex_unlock(&inode->i_mutex); | |
31 | 28 | |
32 | - mutex_lock(&inode->i_mutex); | |
33 | - attr = fat_make_attrs(inode); | |
34 | - mutex_unlock(&inode->i_mutex); | |
29 | + return put_user(attr, user_attr); | |
30 | +} | |
35 | 31 | |
36 | - return put_user(attr, user_attr); | |
37 | - } | |
38 | - case FAT_IOCTL_SET_ATTRIBUTES: | |
39 | - { | |
40 | - u32 attr, oldattr; | |
41 | - int err, is_dir = S_ISDIR(inode->i_mode); | |
42 | - struct iattr ia; | |
32 | +static int fat_ioctl_set_attributes(struct file *file, u32 __user *user_attr) | |
33 | +{ | |
34 | + struct inode *inode = file->f_path.dentry->d_inode; | |
35 | + struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | |
36 | + int is_dir = S_ISDIR(inode->i_mode); | |
37 | + u32 attr, oldattr; | |
38 | + struct iattr ia; | |
39 | + int err; | |
43 | 40 | |
44 | - err = get_user(attr, user_attr); | |
45 | - if (err) | |
46 | - return err; | |
41 | + err = get_user(attr, user_attr); | |
42 | + if (err) | |
43 | + goto out; | |
47 | 44 | |
48 | - mutex_lock(&inode->i_mutex); | |
45 | + mutex_lock(&inode->i_mutex); | |
46 | + err = mnt_want_write(file->f_path.mnt); | |
47 | + if (err) | |
48 | + goto out_unlock_inode; | |
49 | 49 | |
50 | - err = mnt_want_write(filp->f_path.mnt); | |
51 | - if (err) | |
52 | - goto up_no_drop_write; | |
50 | + /* | |
51 | + * ATTR_VOLUME and ATTR_DIR cannot be changed; this also | |
52 | + * prevents the user from turning us into a VFAT | |
53 | + * longname entry. Also, we obviously can't set | |
54 | + * any of the NTFS attributes in the high 24 bits. | |
55 | + */ | |
56 | + attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); | |
57 | + /* Merge in ATTR_VOLUME and ATTR_DIR */ | |
58 | + attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | | |
59 | + (is_dir ? ATTR_DIR : 0); | |
60 | + oldattr = fat_make_attrs(inode); | |
53 | 61 | |
54 | - /* | |
55 | - * ATTR_VOLUME and ATTR_DIR cannot be changed; this also | |
56 | - * prevents the user from turning us into a VFAT | |
57 | - * longname entry. Also, we obviously can't set | |
58 | - * any of the NTFS attributes in the high 24 bits. | |
59 | - */ | |
60 | - attr &= 0xff & ~(ATTR_VOLUME | ATTR_DIR); | |
61 | - /* Merge in ATTR_VOLUME and ATTR_DIR */ | |
62 | - attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | | |
63 | - (is_dir ? ATTR_DIR : 0); | |
64 | - oldattr = fat_make_attrs(inode); | |
62 | + /* Equivalent to a chmod() */ | |
63 | + ia.ia_valid = ATTR_MODE | ATTR_CTIME; | |
64 | + ia.ia_ctime = current_fs_time(inode->i_sb); | |
65 | + if (is_dir) | |
66 | + ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO); | |
67 | + else { | |
68 | + ia.ia_mode = fat_make_mode(sbi, attr, | |
69 | + S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)); | |
70 | + } | |
65 | 71 | |
66 | - /* Equivalent to a chmod() */ | |
67 | - ia.ia_valid = ATTR_MODE | ATTR_CTIME; | |
68 | - ia.ia_ctime = current_fs_time(inode->i_sb); | |
69 | - if (is_dir) | |
70 | - ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO); | |
71 | - else { | |
72 | - ia.ia_mode = fat_make_mode(sbi, attr, | |
73 | - S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)); | |
74 | - } | |
72 | + /* The root directory has no attributes */ | |
73 | + if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { | |
74 | + err = -EINVAL; | |
75 | + goto out_drop_write; | |
76 | + } | |
75 | 77 | |
76 | - /* The root directory has no attributes */ | |
77 | - if (inode->i_ino == MSDOS_ROOT_INO && attr != ATTR_DIR) { | |
78 | - err = -EINVAL; | |
79 | - goto up; | |
80 | - } | |
78 | + if (sbi->options.sys_immutable && | |
79 | + ((attr | oldattr) & ATTR_SYS) && | |
80 | + !capable(CAP_LINUX_IMMUTABLE)) { | |
81 | + err = -EPERM; | |
82 | + goto out_drop_write; | |
83 | + } | |
81 | 84 | |
82 | - if (sbi->options.sys_immutable) { | |
83 | - if ((attr | oldattr) & ATTR_SYS) { | |
84 | - if (!capable(CAP_LINUX_IMMUTABLE)) { | |
85 | - err = -EPERM; | |
86 | - goto up; | |
87 | - } | |
88 | - } | |
89 | - } | |
85 | + /* | |
86 | + * The security check is questionable... We single | |
87 | + * out the RO attribute for checking by the security | |
88 | + * module, just because it maps to a file mode. | |
89 | + */ | |
90 | + err = security_inode_setattr(file->f_path.dentry, &ia); | |
91 | + if (err) | |
92 | + goto out_drop_write; | |
90 | 93 | |
91 | - /* | |
92 | - * The security check is questionable... We single | |
93 | - * out the RO attribute for checking by the security | |
94 | - * module, just because it maps to a file mode. | |
95 | - */ | |
96 | - err = security_inode_setattr(filp->f_path.dentry, &ia); | |
97 | - if (err) | |
98 | - goto up; | |
94 | + /* This MUST be done before doing anything irreversible... */ | |
95 | + err = fat_setattr(file->f_path.dentry, &ia); | |
96 | + if (err) | |
97 | + goto out_drop_write; | |
99 | 98 | |
100 | - /* This MUST be done before doing anything irreversible... */ | |
101 | - err = fat_setattr(filp->f_path.dentry, &ia); | |
102 | - if (err) | |
103 | - goto up; | |
99 | + fsnotify_change(file->f_path.dentry, ia.ia_valid); | |
100 | + if (sbi->options.sys_immutable) { | |
101 | + if (attr & ATTR_SYS) | |
102 | + inode->i_flags |= S_IMMUTABLE; | |
103 | + else | |
104 | + inode->i_flags &= S_IMMUTABLE; | |
105 | + } | |
104 | 106 | |
105 | - fsnotify_change(filp->f_path.dentry, ia.ia_valid); | |
106 | - if (sbi->options.sys_immutable) { | |
107 | - if (attr & ATTR_SYS) | |
108 | - inode->i_flags |= S_IMMUTABLE; | |
109 | - else | |
110 | - inode->i_flags &= S_IMMUTABLE; | |
111 | - } | |
107 | + fat_save_attrs(inode, attr); | |
108 | + mark_inode_dirty(inode); | |
109 | +out_drop_write: | |
110 | + mnt_drop_write(file->f_path.mnt); | |
111 | +out_unlock_inode: | |
112 | + mutex_unlock(&inode->i_mutex); | |
113 | +out: | |
114 | + return err; | |
115 | +} | |
112 | 116 | |
113 | - fat_save_attrs(inode, attr); | |
114 | - mark_inode_dirty(inode); | |
115 | -up: | |
116 | - mnt_drop_write(filp->f_path.mnt); | |
117 | -up_no_drop_write: | |
118 | - mutex_unlock(&inode->i_mutex); | |
119 | - return err; | |
120 | - } | |
117 | +int fat_generic_ioctl(struct inode *inode, struct file *filp, | |
118 | + unsigned int cmd, unsigned long arg) | |
119 | +{ | |
120 | + u32 __user *user_attr = (u32 __user *)arg; | |
121 | + | |
122 | + switch (cmd) { | |
123 | + case FAT_IOCTL_GET_ATTRIBUTES: | |
124 | + return fat_ioctl_get_attributes(inode, user_attr); | |
125 | + case FAT_IOCTL_SET_ATTRIBUTES: | |
126 | + return fat_ioctl_set_attributes(filp, user_attr); | |
121 | 127 | default: |
122 | 128 | return -ENOTTY; /* Inappropriate ioctl for device */ |
123 | 129 | } |
... | ... | @@ -225,7 +231,7 @@ |
225 | 231 | fatent_brelse(&fatent); |
226 | 232 | return 0; |
227 | 233 | } else if (ret == FAT_ENT_FREE) { |
228 | - fat_fs_panic(sb, | |
234 | + fat_fs_error(sb, | |
229 | 235 | "%s: invalid cluster chain (i_pos %lld)", |
230 | 236 | __func__, MSDOS_I(inode)->i_pos); |
231 | 237 | ret = -EIO; |
fs/fat/inode.c
... | ... | @@ -76,7 +76,7 @@ |
76 | 76 | return 0; |
77 | 77 | |
78 | 78 | if (iblock != MSDOS_I(inode)->mmu_private >> sb->s_blocksize_bits) { |
79 | - fat_fs_panic(sb, "corrupted file size (i_pos %lld, %lld)", | |
79 | + fat_fs_error(sb, "corrupted file size (i_pos %lld, %lld)", | |
80 | 80 | MSDOS_I(inode)->i_pos, MSDOS_I(inode)->mmu_private); |
81 | 81 | return -EIO; |
82 | 82 | } |
... | ... | @@ -856,6 +856,12 @@ |
856 | 856 | seq_puts(m, ",flush"); |
857 | 857 | if (opts->tz_utc) |
858 | 858 | seq_puts(m, ",tz=UTC"); |
859 | + if (opts->errors == FAT_ERRORS_CONT) | |
860 | + seq_puts(m, ",errors=continue"); | |
861 | + else if (opts->errors == FAT_ERRORS_PANIC) | |
862 | + seq_puts(m, ",errors=panic"); | |
863 | + else | |
864 | + seq_puts(m, ",errors=remount-ro"); | |
859 | 865 | |
860 | 866 | return 0; |
861 | 867 | } |
... | ... | @@ -868,7 +874,8 @@ |
868 | 874 | Opt_charset, Opt_shortname_lower, Opt_shortname_win95, |
869 | 875 | Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, |
870 | 876 | Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, |
871 | - Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err, | |
877 | + Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err_cont, | |
878 | + Opt_err_panic, Opt_err_ro, Opt_err, | |
872 | 879 | }; |
873 | 880 | |
874 | 881 | static const match_table_t fat_tokens = { |
... | ... | @@ -891,6 +898,11 @@ |
891 | 898 | {Opt_showexec, "showexec"}, |
892 | 899 | {Opt_debug, "debug"}, |
893 | 900 | {Opt_immutable, "sys_immutable"}, |
901 | + {Opt_flush, "flush"}, | |
902 | + {Opt_tz_utc, "tz=UTC"}, | |
903 | + {Opt_err_cont, "errors=continue"}, | |
904 | + {Opt_err_panic, "errors=panic"}, | |
905 | + {Opt_err_ro, "errors=remount-ro"}, | |
894 | 906 | {Opt_obsolate, "conv=binary"}, |
895 | 907 | {Opt_obsolate, "conv=text"}, |
896 | 908 | {Opt_obsolate, "conv=auto"}, |
... | ... | @@ -902,8 +914,6 @@ |
902 | 914 | {Opt_obsolate, "cvf_format=%20s"}, |
903 | 915 | {Opt_obsolate, "cvf_options=%100s"}, |
904 | 916 | {Opt_obsolate, "posix"}, |
905 | - {Opt_flush, "flush"}, | |
906 | - {Opt_tz_utc, "tz=UTC"}, | |
907 | 917 | {Opt_err, NULL}, |
908 | 918 | }; |
909 | 919 | static const match_table_t msdos_tokens = { |
... | ... | @@ -973,6 +983,7 @@ |
973 | 983 | opts->numtail = 1; |
974 | 984 | opts->usefree = opts->nocase = 0; |
975 | 985 | opts->tz_utc = 0; |
986 | + opts->errors = FAT_ERRORS_RO; | |
976 | 987 | *debug = 0; |
977 | 988 | |
978 | 989 | if (!options) |
... | ... | @@ -1064,6 +1075,15 @@ |
1064 | 1075 | break; |
1065 | 1076 | case Opt_tz_utc: |
1066 | 1077 | opts->tz_utc = 1; |
1078 | + break; | |
1079 | + case Opt_err_cont: | |
1080 | + opts->errors = FAT_ERRORS_CONT; | |
1081 | + break; | |
1082 | + case Opt_err_panic: | |
1083 | + opts->errors = FAT_ERRORS_PANIC; | |
1084 | + break; | |
1085 | + case Opt_err_ro: | |
1086 | + opts->errors = FAT_ERRORS_RO; | |
1067 | 1087 | break; |
1068 | 1088 | |
1069 | 1089 | /* msdos specific */ |
fs/fat/misc.c
... | ... | @@ -12,14 +12,19 @@ |
12 | 12 | #include "fat.h" |
13 | 13 | |
14 | 14 | /* |
15 | - * fat_fs_panic reports a severe file system problem and sets the file system | |
16 | - * read-only. The file system can be made writable again by remounting it. | |
15 | + * fat_fs_error reports a file system problem that might indicate fa data | |
16 | + * corruption/inconsistency. Depending on 'errors' mount option the | |
17 | + * panic() is called, or error message is printed FAT and nothing is done, | |
18 | + * or filesystem is remounted read-only (default behavior). | |
19 | + * In case the file system is remounted read-only, it can be made writable | |
20 | + * again by remounting it. | |
17 | 21 | */ |
18 | -void fat_fs_panic(struct super_block *s, const char *fmt, ...) | |
22 | +void fat_fs_error(struct super_block *s, const char *fmt, ...) | |
19 | 23 | { |
24 | + struct fat_mount_options *opts = &MSDOS_SB(s)->options; | |
20 | 25 | va_list args; |
21 | 26 | |
22 | - printk(KERN_ERR "FAT: Filesystem panic (dev %s)\n", s->s_id); | |
27 | + printk(KERN_ERR "FAT: Filesystem error (dev %s)\n", s->s_id); | |
23 | 28 | |
24 | 29 | printk(KERN_ERR " "); |
25 | 30 | va_start(args, fmt); |
26 | 31 | |
27 | 32 | |
... | ... | @@ -27,14 +32,15 @@ |
27 | 32 | va_end(args); |
28 | 33 | printk("\n"); |
29 | 34 | |
30 | - if (!(s->s_flags & MS_RDONLY)) { | |
35 | + if (opts->errors == FAT_ERRORS_PANIC) | |
36 | + panic(" FAT fs panic from previous error\n"); | |
37 | + else if (opts->errors == FAT_ERRORS_RO && !(s->s_flags & MS_RDONLY)) { | |
31 | 38 | s->s_flags |= MS_RDONLY; |
32 | 39 | printk(KERN_ERR " File system has been set read-only\n"); |
33 | 40 | } |
34 | 41 | } |
42 | +EXPORT_SYMBOL_GPL(fat_fs_error); | |
35 | 43 | |
36 | -EXPORT_SYMBOL_GPL(fat_fs_panic); | |
37 | - | |
38 | 44 | /* Flushes the number of free clusters on FAT32 */ |
39 | 45 | /* XXX: Need to write one per FSINFO block. Currently only writes 1 */ |
40 | 46 | void fat_clusters_flush(struct super_block *sb) |
... | ... | @@ -124,7 +130,7 @@ |
124 | 130 | mark_inode_dirty(inode); |
125 | 131 | } |
126 | 132 | if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) { |
127 | - fat_fs_panic(sb, "clusters badly computed (%d != %llu)", | |
133 | + fat_fs_error(sb, "clusters badly computed (%d != %llu)", | |
128 | 134 | new_fclus, |
129 | 135 | (llu)(inode->i_blocks >> (sbi->cluster_bits - 9))); |
130 | 136 | fat_cache_inval_inode(inode); |
fs/fat/namei_msdos.c
fs/fat/namei_vfat.c