aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fat/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fat/inode.c')
-rw-r--r--fs/fat/inode.c133
1 files changed, 72 insertions, 61 deletions
diff --git a/fs/fat/inode.c b/fs/fat/inode.c
index 19eafbe3c379..bdd8fb7be2ca 100644
--- a/fs/fat/inode.c
+++ b/fs/fat/inode.c
@@ -16,7 +16,6 @@
16#include <linux/slab.h> 16#include <linux/slab.h>
17#include <linux/smp_lock.h> 17#include <linux/smp_lock.h>
18#include <linux/seq_file.h> 18#include <linux/seq_file.h>
19#include <linux/msdos_fs.h>
20#include <linux/pagemap.h> 19#include <linux/pagemap.h>
21#include <linux/mpage.h> 20#include <linux/mpage.h>
22#include <linux/buffer_head.h> 21#include <linux/buffer_head.h>
@@ -27,7 +26,9 @@
27#include <linux/uio.h> 26#include <linux/uio.h>
28#include <linux/writeback.h> 27#include <linux/writeback.h>
29#include <linux/log2.h> 28#include <linux/log2.h>
29#include <linux/hash.h>
30#include <asm/unaligned.h> 30#include <asm/unaligned.h>
31#include "fat.h"
31 32
32#ifndef CONFIG_FAT_DEFAULT_IOCHARSET 33#ifndef CONFIG_FAT_DEFAULT_IOCHARSET
33/* if user don't select VFAT, this is undefined. */ 34/* if user don't select VFAT, this is undefined. */
@@ -63,7 +64,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
63 sector_t phys; 64 sector_t phys;
64 int err, offset; 65 int err, offset;
65 66
66 err = fat_bmap(inode, iblock, &phys, &mapped_blocks); 67 err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
67 if (err) 68 if (err)
68 return err; 69 return err;
69 if (phys) { 70 if (phys) {
@@ -93,7 +94,7 @@ static inline int __fat_get_block(struct inode *inode, sector_t iblock,
93 *max_blocks = min(mapped_blocks, *max_blocks); 94 *max_blocks = min(mapped_blocks, *max_blocks);
94 MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits; 95 MSDOS_I(inode)->mmu_private += *max_blocks << sb->s_blocksize_bits;
95 96
96 err = fat_bmap(inode, iblock, &phys, &mapped_blocks); 97 err = fat_bmap(inode, iblock, &phys, &mapped_blocks, create);
97 if (err) 98 if (err)
98 return err; 99 return err;
99 100
@@ -175,7 +176,7 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
175 176
176 if (rw == WRITE) { 177 if (rw == WRITE) {
177 /* 178 /*
178 * FIXME: blockdev_direct_IO() doesn't use ->prepare_write(), 179 * FIXME: blockdev_direct_IO() doesn't use ->write_begin(),
179 * so we need to update the ->mmu_private to block boundary. 180 * so we need to update the ->mmu_private to block boundary.
180 * 181 *
181 * But we must fill the remaining area or hole by nul for 182 * But we must fill the remaining area or hole by nul for
@@ -198,7 +199,14 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb,
198 199
199static sector_t _fat_bmap(struct address_space *mapping, sector_t block) 200static sector_t _fat_bmap(struct address_space *mapping, sector_t block)
200{ 201{
201 return generic_block_bmap(mapping, block, fat_get_block); 202 sector_t blocknr;
203
204 /* fat_get_cluster() assumes the requested blocknr isn't truncated. */
205 mutex_lock(&mapping->host->i_mutex);
206 blocknr = generic_block_bmap(mapping, block, fat_get_block);
207 mutex_unlock(&mapping->host->i_mutex);
208
209 return blocknr;
202} 210}
203 211
204static const struct address_space_operations fat_aops = { 212static const struct address_space_operations fat_aops = {
@@ -247,25 +255,21 @@ static void fat_hash_init(struct super_block *sb)
247 INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); 255 INIT_HLIST_HEAD(&sbi->inode_hashtable[i]);
248} 256}
249 257
250static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos) 258static inline unsigned long fat_hash(loff_t i_pos)
251{ 259{
252 unsigned long tmp = (unsigned long)i_pos | (unsigned long) sb; 260 return hash_32(i_pos, FAT_HASH_BITS);
253 tmp = tmp + (tmp >> FAT_HASH_BITS) + (tmp >> FAT_HASH_BITS * 2);
254 return tmp & FAT_HASH_MASK;
255} 261}
256 262
257void fat_attach(struct inode *inode, loff_t i_pos) 263void fat_attach(struct inode *inode, loff_t i_pos)
258{ 264{
259 struct super_block *sb = inode->i_sb; 265 struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
260 struct msdos_sb_info *sbi = MSDOS_SB(sb); 266 struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
261 267
262 spin_lock(&sbi->inode_hash_lock); 268 spin_lock(&sbi->inode_hash_lock);
263 MSDOS_I(inode)->i_pos = i_pos; 269 MSDOS_I(inode)->i_pos = i_pos;
264 hlist_add_head(&MSDOS_I(inode)->i_fat_hash, 270 hlist_add_head(&MSDOS_I(inode)->i_fat_hash, head);
265 sbi->inode_hashtable + fat_hash(sb, i_pos));
266 spin_unlock(&sbi->inode_hash_lock); 271 spin_unlock(&sbi->inode_hash_lock);
267} 272}
268
269EXPORT_SYMBOL_GPL(fat_attach); 273EXPORT_SYMBOL_GPL(fat_attach);
270 274
271void fat_detach(struct inode *inode) 275void fat_detach(struct inode *inode)
@@ -276,13 +280,12 @@ void fat_detach(struct inode *inode)
276 hlist_del_init(&MSDOS_I(inode)->i_fat_hash); 280 hlist_del_init(&MSDOS_I(inode)->i_fat_hash);
277 spin_unlock(&sbi->inode_hash_lock); 281 spin_unlock(&sbi->inode_hash_lock);
278} 282}
279
280EXPORT_SYMBOL_GPL(fat_detach); 283EXPORT_SYMBOL_GPL(fat_detach);
281 284
282struct inode *fat_iget(struct super_block *sb, loff_t i_pos) 285struct inode *fat_iget(struct super_block *sb, loff_t i_pos)
283{ 286{
284 struct msdos_sb_info *sbi = MSDOS_SB(sb); 287 struct msdos_sb_info *sbi = MSDOS_SB(sb);
285 struct hlist_head *head = sbi->inode_hashtable + fat_hash(sb, i_pos); 288 struct hlist_head *head = sbi->inode_hashtable + fat_hash(i_pos);
286 struct hlist_node *_p; 289 struct hlist_node *_p;
287 struct msdos_inode_info *i; 290 struct msdos_inode_info *i;
288 struct inode *inode = NULL; 291 struct inode *inode = NULL;
@@ -341,8 +344,7 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
341 344
342 if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) { 345 if ((de->attr & ATTR_DIR) && !IS_FREE(de->name)) {
343 inode->i_generation &= ~1; 346 inode->i_generation &= ~1;
344 inode->i_mode = MSDOS_MKMODE(de->attr, 347 inode->i_mode = fat_make_mode(sbi, de->attr, S_IRWXUGO);
345 S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR;
346 inode->i_op = sbi->dir_ops; 348 inode->i_op = sbi->dir_ops;
347 inode->i_fop = &fat_dir_operations; 349 inode->i_fop = &fat_dir_operations;
348 350
@@ -359,10 +361,9 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
359 inode->i_nlink = fat_subdirs(inode); 361 inode->i_nlink = fat_subdirs(inode);
360 } else { /* not a directory */ 362 } else { /* not a directory */
361 inode->i_generation |= 1; 363 inode->i_generation |= 1;
362 inode->i_mode = MSDOS_MKMODE(de->attr, 364 inode->i_mode = fat_make_mode(sbi, de->attr,
363 ((sbi->options.showexec && !is_exec(de->name + 8)) 365 ((sbi->options.showexec && !is_exec(de->name + 8))
364 ? S_IRUGO|S_IWUGO : S_IRWXUGO) 366 ? S_IRUGO|S_IWUGO : S_IRWXUGO));
365 & ~sbi->options.fs_fmask) | S_IFREG;
366 MSDOS_I(inode)->i_start = le16_to_cpu(de->start); 367 MSDOS_I(inode)->i_start = le16_to_cpu(de->start);
367 if (sbi->fat_bits == 32) 368 if (sbi->fat_bits == 32)
368 MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16); 369 MSDOS_I(inode)->i_start |= (le16_to_cpu(de->starthi) << 16);
@@ -378,25 +379,16 @@ static int fat_fill_inode(struct inode *inode, struct msdos_dir_entry *de)
378 if (sbi->options.sys_immutable) 379 if (sbi->options.sys_immutable)
379 inode->i_flags |= S_IMMUTABLE; 380 inode->i_flags |= S_IMMUTABLE;
380 } 381 }
381 MSDOS_I(inode)->i_attrs = de->attr & ATTR_UNUSED; 382 fat_save_attrs(inode, de->attr);
383
382 inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1)) 384 inode->i_blocks = ((inode->i_size + (sbi->cluster_size - 1))
383 & ~((loff_t)sbi->cluster_size - 1)) >> 9; 385 & ~((loff_t)sbi->cluster_size - 1)) >> 9;
384 inode->i_mtime.tv_sec = 386
385 date_dos2unix(le16_to_cpu(de->time), le16_to_cpu(de->date), 387 fat_time_fat2unix(sbi, &inode->i_mtime, de->time, de->date, 0);
386 sbi->options.tz_utc);
387 inode->i_mtime.tv_nsec = 0;
388 if (sbi->options.isvfat) { 388 if (sbi->options.isvfat) {
389 int secs = de->ctime_cs / 100; 389 fat_time_fat2unix(sbi, &inode->i_ctime, de->ctime,
390 int csecs = de->ctime_cs % 100; 390 de->cdate, de->ctime_cs);
391 inode->i_ctime.tv_sec = 391 fat_time_fat2unix(sbi, &inode->i_atime, 0, de->adate, 0);
392 date_dos2unix(le16_to_cpu(de->ctime),
393 le16_to_cpu(de->cdate),
394 sbi->options.tz_utc) + secs;
395 inode->i_ctime.tv_nsec = csecs * 10000000;
396 inode->i_atime.tv_sec =
397 date_dos2unix(0, le16_to_cpu(de->adate),
398 sbi->options.tz_utc);
399 inode->i_atime.tv_nsec = 0;
400 } else 392 } else
401 inode->i_ctime = inode->i_atime = inode->i_mtime; 393 inode->i_ctime = inode->i_atime = inode->i_mtime;
402 394
@@ -443,13 +435,8 @@ static void fat_delete_inode(struct inode *inode)
443 435
444static void fat_clear_inode(struct inode *inode) 436static void fat_clear_inode(struct inode *inode)
445{ 437{
446 struct super_block *sb = inode->i_sb;
447 struct msdos_sb_info *sbi = MSDOS_SB(sb);
448
449 spin_lock(&sbi->inode_hash_lock);
450 fat_cache_inval_inode(inode); 438 fat_cache_inval_inode(inode);
451 hlist_del_init(&MSDOS_I(inode)->i_fat_hash); 439 fat_detach(inode);
452 spin_unlock(&sbi->inode_hash_lock);
453} 440}
454 441
455static void fat_write_super(struct super_block *sb) 442static void fat_write_super(struct super_block *sb)
@@ -555,6 +542,20 @@ static int fat_statfs(struct dentry *dentry, struct kstatfs *buf)
555 return 0; 542 return 0;
556} 543}
557 544
545static inline loff_t fat_i_pos_read(struct msdos_sb_info *sbi,
546 struct inode *inode)
547{
548 loff_t i_pos;
549#if BITS_PER_LONG == 32
550 spin_lock(&sbi->inode_hash_lock);
551#endif
552 i_pos = MSDOS_I(inode)->i_pos;
553#if BITS_PER_LONG == 32
554 spin_unlock(&sbi->inode_hash_lock);
555#endif
556 return i_pos;
557}
558
558static int fat_write_inode(struct inode *inode, int wait) 559static int fat_write_inode(struct inode *inode, int wait)
559{ 560{
560 struct super_block *sb = inode->i_sb; 561 struct super_block *sb = inode->i_sb;
@@ -564,9 +565,12 @@ static int fat_write_inode(struct inode *inode, int wait)
564 loff_t i_pos; 565 loff_t i_pos;
565 int err; 566 int err;
566 567
568 if (inode->i_ino == MSDOS_ROOT_INO)
569 return 0;
570
567retry: 571retry:
568 i_pos = MSDOS_I(inode)->i_pos; 572 i_pos = fat_i_pos_read(sbi, inode);
569 if (inode->i_ino == MSDOS_ROOT_INO || !i_pos) 573 if (!i_pos)
570 return 0; 574 return 0;
571 575
572 bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits); 576 bh = sb_bread(sb, i_pos >> sbi->dir_per_block_bits);
@@ -588,19 +592,17 @@ retry:
588 raw_entry->size = 0; 592 raw_entry->size = 0;
589 else 593 else
590 raw_entry->size = cpu_to_le32(inode->i_size); 594 raw_entry->size = cpu_to_le32(inode->i_size);
591 raw_entry->attr = fat_attr(inode); 595 raw_entry->attr = fat_make_attrs(inode);
592 raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart); 596 raw_entry->start = cpu_to_le16(MSDOS_I(inode)->i_logstart);
593 raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16); 597 raw_entry->starthi = cpu_to_le16(MSDOS_I(inode)->i_logstart >> 16);
594 fat_date_unix2dos(inode->i_mtime.tv_sec, &raw_entry->time, 598 fat_time_unix2fat(sbi, &inode->i_mtime, &raw_entry->time,
595 &raw_entry->date, sbi->options.tz_utc); 599 &raw_entry->date, NULL);
596 if (sbi->options.isvfat) { 600 if (sbi->options.isvfat) {
597 __le16 atime; 601 __le16 atime;
598 fat_date_unix2dos(inode->i_ctime.tv_sec, &raw_entry->ctime, 602 fat_time_unix2fat(sbi, &inode->i_ctime, &raw_entry->ctime,
599 &raw_entry->cdate, sbi->options.tz_utc); 603 &raw_entry->cdate, &raw_entry->ctime_cs);
600 fat_date_unix2dos(inode->i_atime.tv_sec, &atime, 604 fat_time_unix2fat(sbi, &inode->i_atime, &atime,
601 &raw_entry->adate, sbi->options.tz_utc); 605 &raw_entry->adate, NULL);
602 raw_entry->ctime_cs = (inode->i_ctime.tv_sec & 1) * 100 +
603 inode->i_ctime.tv_nsec / 10000000;
604 } 606 }
605 spin_unlock(&sbi->inode_hash_lock); 607 spin_unlock(&sbi->inode_hash_lock);
606 mark_buffer_dirty(bh); 608 mark_buffer_dirty(bh);
@@ -819,8 +821,10 @@ static int fat_show_options(struct seq_file *m, struct vfsmount *mnt)
819 seq_puts(m, ",uni_xlate"); 821 seq_puts(m, ",uni_xlate");
820 if (!opts->numtail) 822 if (!opts->numtail)
821 seq_puts(m, ",nonumtail"); 823 seq_puts(m, ",nonumtail");
824 if (opts->rodir)
825 seq_puts(m, ",rodir");
822 } 826 }
823 if (sbi->options.flush) 827 if (opts->flush)
824 seq_puts(m, ",flush"); 828 seq_puts(m, ",flush");
825 if (opts->tz_utc) 829 if (opts->tz_utc)
826 seq_puts(m, ",tz=UTC"); 830 seq_puts(m, ",tz=UTC");
@@ -836,7 +840,7 @@ enum {
836 Opt_charset, Opt_shortname_lower, Opt_shortname_win95, 840 Opt_charset, Opt_shortname_lower, Opt_shortname_win95,
837 Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes, 841 Opt_shortname_winnt, Opt_shortname_mixed, Opt_utf8_no, Opt_utf8_yes,
838 Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes, 842 Opt_uni_xl_no, Opt_uni_xl_yes, Opt_nonumtail_no, Opt_nonumtail_yes,
839 Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_err, 843 Opt_obsolate, Opt_flush, Opt_tz_utc, Opt_rodir, Opt_err,
840}; 844};
841 845
842static const match_table_t fat_tokens = { 846static const match_table_t fat_tokens = {
@@ -908,6 +912,7 @@ static const match_table_t vfat_tokens = {
908 {Opt_nonumtail_yes, "nonumtail=yes"}, 912 {Opt_nonumtail_yes, "nonumtail=yes"},
909 {Opt_nonumtail_yes, "nonumtail=true"}, 913 {Opt_nonumtail_yes, "nonumtail=true"},
910 {Opt_nonumtail_yes, "nonumtail"}, 914 {Opt_nonumtail_yes, "nonumtail"},
915 {Opt_rodir, "rodir"},
911 {Opt_err, NULL} 916 {Opt_err, NULL}
912}; 917};
913 918
@@ -927,10 +932,13 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
927 opts->allow_utime = -1; 932 opts->allow_utime = -1;
928 opts->codepage = fat_default_codepage; 933 opts->codepage = fat_default_codepage;
929 opts->iocharset = fat_default_iocharset; 934 opts->iocharset = fat_default_iocharset;
930 if (is_vfat) 935 if (is_vfat) {
931 opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95; 936 opts->shortname = VFAT_SFN_DISPLAY_LOWER|VFAT_SFN_CREATE_WIN95;
932 else 937 opts->rodir = 0;
938 } else {
933 opts->shortname = 0; 939 opts->shortname = 0;
940 opts->rodir = 1;
941 }
934 opts->name_check = 'n'; 942 opts->name_check = 'n';
935 opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0; 943 opts->quiet = opts->showexec = opts->sys_immutable = opts->dotsOK = 0;
936 opts->utf8 = opts->unicode_xlate = 0; 944 opts->utf8 = opts->unicode_xlate = 0;
@@ -1081,6 +1089,9 @@ static int parse_options(char *options, int is_vfat, int silent, int *debug,
1081 case Opt_nonumtail_yes: /* empty or 1 or yes or true */ 1089 case Opt_nonumtail_yes: /* empty or 1 or yes or true */
1082 opts->numtail = 0; /* negated option */ 1090 opts->numtail = 0; /* negated option */
1083 break; 1091 break;
1092 case Opt_rodir:
1093 opts->rodir = 1;
1094 break;
1084 1095
1085 /* obsolete mount options */ 1096 /* obsolete mount options */
1086 case Opt_obsolate: 1097 case Opt_obsolate:
@@ -1126,7 +1137,7 @@ static int fat_read_root(struct inode *inode)
1126 inode->i_gid = sbi->options.fs_gid; 1137 inode->i_gid = sbi->options.fs_gid;
1127 inode->i_version++; 1138 inode->i_version++;
1128 inode->i_generation = 0; 1139 inode->i_generation = 0;
1129 inode->i_mode = (S_IRWXUGO & ~sbi->options.fs_dmask) | S_IFDIR; 1140 inode->i_mode = fat_make_mode(sbi, ATTR_DIR, S_IRWXUGO);
1130 inode->i_op = sbi->dir_ops; 1141 inode->i_op = sbi->dir_ops;
1131 inode->i_fop = &fat_dir_operations; 1142 inode->i_fop = &fat_dir_operations;
1132 if (sbi->fat_bits == 32) { 1143 if (sbi->fat_bits == 32) {
@@ -1143,7 +1154,7 @@ static int fat_read_root(struct inode *inode)
1143 MSDOS_I(inode)->i_logstart = 0; 1154 MSDOS_I(inode)->i_logstart = 0;
1144 MSDOS_I(inode)->mmu_private = inode->i_size; 1155 MSDOS_I(inode)->mmu_private = inode->i_size;
1145 1156
1146 MSDOS_I(inode)->i_attrs = ATTR_NONE; 1157 fat_save_attrs(inode, ATTR_DIR);
1147 inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0; 1158 inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = 0;
1148 inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0; 1159 inode->i_mtime.tv_nsec = inode->i_atime.tv_nsec = inode->i_ctime.tv_nsec = 0;
1149 inode->i_nlink = fat_subdirs(inode)+2; 1160 inode->i_nlink = fat_subdirs(inode)+2;