aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/filesystems/vfat.txt8
-rw-r--r--fs/fat/fat.h14
-rw-r--r--fs/fat/file.c16
-rw-r--r--fs/fat/inode.c17
4 files changed, 43 insertions, 12 deletions
diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt
index dc9dc73d7d38..3a5ddc96901a 100644
--- a/Documentation/filesystems/vfat.txt
+++ b/Documentation/filesystems/vfat.txt
@@ -124,6 +124,14 @@ sys_immutable -- If set, ATTR_SYS attribute on FAT is handled as
124flush -- If set, the filesystem will try to flush to disk more 124flush -- If set, the filesystem will try to flush to disk more
125 early than normal. Not set by default. 125 early than normal. Not set by default.
126 126
127rodir -- FAT has the ATTR_RO (read-only) attribute. But on Windows,
128 the ATTR_RO of the directory will be just ignored actually,
129 and is used by only applications as flag. E.g. it's setted
130 for the customized folder.
131
132 If you want to use ATTR_RO as read-only flag even for
133 the directory, set this option.
134
127<bool>: 0,1,yes,no,true,false 135<bool>: 0,1,yes,no,true,false
128 136
129TODO 137TODO
diff --git a/fs/fat/fat.h b/fs/fat/fat.h
index 313b645b8126..e9dce5d8e7a7 100644
--- a/fs/fat/fat.h
+++ b/fs/fat/fat.h
@@ -38,7 +38,8 @@ struct fat_mount_options {
38 flush:1, /* write things quickly */ 38 flush:1, /* write things quickly */
39 nocase:1, /* Does this need case conversion? 0=need case conversion*/ 39 nocase:1, /* Does this need case conversion? 0=need case conversion*/
40 usefree:1, /* Use free_clusters for FAT32 */ 40 usefree:1, /* Use free_clusters for FAT32 */
41 tz_utc:1; /* Filesystem timestamps are in UTC */ 41 tz_utc:1, /* Filesystem timestamps are in UTC */
42 rodir:1; /* allow ATTR_RO for directory */
42}; 43};
43 44
44#define FAT_HASH_BITS 8 45#define FAT_HASH_BITS 8
@@ -120,15 +121,20 @@ static inline struct msdos_inode_info *MSDOS_I(struct inode *inode)
120/* 121/*
121 * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to 122 * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to
122 * save ATTR_RO instead of ->i_mode. 123 * save ATTR_RO instead of ->i_mode.
124 *
125 * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only
126 * bit, it's just used as flag for app.
123 */ 127 */
124static inline int fat_mode_can_hold_ro(struct inode *inode) 128static inline int fat_mode_can_hold_ro(struct inode *inode)
125{ 129{
126 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); 130 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
127 mode_t mask; 131 mode_t mask;
128 132
129 if (S_ISDIR(inode->i_mode)) 133 if (S_ISDIR(inode->i_mode)) {
134 if (!sbi->options.rodir)
135 return 0;
130 mask = ~sbi->options.fs_dmask; 136 mask = ~sbi->options.fs_dmask;
131 else 137 } else
132 mask = ~sbi->options.fs_fmask; 138 mask = ~sbi->options.fs_fmask;
133 139
134 if (!(mask & S_IWUGO)) 140 if (!(mask & S_IWUGO))
@@ -140,7 +146,7 @@ static inline int fat_mode_can_hold_ro(struct inode *inode)
140static inline mode_t fat_make_mode(struct msdos_sb_info *sbi, 146static inline mode_t fat_make_mode(struct msdos_sb_info *sbi,
141 u8 attrs, mode_t mode) 147 u8 attrs, mode_t mode)
142{ 148{
143 if (attrs & ATTR_RO) 149 if (attrs & ATTR_RO && !((attrs & ATTR_DIR) && !sbi->options.rodir))
144 mode &= ~S_IWUGO; 150 mode &= ~S_IWUGO;
145 151
146 if (attrs & ATTR_DIR) 152 if (attrs & ATTR_DIR)
diff --git a/fs/fat/file.c b/fs/fat/file.c
index 81b15c623803..f06a4e525ece 100644
--- a/fs/fat/file.c
+++ b/fs/fat/file.c
@@ -282,11 +282,18 @@ static int fat_sanitize_mode(const struct msdos_sb_info *sbi,
282 /* 282 /*
283 * Of the r and x bits, all (subject to umask) must be present. Of the 283 * Of the r and x bits, all (subject to umask) must be present. Of the
284 * w bits, either all (subject to umask) or none must be present. 284 * w bits, either all (subject to umask) or none must be present.
285 *
286 * If fat_mode_can_hold_ro(inode) is false, can't change w bits.
285 */ 287 */
286 if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO))) 288 if ((perm & (S_IRUGO | S_IXUGO)) != (inode->i_mode & (S_IRUGO|S_IXUGO)))
287 return -EPERM; 289 return -EPERM;
288 if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) 290 if (fat_mode_can_hold_ro(inode)) {
289 return -EPERM; 291 if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask)))
292 return -EPERM;
293 } else {
294 if ((perm & S_IWUGO) != (S_IWUGO & ~mask))
295 return -EPERM;
296 }
290 297
291 *mode_ptr &= S_IFMT | perm; 298 *mode_ptr &= S_IFMT | perm;
292 299
@@ -316,8 +323,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
316{ 323{
317 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); 324 struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb);
318 struct inode *inode = dentry->d_inode; 325 struct inode *inode = dentry->d_inode;
319 int error = 0;
320 unsigned int ia_valid; 326 unsigned int ia_valid;
327 int error;
321 328
322 /* 329 /*
323 * Expand the file. Since inode_setattr() updates ->i_size 330 * Expand the file. Since inode_setattr() updates ->i_size
@@ -371,7 +378,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr)
371 attr->ia_valid &= ~ATTR_MODE; 378 attr->ia_valid &= ~ATTR_MODE;
372 } 379 }
373 380
374 error = inode_setattr(inode, attr); 381 if (attr->ia_valid)
382 error = inode_setattr(inode, attr);
375out: 383out:
376 return error; 384 return error;
377} 385}
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 7aaa21cf019a..0da04e6d1e34 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -797,8 +797,10 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt)
797 seq_puts(m, ",uni_xlate"); 797 seq_puts(m, ",uni_xlate");
798 if (!opts->numtail) 798 if (!opts->numtail)
799 seq_puts(m, ",nonumtail"); 799 seq_puts(m, ",nonumtail");
800 if (opts->rodir)
801 seq_puts(m, ",rodir");
800 } 802 }
801 if (sbi->options.flush) 803 if (opts->flush)
802 seq_puts(m, ",flush"); 804 seq_puts(m, ",flush");
803 if (opts->tz_utc) 805 if (opts->tz_utc)
804 seq_puts(m, ",tz=UTC"); 806 seq_puts(m, ",tz=UTC");
@@ -814,7 +816,7 @@ enum {
814 Opt_charset, Opt_shortname_lower, Opt_shortname_win95, 816 Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
815 Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, 817 Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
816 Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, 818 Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
817 Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, 819 Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
818}; 820};
819 821
820static const match_table_t fat_tokens = { 822static const match_table_t fat_tokens = {
@@ -886,6 +888,7 @@ static const match_table_t vfat_tokens = {
886 {Opt_nonumtail_yes, "nonumtail=yes"}, 888 {Opt_nonumtail_yes, "nonumtail=yes"},
887 {Opt_nonumtail_yes, "nonumtail=true"}, 889 {Opt_nonumtail_yes, "nonumtail=true"},
888 {Opt_nonumtail_yes, "nonumtail"}, 890 {Opt_nonumtail_yes, "nonumtail"},
891 {Opt_rodir, "rodir"},
889 {Opt_err, NULL} 892 {Opt_err, NULL}
890}; 893};
891 894
@@ -905,10 +908,13 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
905 opts->allow_utime = -1; 908 opts->allow_utime = -1;
906 opts->codepage = fat_default_codepage; 909 opts->codepage = fat_default_codepage;
907 opts->iocharset = fat_default_iocharset; 910 opts->iocharset = fat_default_iocharset;
908 if (is_vfat) 911 if (is_vfat) {
909 opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95; 912 opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95;
910 else 913 opts->rodir = 0;
914 } else {
911 opts->shortname = 0; 915 opts->shortname = 0;
916 opts->rodir = 1;
917 }
912 opts->name_check = 'n'; 918 opts->name_check = 'n';
913 opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0; 919 opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0;
914 opts->utf8 = opts->unicode_xlate = 0; 920 opts->utf8 = opts->unicode_xlate = 0;
@@ -1059,6 +1065,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
1059 case Opt_nonumtail_yes: /* empty or 1 or yes or true */ 1065 case Opt_nonumtail_yes: /* empty or 1 or yes or true */
1060 opts->numtail = 0; /* negated option */ 1066 opts->numtail = 0; /* negated option */
1061 break; 1067 break;
1068 case Opt_rodir:
1069 opts->rodir = 1;
1070 break;
1062 1071
1063 /* obsolete mount options */ 1072 /* obsolete mount options */
1064 case Opt_obsolate: 1073 case Opt_obsolate: