diff options
Diffstat (limited to 'fs/fat')
-rw-r--r-- | fs/fat/Makefile | 6 | ||||
-rw-r--r-- | fs/fat/cache.c | 25 | ||||
-rw-r--r-- | fs/fat/dir.c | 20 | ||||
-rw-r--r-- | fs/fat/fat.h | 329 | ||||
-rw-r--r-- | fs/fat/fatent.c | 24 | ||||
-rw-r--r-- | fs/fat/file.c | 49 | ||||
-rw-r--r-- | fs/fat/inode.c | 131 | ||||
-rw-r--r-- | fs/fat/misc.c | 155 | ||||
-rw-r--r-- | fs/fat/namei_msdos.c | 706 | ||||
-rw-r--r-- | fs/fat/namei_vfat.c | 1098 |
10 files changed, 2395 insertions, 148 deletions
diff --git a/fs/fat/Makefile b/fs/fat/Makefile index bfb5f06cf2c8..e06190322c1c 100644 --- a/fs/fat/Makefile +++ b/fs/fat/Makefile | |||
@@ -3,5 +3,9 @@ | |||
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_FAT_FS) += fat.o | 5 | obj-$(CONFIG_FAT_FS) += fat.o |
6 | obj-$(CONFIG_VFAT_FS) += vfat.o | ||
7 | obj-$(CONFIG_MSDOS_FS) += msdos.o | ||
6 | 8 | ||
7 | fat-objs := cache.o dir.o fatent.o file.o inode.o misc.o | 9 | fat-y := cache.o dir.o fatent.o file.o inode.o misc.o |
10 | vfat-y := namei_vfat.o | ||
11 | msdos-y := namei_msdos.o | ||
diff --git a/fs/fat/cache.c b/fs/fat/cache.c index 3222f51c41cf..b42602298087 100644 --- a/fs/fat/cache.c +++ b/fs/fat/cache.c | |||
@@ -9,8 +9,8 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/fs.h> | 11 | #include <linux/fs.h> |
12 | #include <linux/msdos_fs.h> | ||
13 | #include <linux/buffer_head.h> | 12 | #include <linux/buffer_head.h> |
13 | #include "fat.h" | ||
14 | 14 | ||
15 | /* this must be > 0. */ | 15 | /* this must be > 0. */ |
16 | #define FAT_MAX_CACHE 8 | 16 | #define FAT_MAX_CACHE 8 |
@@ -293,10 +293,12 @@ static int fat_bmap_cluster(struct inode *inode, int cluster) | |||
293 | } | 293 | } |
294 | 294 | ||
295 | int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | 295 | int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, |
296 | unsigned long *mapped_blocks) | 296 | unsigned long *mapped_blocks, int create) |
297 | { | 297 | { |
298 | struct super_block *sb = inode->i_sb; | 298 | struct super_block *sb = inode->i_sb; |
299 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | 299 | struct msdos_sb_info *sbi = MSDOS_SB(sb); |
300 | const unsigned long blocksize = sb->s_blocksize; | ||
301 | const unsigned char blocksize_bits = sb->s_blocksize_bits; | ||
300 | sector_t last_block; | 302 | sector_t last_block; |
301 | int cluster, offset; | 303 | int cluster, offset; |
302 | 304 | ||
@@ -309,10 +311,21 @@ int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | |||
309 | } | 311 | } |
310 | return 0; | 312 | return 0; |
311 | } | 313 | } |
312 | last_block = (MSDOS_I(inode)->mmu_private + (sb->s_blocksize - 1)) | 314 | |
313 | >> sb->s_blocksize_bits; | 315 | last_block = (i_size_read(inode) + (blocksize - 1)) >> blocksize_bits; |
314 | if (sector >= last_block) | 316 | if (sector >= last_block) { |
315 | return 0; | 317 | if (!create) |
318 | return 0; | ||
319 | |||
320 | /* | ||
321 | * ->mmu_private can access on only allocation path. | ||
322 | * (caller must hold ->i_mutex) | ||
323 | */ | ||
324 | last_block = (MSDOS_I(inode)->mmu_private + (blocksize - 1)) | ||
325 | >> blocksize_bits; | ||
326 | if (sector >= last_block) | ||
327 | return 0; | ||
328 | } | ||
316 | 329 | ||
317 | cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); | 330 | cluster = sector >> (sbi->cluster_bits - sb->s_blocksize_bits); |
318 | offset = sector & (sbi->sec_per_clus - 1); | 331 | offset = sector & (sbi->sec_per_clus - 1); |
diff --git a/fs/fat/dir.c b/fs/fat/dir.c index bae1c3292522..67e058357098 100644 --- a/fs/fat/dir.c +++ b/fs/fat/dir.c | |||
@@ -16,11 +16,11 @@ | |||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/slab.h> | 17 | #include <linux/slab.h> |
18 | #include <linux/time.h> | 18 | #include <linux/time.h> |
19 | #include <linux/msdos_fs.h> | ||
20 | #include <linux/smp_lock.h> | 19 | #include <linux/smp_lock.h> |
21 | #include <linux/buffer_head.h> | 20 | #include <linux/buffer_head.h> |
22 | #include <linux/compat.h> | 21 | #include <linux/compat.h> |
23 | #include <asm/uaccess.h> | 22 | #include <asm/uaccess.h> |
23 | #include "fat.h" | ||
24 | 24 | ||
25 | static inline loff_t fat_make_i_pos(struct super_block *sb, | 25 | static inline loff_t fat_make_i_pos(struct super_block *sb, |
26 | struct buffer_head *bh, | 26 | struct buffer_head *bh, |
@@ -77,7 +77,7 @@ next: | |||
77 | 77 | ||
78 | *bh = NULL; | 78 | *bh = NULL; |
79 | iblock = *pos >> sb->s_blocksize_bits; | 79 | iblock = *pos >> sb->s_blocksize_bits; |
80 | err = fat_bmap(dir, iblock, &phys, &mapped_blocks); | 80 | err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0); |
81 | if (err || !phys) | 81 | if (err || !phys) |
82 | return -1; /* beyond EOF or error */ | 82 | return -1; /* beyond EOF or error */ |
83 | 83 | ||
@@ -86,7 +86,7 @@ next: | |||
86 | *bh = sb_bread(sb, phys); | 86 | *bh = sb_bread(sb, phys); |
87 | if (*bh == NULL) { | 87 | if (*bh == NULL) { |
88 | printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", | 88 | printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", |
89 | (unsigned long long)phys); | 89 | (llu)phys); |
90 | /* skip this block */ | 90 | /* skip this block */ |
91 | *pos = (iblock + 1) << sb->s_blocksize_bits; | 91 | *pos = (iblock + 1) << sb->s_blocksize_bits; |
92 | goto next; | 92 | goto next; |
@@ -373,9 +373,10 @@ parse_record: | |||
373 | if (de->attr == ATTR_EXT) { | 373 | if (de->attr == ATTR_EXT) { |
374 | int status = fat_parse_long(inode, &cpos, &bh, &de, | 374 | int status = fat_parse_long(inode, &cpos, &bh, &de, |
375 | &unicode, &nr_slots); | 375 | &unicode, &nr_slots); |
376 | if (status < 0) | 376 | if (status < 0) { |
377 | return status; | 377 | err = status; |
378 | else if (status == PARSE_INVALID) | 378 | goto end_of_dir; |
379 | } else if (status == PARSE_INVALID) | ||
379 | continue; | 380 | continue; |
380 | else if (status == PARSE_NOT_LONGNAME) | 381 | else if (status == PARSE_NOT_LONGNAME) |
381 | goto parse_record; | 382 | goto parse_record; |
@@ -832,6 +833,7 @@ static long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, | |||
832 | #endif /* CONFIG_COMPAT */ | 833 | #endif /* CONFIG_COMPAT */ |
833 | 834 | ||
834 | const struct file_operations fat_dir_operations = { | 835 | const struct file_operations fat_dir_operations = { |
836 | .llseek = generic_file_llseek, | ||
835 | .read = generic_read_dir, | 837 | .read = generic_read_dir, |
836 | .readdir = fat_readdir, | 838 | .readdir = fat_readdir, |
837 | .ioctl = fat_dir_ioctl, | 839 | .ioctl = fat_dir_ioctl, |
@@ -1089,6 +1091,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1089 | struct msdos_dir_entry *de; | 1091 | struct msdos_dir_entry *de; |
1090 | sector_t blknr; | 1092 | sector_t blknr; |
1091 | __le16 date, time; | 1093 | __le16 date, time; |
1094 | u8 time_cs; | ||
1092 | int err, cluster; | 1095 | int err, cluster; |
1093 | 1096 | ||
1094 | err = fat_alloc_clusters(dir, &cluster, 1); | 1097 | err = fat_alloc_clusters(dir, &cluster, 1); |
@@ -1102,7 +1105,7 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1102 | goto error_free; | 1105 | goto error_free; |
1103 | } | 1106 | } |
1104 | 1107 | ||
1105 | fat_date_unix2dos(ts->tv_sec, &time, &date, sbi->options.tz_utc); | 1108 | fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); |
1106 | 1109 | ||
1107 | de = (struct msdos_dir_entry *)bhs[0]->b_data; | 1110 | de = (struct msdos_dir_entry *)bhs[0]->b_data; |
1108 | /* filling the new directory slots ("." and ".." entries) */ | 1111 | /* filling the new directory slots ("." and ".." entries) */ |
@@ -1112,13 +1115,14 @@ int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | |||
1112 | de[0].lcase = de[1].lcase = 0; | 1115 | de[0].lcase = de[1].lcase = 0; |
1113 | de[0].time = de[1].time = time; | 1116 | de[0].time = de[1].time = time; |
1114 | de[0].date = de[1].date = date; | 1117 | de[0].date = de[1].date = date; |
1115 | de[0].ctime_cs = de[1].ctime_cs = 0; | ||
1116 | if (sbi->options.isvfat) { | 1118 | if (sbi->options.isvfat) { |
1117 | /* extra timestamps */ | 1119 | /* extra timestamps */ |
1118 | de[0].ctime = de[1].ctime = time; | 1120 | de[0].ctime = de[1].ctime = time; |
1121 | de[0].ctime_cs = de[1].ctime_cs = time_cs; | ||
1119 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; | 1122 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; |
1120 | } else { | 1123 | } else { |
1121 | de[0].ctime = de[1].ctime = 0; | 1124 | de[0].ctime = de[1].ctime = 0; |
1125 | de[0].ctime_cs = de[1].ctime_cs = 0; | ||
1122 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; | 1126 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; |
1123 | } | 1127 | } |
1124 | de[0].start = cpu_to_le16(cluster); | 1128 | de[0].start = cpu_to_le16(cluster); |
diff --git a/fs/fat/fat.h b/fs/fat/fat.h new file mode 100644 index 000000000000..ea440d65819c --- /dev/null +++ b/fs/fat/fat.h | |||
@@ -0,0 +1,329 @@ | |||
1 | #ifndef _FAT_H | ||
2 | #define _FAT_H | ||
3 | |||
4 | #include <linux/buffer_head.h> | ||
5 | #include <linux/string.h> | ||
6 | #include <linux/nls.h> | ||
7 | #include <linux/fs.h> | ||
8 | #include <linux/mutex.h> | ||
9 | #include <linux/msdos_fs.h> | ||
10 | |||
11 | /* | ||
12 | * vfat shortname flags | ||
13 | */ | ||
14 | #define VFAT_SFN_DISPLAY_LOWER 0x0001 /* convert to lowercase for display */ | ||
15 | #define VFAT_SFN_DISPLAY_WIN95 0x0002 /* emulate win95 rule for display */ | ||
16 | #define VFAT_SFN_DISPLAY_WINNT 0x0004 /* emulate winnt rule for display */ | ||
17 | #define VFAT_SFN_CREATE_WIN95 0x0100 /* emulate win95 rule for create */ | ||
18 | #define VFAT_SFN_CREATE_WINNT 0x0200 /* emulate winnt rule for create */ | ||
19 | |||
20 | struct fat_mount_options { | ||
21 | uid_t fs_uid; | ||
22 | gid_t fs_gid; | ||
23 | unsigned short fs_fmask; | ||
24 | unsigned short fs_dmask; | ||
25 | unsigned short codepage; /* Codepage for shortname conversions */ | ||
26 | char *iocharset; /* Charset used for filename input/display */ | ||
27 | unsigned short shortname; /* flags for shortname display/create rule */ | ||
28 | unsigned char name_check; /* r = relaxed, n = normal, s = strict */ | ||
29 | unsigned short allow_utime;/* permission for setting the [am]time */ | ||
30 | unsigned quiet:1, /* set = fake successful chmods and chowns */ | ||
31 | showexec:1, /* set = only set x bit for com/exe/bat */ | ||
32 | sys_immutable:1, /* set = system files are immutable */ | ||
33 | dotsOK:1, /* set = hidden and system files are named '.filename' */ | ||
34 | isvfat:1, /* 0=no vfat long filename support, 1=vfat support */ | ||
35 | utf8:1, /* Use of UTF-8 character set (Default) */ | ||
36 | unicode_xlate:1, /* create escape sequences for unhandled Unicode */ | ||
37 | numtail:1, /* Does first alias have a numeric '~1' type tail? */ | ||
38 | flush:1, /* write things quickly */ | ||
39 | nocase:1, /* Does this need case conversion? 0=need case conversion*/ | ||
40 | usefree:1, /* Use free_clusters for FAT32 */ | ||
41 | tz_utc:1, /* Filesystem timestamps are in UTC */ | ||
42 | rodir:1; /* allow ATTR_RO for directory */ | ||
43 | }; | ||
44 | |||
45 | #define FAT_HASH_BITS 8 | ||
46 | #define FAT_HASH_SIZE (1UL << FAT_HASH_BITS) | ||
47 | |||
48 | /* | ||
49 | * MS-DOS file system in-core superblock data | ||
50 | */ | ||
51 | struct msdos_sb_info { | ||
52 | unsigned short sec_per_clus; /* sectors/cluster */ | ||
53 | unsigned short cluster_bits; /* log2(cluster_size) */ | ||
54 | unsigned int cluster_size; /* cluster size */ | ||
55 | unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */ | ||
56 | unsigned short fat_start; | ||
57 | unsigned long fat_length; /* FAT start & length (sec.) */ | ||
58 | unsigned long dir_start; | ||
59 | unsigned short dir_entries; /* root dir start & entries */ | ||
60 | unsigned long data_start; /* first data sector */ | ||
61 | unsigned long max_cluster; /* maximum cluster number */ | ||
62 | unsigned long root_cluster; /* first cluster of the root directory */ | ||
63 | unsigned long fsinfo_sector; /* sector number of FAT32 fsinfo */ | ||
64 | struct mutex fat_lock; | ||
65 | unsigned int prev_free; /* previously allocated cluster number */ | ||
66 | unsigned int free_clusters; /* -1 if undefined */ | ||
67 | unsigned int free_clus_valid; /* is free_clusters valid? */ | ||
68 | struct fat_mount_options options; | ||
69 | struct nls_table *nls_disk; /* Codepage used on disk */ | ||
70 | struct nls_table *nls_io; /* Charset used for input and display */ | ||
71 | const void *dir_ops; /* Opaque; default directory operations */ | ||
72 | int dir_per_block; /* dir entries per block */ | ||
73 | int dir_per_block_bits; /* log2(dir_per_block) */ | ||
74 | |||
75 | int fatent_shift; | ||
76 | struct fatent_operations *fatent_ops; | ||
77 | |||
78 | spinlock_t inode_hash_lock; | ||
79 | struct hlist_head inode_hashtable[FAT_HASH_SIZE]; | ||
80 | }; | ||
81 | |||
82 | #define FAT_CACHE_VALID 0 /* special case for valid cache */ | ||
83 | |||
84 | /* | ||
85 | * MS-DOS file system inode data in memory | ||
86 | */ | ||
87 | struct msdos_inode_info { | ||
88 | spinlock_t cache_lru_lock; | ||
89 | struct list_head cache_lru; | ||
90 | int nr_caches; | ||
91 | /* for avoiding the race between fat_free() and fat_get_cluster() */ | ||
92 | unsigned int cache_valid_id; | ||
93 | |||
94 | /* NOTE: mmu_private is 64bits, so must hold ->i_mutex to access */ | ||
95 | loff_t mmu_private; /* physically allocated size */ | ||
96 | |||
97 | int i_start; /* first cluster or 0 */ | ||
98 | int i_logstart; /* logical first cluster */ | ||
99 | int i_attrs; /* unused attribute bits */ | ||
100 | loff_t i_pos; /* on-disk position of directory entry or 0 */ | ||
101 | struct hlist_node i_fat_hash; /* hash by i_location */ | ||
102 | struct inode vfs_inode; | ||
103 | }; | ||
104 | |||
105 | struct fat_slot_info { | ||
106 | loff_t i_pos; /* on-disk position of directory entry */ | ||
107 | loff_t slot_off; /* offset for slot or de start */ | ||
108 | int nr_slots; /* number of slots + 1(de) in filename */ | ||
109 | struct msdos_dir_entry *de; | ||
110 | struct buffer_head *bh; | ||
111 | }; | ||
112 | |||
113 | static inline struct msdos_sb_info *MSDOS_SB(struct super_block *sb) | ||
114 | { | ||
115 | return sb->s_fs_info; | ||
116 | } | ||
117 | |||
118 | static inline struct msdos_inode_info *MSDOS_I(struct inode *inode) | ||
119 | { | ||
120 | return container_of(inode, struct msdos_inode_info, vfs_inode); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * If ->i_mode can't hold S_IWUGO (i.e. ATTR_RO), we use ->i_attrs to | ||
125 | * save ATTR_RO instead of ->i_mode. | ||
126 | * | ||
127 | * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only | ||
128 | * bit, it's just used as flag for app. | ||
129 | */ | ||
130 | static inline int fat_mode_can_hold_ro(struct inode *inode) | ||
131 | { | ||
132 | struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb); | ||
133 | mode_t mask; | ||
134 | |||
135 | if (S_ISDIR(inode->i_mode)) { | ||
136 | if (!sbi->options.rodir) | ||
137 | return 0; | ||
138 | mask = ~sbi->options.fs_dmask; | ||
139 | } else | ||
140 | mask = ~sbi->options.fs_fmask; | ||
141 | |||
142 | if (!(mask & S_IWUGO)) | ||
143 | return 0; | ||
144 | return 1; | ||
145 | } | ||
146 | |||
147 | /* Convert attribute bits and a mask to the UNIX mode. */ | ||
148 | static inline mode_t fat_make_mode(struct msdos_sb_info *sbi, | ||
149 | u8 attrs, mode_t mode) | ||
150 | { | ||
151 | if (attrs & ATTR_RO && !((attrs & ATTR_DIR) && !sbi->options.rodir)) | ||
152 | mode &= ~S_IWUGO; | ||
153 | |||
154 | if (attrs & ATTR_DIR) | ||
155 | return (mode & ~sbi->options.fs_dmask) | S_IFDIR; | ||
156 | else | ||
157 | return (mode & ~sbi->options.fs_fmask) | S_IFREG; | ||
158 | } | ||
159 | |||
160 | /* Return the FAT attribute byte for this inode */ | ||
161 | static inline u8 fat_make_attrs(struct inode *inode) | ||
162 | { | ||
163 | u8 attrs = MSDOS_I(inode)->i_attrs; | ||
164 | if (S_ISDIR(inode->i_mode)) | ||
165 | attrs |= ATTR_DIR; | ||
166 | if (fat_mode_can_hold_ro(inode) && !(inode->i_mode & S_IWUGO)) | ||
167 | attrs |= ATTR_RO; | ||
168 | return attrs; | ||
169 | } | ||
170 | |||
171 | static inline void fat_save_attrs(struct inode *inode, u8 attrs) | ||
172 | { | ||
173 | if (fat_mode_can_hold_ro(inode)) | ||
174 | MSDOS_I(inode)->i_attrs = attrs & ATTR_UNUSED; | ||
175 | else | ||
176 | MSDOS_I(inode)->i_attrs = attrs & (ATTR_UNUSED | ATTR_RO); | ||
177 | } | ||
178 | |||
179 | static inline unsigned char fat_checksum(const __u8 *name) | ||
180 | { | ||
181 | unsigned char s = name[0]; | ||
182 | s = (s<<7) + (s>>1) + name[1]; s = (s<<7) + (s>>1) + name[2]; | ||
183 | s = (s<<7) + (s>>1) + name[3]; s = (s<<7) + (s>>1) + name[4]; | ||
184 | s = (s<<7) + (s>>1) + name[5]; s = (s<<7) + (s>>1) + name[6]; | ||
185 | s = (s<<7) + (s>>1) + name[7]; s = (s<<7) + (s>>1) + name[8]; | ||
186 | s = (s<<7) + (s>>1) + name[9]; s = (s<<7) + (s>>1) + name[10]; | ||
187 | return s; | ||
188 | } | ||
189 | |||
190 | static inline sector_t fat_clus_to_blknr(struct msdos_sb_info *sbi, int clus) | ||
191 | { | ||
192 | return ((sector_t)clus - FAT_START_ENT) * sbi->sec_per_clus | ||
193 | + sbi->data_start; | ||
194 | } | ||
195 | |||
196 | static inline void fat16_towchar(wchar_t *dst, const __u8 *src, size_t len) | ||
197 | { | ||
198 | #ifdef __BIG_ENDIAN | ||
199 | while (len--) { | ||
200 | *dst++ = src[0] | (src[1] << 8); | ||
201 | src += 2; | ||
202 | } | ||
203 | #else | ||
204 | memcpy(dst, src, len * 2); | ||
205 | #endif | ||
206 | } | ||
207 | |||
208 | static inline void fatwchar_to16(__u8 *dst, const wchar_t *src, size_t len) | ||
209 | { | ||
210 | #ifdef __BIG_ENDIAN | ||
211 | while (len--) { | ||
212 | dst[0] = *src & 0x00FF; | ||
213 | dst[1] = (*src & 0xFF00) >> 8; | ||
214 | dst += 2; | ||
215 | src++; | ||
216 | } | ||
217 | #else | ||
218 | memcpy(dst, src, len * 2); | ||
219 | #endif | ||
220 | } | ||
221 | |||
222 | /* fat/cache.c */ | ||
223 | extern void fat_cache_inval_inode(struct inode *inode); | ||
224 | extern int fat_get_cluster(struct inode *inode, int cluster, | ||
225 | int *fclus, int *dclus); | ||
226 | extern int fat_bmap(struct inode *inode, sector_t sector, sector_t *phys, | ||
227 | unsigned long *mapped_blocks, int create); | ||
228 | |||
229 | /* fat/dir.c */ | ||
230 | extern const struct file_operations fat_dir_operations; | ||
231 | extern int fat_search_long(struct inode *inode, const unsigned char *name, | ||
232 | int name_len, struct fat_slot_info *sinfo); | ||
233 | extern int fat_dir_empty(struct inode *dir); | ||
234 | extern int fat_subdirs(struct inode *dir); | ||
235 | extern int fat_scan(struct inode *dir, const unsigned char *name, | ||
236 | struct fat_slot_info *sinfo); | ||
237 | extern int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, | ||
238 | struct msdos_dir_entry **de, loff_t *i_pos); | ||
239 | extern int fat_alloc_new_dir(struct inode *dir, struct timespec *ts); | ||
240 | extern int fat_add_entries(struct inode *dir, void *slots, int nr_slots, | ||
241 | struct fat_slot_info *sinfo); | ||
242 | extern int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo); | ||
243 | |||
244 | /* fat/fatent.c */ | ||
245 | struct fat_entry { | ||
246 | int entry; | ||
247 | union { | ||
248 | u8 *ent12_p[2]; | ||
249 | __le16 *ent16_p; | ||
250 | __le32 *ent32_p; | ||
251 | } u; | ||
252 | int nr_bhs; | ||
253 | struct buffer_head *bhs[2]; | ||
254 | }; | ||
255 | |||
256 | static inline void fatent_init(struct fat_entry *fatent) | ||
257 | { | ||
258 | fatent->nr_bhs = 0; | ||
259 | fatent->entry = 0; | ||
260 | fatent->u.ent32_p = NULL; | ||
261 | fatent->bhs[0] = fatent->bhs[1] = NULL; | ||
262 | } | ||
263 | |||
264 | static inline void fatent_set_entry(struct fat_entry *fatent, int entry) | ||
265 | { | ||
266 | fatent->entry = entry; | ||
267 | fatent->u.ent32_p = NULL; | ||
268 | } | ||
269 | |||
270 | static inline void fatent_brelse(struct fat_entry *fatent) | ||
271 | { | ||
272 | int i; | ||
273 | fatent->u.ent32_p = NULL; | ||
274 | for (i = 0; i < fatent->nr_bhs; i++) | ||
275 | brelse(fatent->bhs[i]); | ||
276 | fatent->nr_bhs = 0; | ||
277 | fatent->bhs[0] = fatent->bhs[1] = NULL; | ||
278 | } | ||
279 | |||
280 | extern void fat_ent_access_init(struct super_block *sb); | ||
281 | extern int fat_ent_read(struct inode *inode, struct fat_entry *fatent, | ||
282 | int entry); | ||
283 | extern int fat_ent_write(struct inode *inode, struct fat_entry *fatent, | ||
284 | int new, int wait); | ||
285 | extern int fat_alloc_clusters(struct inode *inode, int *cluster, | ||
286 | int nr_cluster); | ||
287 | extern int fat_free_clusters(struct inode *inode, int cluster); | ||
288 | extern int fat_count_free_clusters(struct super_block *sb); | ||
289 | |||
290 | /* fat/file.c */ | ||
291 | extern int fat_generic_ioctl(struct inode *inode, struct file *filp, | ||
292 | unsigned int cmd, unsigned long arg); | ||
293 | extern const struct file_operations fat_file_operations; | ||
294 | extern const struct inode_operations fat_file_inode_operations; | ||
295 | extern int fat_setattr(struct dentry * dentry, struct iattr * attr); | ||
296 | extern void fat_truncate(struct inode *inode); | ||
297 | extern int fat_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
298 | struct kstat *stat); | ||
299 | |||
300 | /* fat/inode.c */ | ||
301 | extern void fat_attach(struct inode *inode, loff_t i_pos); | ||
302 | extern void fat_detach(struct inode *inode); | ||
303 | extern struct inode *fat_iget(struct super_block *sb, loff_t i_pos); | ||
304 | extern struct inode *fat_build_inode(struct super_block *sb, | ||
305 | struct msdos_dir_entry *de, loff_t i_pos); | ||
306 | extern int fat_sync_inode(struct inode *inode); | ||
307 | extern int fat_fill_super(struct super_block *sb, void *data, int silent, | ||
308 | const struct inode_operations *fs_dir_inode_ops, int isvfat); | ||
309 | |||
310 | extern int fat_flush_inodes(struct super_block *sb, struct inode *i1, | ||
311 | struct inode *i2); | ||
312 | /* fat/misc.c */ | ||
313 | extern void fat_fs_panic(struct super_block *s, const char *fmt, ...) | ||
314 | __attribute__ ((format (printf, 2, 3))) __cold; | ||
315 | extern void fat_clusters_flush(struct super_block *sb); | ||
316 | extern int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster); | ||
317 | extern void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, | ||
318 | __le16 __time, __le16 __date, u8 time_cs); | ||
319 | extern void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, | ||
320 | __le16 *time, __le16 *date, u8 *time_cs); | ||
321 | extern int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs); | ||
322 | |||
323 | int fat_cache_init(void); | ||
324 | void fat_cache_destroy(void); | ||
325 | |||
326 | /* helper for printk */ | ||
327 | typedef unsigned long long llu; | ||
328 | |||
329 | #endif /* !_FAT_H */ | ||
diff --git a/fs/fat/fatent.c b/fs/fat/fatent.c index fb98b3d847ed..da6eea47872f 100644 --- a/fs/fat/fatent.c +++ b/fs/fat/fatent.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include <linux/fs.h> | 7 | #include <linux/fs.h> |
8 | #include <linux/msdos_fs.h> | 8 | #include <linux/msdos_fs.h> |
9 | #include <linux/blkdev.h> | 9 | #include <linux/blkdev.h> |
10 | #include "fat.h" | ||
10 | 11 | ||
11 | struct fatent_operations { | 12 | struct fatent_operations { |
12 | void (*ent_blocknr)(struct super_block *, int, int *, sector_t *); | 13 | void (*ent_blocknr)(struct super_block *, int, int *, sector_t *); |
@@ -92,8 +93,7 @@ static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent, | |||
92 | err_brelse: | 93 | err_brelse: |
93 | brelse(bhs[0]); | 94 | brelse(bhs[0]); |
94 | err: | 95 | err: |
95 | printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", | 96 | printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", (llu)blocknr); |
96 | (unsigned long long)blocknr); | ||
97 | return -EIO; | 97 | return -EIO; |
98 | } | 98 | } |
99 | 99 | ||
@@ -106,7 +106,7 @@ static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent, | |||
106 | fatent->bhs[0] = sb_bread(sb, blocknr); | 106 | fatent->bhs[0] = sb_bread(sb, blocknr); |
107 | if (!fatent->bhs[0]) { | 107 | if (!fatent->bhs[0]) { |
108 | printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", | 108 | printk(KERN_ERR "FAT: FAT read failed (blocknr %llu)\n", |
109 | (unsigned long long)blocknr); | 109 | (llu)blocknr); |
110 | return -EIO; | 110 | return -EIO; |
111 | } | 111 | } |
112 | fatent->nr_bhs = 1; | 112 | fatent->nr_bhs = 1; |
@@ -316,10 +316,20 @@ static inline int fat_ent_update_ptr(struct super_block *sb, | |||
316 | /* Is this fatent's blocks including this entry? */ | 316 | /* Is this fatent's blocks including this entry? */ |
317 | if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr) | 317 | if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr) |
318 | return 0; | 318 | return 0; |
319 | /* Does this entry need the next block? */ | 319 | if (sbi->fat_bits == 12) { |
320 | if (sbi->fat_bits == 12 && (offset + 1) >= sb->s_blocksize) { | 320 | if ((offset + 1) < sb->s_blocksize) { |
321 | if (fatent->nr_bhs != 2 || bhs[1]->b_blocknr != (blocknr + 1)) | 321 | /* This entry is on bhs[0]. */ |
322 | return 0; | 322 | if (fatent->nr_bhs == 2) { |
323 | brelse(bhs[1]); | ||
324 | fatent->nr_bhs = 1; | ||
325 | } | ||
326 | } else { | ||
327 | /* This entry needs the next block. */ | ||
328 | if (fatent->nr_bhs != 2) | ||
329 | return 0; | ||
330 | if (bhs[1]->b_blocknr != (blocknr + 1)) | ||
331 | return 0; | ||
332 | } | ||
323 | } | 333 | } |
324 | ops->ent_set_ptr(fatent, offset); | 334 | ops->ent_set_ptr(fatent, offset); |
325 | return 1; | 335 | return 1; |
diff --git a/fs/fat/file.c b/fs/fat/file.c index ddde37025ca6..f06a4e525ece 100644 --- a/fs/fat/file.c +++ b/fs/fat/file.c | |||
@@ -10,13 +10,13 @@ | |||
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/mount.h> | 11 | #include <linux/mount.h> |
12 | #include <linux/time.h> | 12 | #include <linux/time.h> |
13 | #include <linux/msdos_fs.h> | ||
14 | #include <linux/buffer_head.h> | 13 | #include <linux/buffer_head.h> |
15 | #include <linux/writeback.h> | 14 | #include <linux/writeback.h> |
16 | #include <linux/backing-dev.h> | 15 | #include <linux/backing-dev.h> |
17 | #include <linux/blkdev.h> | 16 | #include <linux/blkdev.h> |
18 | #include <linux/fsnotify.h> | 17 | #include <linux/fsnotify.h> |
19 | #include <linux/security.h> | 18 | #include <linux/security.h> |
19 | #include "fat.h" | ||
20 | 20 | ||
21 | int fat_generic_ioctl(struct inode *inode, struct file *filp, | 21 | int fat_generic_ioctl(struct inode *inode, struct file *filp, |
22 | unsigned int cmd, unsigned long arg) | 22 | unsigned int cmd, unsigned long arg) |
@@ -29,10 +29,9 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp, | |||
29 | { | 29 | { |
30 | u32 attr; | 30 | u32 attr; |
31 | 31 | ||
32 | if (inode->i_ino == MSDOS_ROOT_INO) | 32 | mutex_lock(&inode->i_mutex); |
33 | attr = ATTR_DIR; | 33 | attr = fat_make_attrs(inode); |
34 | else | 34 | mutex_unlock(&inode->i_mutex); |
35 | attr = fat_attr(inode); | ||
36 | 35 | ||
37 | return put_user(attr, user_attr); | 36 | return put_user(attr, user_attr); |
38 | } | 37 | } |
@@ -62,20 +61,16 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp, | |||
62 | /* Merge in ATTR_VOLUME and ATTR_DIR */ | 61 | /* Merge in ATTR_VOLUME and ATTR_DIR */ |
63 | attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | | 62 | attr |= (MSDOS_I(inode)->i_attrs & ATTR_VOLUME) | |
64 | (is_dir ? ATTR_DIR : 0); | 63 | (is_dir ? ATTR_DIR : 0); |
65 | oldattr = fat_attr(inode); | 64 | oldattr = fat_make_attrs(inode); |
66 | 65 | ||
67 | /* Equivalent to a chmod() */ | 66 | /* Equivalent to a chmod() */ |
68 | ia.ia_valid = ATTR_MODE | ATTR_CTIME; | 67 | ia.ia_valid = ATTR_MODE | ATTR_CTIME; |
69 | ia.ia_ctime = current_fs_time(inode->i_sb); | 68 | ia.ia_ctime = current_fs_time(inode->i_sb); |
70 | if (is_dir) { | 69 | if (is_dir) |
71 | ia.ia_mode = MSDOS_MKMODE(attr, | 70 | ia.ia_mode = fat_make_mode(sbi, attr, S_IRWXUGO); |
72 | S_IRWXUGO & ~sbi->options.fs_dmask) | 71 | else { |
73 | | S_IFDIR; | 72 | ia.ia_mode = fat_make_mode(sbi, attr, |
74 | } else { | 73 | S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)); |
75 | ia.ia_mode = MSDOS_MKMODE(attr, | ||
76 | (S_IRUGO | S_IWUGO | (inode->i_mode & S_IXUGO)) | ||
77 | & ~sbi->options.fs_fmask) | ||
78 | | S_IFREG; | ||
79 | } | 74 | } |
80 | 75 | ||
81 | /* The root directory has no attributes */ | 76 | /* The root directory has no attributes */ |
@@ -115,7 +110,7 @@ int fat_generic_ioctl(struct inode *inode, struct file *filp, | |||
115 | inode->i_flags &= S_IMMUTABLE; | 110 | inode->i_flags &= S_IMMUTABLE; |
116 | } | 111 | } |
117 | 112 | ||
118 | MSDOS_I(inode)->i_attrs = attr & ATTR_UNUSED; | 113 | fat_save_attrs(inode, attr); |
119 | mark_inode_dirty(inode); | 114 | mark_inode_dirty(inode); |
120 | up: | 115 | up: |
121 | mnt_drop_write(filp->f_path.mnt); | 116 | mnt_drop_write(filp->f_path.mnt); |
@@ -274,7 +269,7 @@ static int fat_sanitize_mode(const struct msdos_sb_info *sbi, | |||
274 | 269 | ||
275 | /* | 270 | /* |
276 | * Note, the basic check is already done by a caller of | 271 | * Note, the basic check is already done by a caller of |
277 | * (attr->ia_mode & ~MSDOS_VALID_MODE) | 272 | * (attr->ia_mode & ~FAT_VALID_MODE) |
278 | */ | 273 | */ |
279 | 274 | ||
280 | if (S_ISREG(inode->i_mode)) | 275 | if (S_ISREG(inode->i_mode)) |
@@ -287,11 +282,18 @@ static int fat_sanitize_mode(const struct msdos_sb_info *sbi, | |||
287 | /* | 282 | /* |
288 | * 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 |
289 | * 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. | ||
290 | */ | 287 | */ |
291 | 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))) |
292 | return -EPERM; | 289 | return -EPERM; |
293 | if ((perm & S_IWUGO) && ((perm & S_IWUGO) != (S_IWUGO & ~mask))) | 290 | if (fat_mode_can_hold_ro(inode)) { |
294 | 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 | } | ||
295 | 297 | ||
296 | *mode_ptr &= S_IFMT | perm; | 298 | *mode_ptr &= S_IFMT | perm; |
297 | 299 | ||
@@ -314,13 +316,15 @@ static int fat_allow_set_time(struct msdos_sb_info *sbi, struct inode *inode) | |||
314 | } | 316 | } |
315 | 317 | ||
316 | #define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) | 318 | #define TIMES_SET_FLAGS (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET) |
319 | /* valid file mode bits */ | ||
320 | #define FAT_VALID_MODE (S_IFREG | S_IFDIR | S_IRWXUGO) | ||
317 | 321 | ||
318 | int fat_setattr(struct dentry *dentry, struct iattr *attr) | 322 | int fat_setattr(struct dentry *dentry, struct iattr *attr) |
319 | { | 323 | { |
320 | struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); | 324 | struct msdos_sb_info *sbi = MSDOS_SB(dentry->d_sb); |
321 | struct inode *inode = dentry->d_inode; | 325 | struct inode *inode = dentry->d_inode; |
322 | int error = 0; | ||
323 | unsigned int ia_valid; | 326 | unsigned int ia_valid; |
327 | int error; | ||
324 | 328 | ||
325 | /* | 329 | /* |
326 | * Expand the file. Since inode_setattr() updates ->i_size | 330 | * Expand the file. Since inode_setattr() updates ->i_size |
@@ -356,7 +360,7 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) | |||
356 | ((attr->ia_valid & ATTR_GID) && | 360 | ((attr->ia_valid & ATTR_GID) && |
357 | (attr->ia_gid != sbi->options.fs_gid)) || | 361 | (attr->ia_gid != sbi->options.fs_gid)) || |
358 | ((attr->ia_valid & ATTR_MODE) && | 362 | ((attr->ia_valid & ATTR_MODE) && |
359 | (attr->ia_mode & ~MSDOS_VALID_MODE))) | 363 | (attr->ia_mode & ~FAT_VALID_MODE))) |
360 | error = -EPERM; | 364 | error = -EPERM; |
361 | 365 | ||
362 | if (error) { | 366 | if (error) { |
@@ -374,7 +378,8 @@ int fat_setattr(struct dentry *dentry, struct iattr *attr) | |||
374 | attr->ia_valid &= ~ATTR_MODE; | 378 | attr->ia_valid &= ~ATTR_MODE; |
375 | } | 379 | } |
376 | 380 | ||
377 | error = inode_setattr(inode, attr); | 381 | if (attr->ia_valid) |
382 | error = inode_setattr(inode, attr); | ||
378 | out: | 383 | out: |
379 | return error; | 384 | return error; |
380 | } | 385 | } |
diff --git a/fs/fat/inode.c b/fs/fat/inode.c index 2b2eec1283bf..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 | ||
@@ -198,7 +199,14 @@ static ssize_t fat_direct_IO(int rw, struct kiocb *iocb, | |||
198 | 199 | ||
199 | static sector_t _fat_bmap(struct address_space *mapping, sector_t block) | 200 | static 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 | ||
204 | static const struct address_space_operations fat_aops = { | 212 | static 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 | ||
250 | static inline unsigned long fat_hash(struct super_block *sb, loff_t i_pos) | 258 | static 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 | ||
257 | void fat_attach(struct inode *inode, loff_t i_pos) | 263 | void 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 | |||
269 | EXPORT_SYMBOL_GPL(fat_attach); | 273 | EXPORT_SYMBOL_GPL(fat_attach); |
270 | 274 | ||
271 | void fat_detach(struct inode *inode) | 275 | void 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 | |||
280 | EXPORT_SYMBOL_GPL(fat_detach); | 283 | EXPORT_SYMBOL_GPL(fat_detach); |
281 | 284 | ||
282 | struct inode *fat_iget(struct super_block *sb, loff_t i_pos) | 285 | struct 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 | ||
444 | static void fat_clear_inode(struct inode *inode) | 436 | static 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 | ||
455 | static void fat_write_super(struct super_block *sb) | 442 | static 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 | ||
545 | static 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 | |||
558 | static int fat_write_inode(struct inode *inode, int wait) | 559 | static 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 | |||
567 | retry: | 571 | retry: |
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 | ||
842 | static const match_table_t fat_tokens = { | 846 | static 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; |
diff --git a/fs/fat/misc.c b/fs/fat/misc.c index 79fb98ad36d4..ac39ebcc1496 100644 --- a/fs/fat/misc.c +++ b/fs/fat/misc.c | |||
@@ -8,8 +8,8 @@ | |||
8 | 8 | ||
9 | #include <linux/module.h> | 9 | #include <linux/module.h> |
10 | #include <linux/fs.h> | 10 | #include <linux/fs.h> |
11 | #include <linux/msdos_fs.h> | ||
12 | #include <linux/buffer_head.h> | 11 | #include <linux/buffer_head.h> |
12 | #include "fat.h" | ||
13 | 13 | ||
14 | /* | 14 | /* |
15 | * fat_fs_panic reports a severe file system problem and sets the file system | 15 | * fat_fs_panic reports a severe file system problem and sets the file system |
@@ -124,8 +124,9 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) | |||
124 | mark_inode_dirty(inode); | 124 | mark_inode_dirty(inode); |
125 | } | 125 | } |
126 | if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) { | 126 | if (new_fclus != (inode->i_blocks >> (sbi->cluster_bits - 9))) { |
127 | fat_fs_panic(sb, "clusters badly computed (%d != %lu)", | 127 | fat_fs_panic(sb, "clusters badly computed (%d != %llu)", |
128 | new_fclus, inode->i_blocks >> (sbi->cluster_bits - 9)); | 128 | new_fclus, |
129 | (llu)(inode->i_blocks >> (sbi->cluster_bits - 9))); | ||
129 | fat_cache_inval_inode(inode); | 130 | fat_cache_inval_inode(inode); |
130 | } | 131 | } |
131 | inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9); | 132 | inode->i_blocks += nr_cluster << (sbi->cluster_bits - 9); |
@@ -135,65 +136,131 @@ int fat_chain_add(struct inode *inode, int new_dclus, int nr_cluster) | |||
135 | 136 | ||
136 | extern struct timezone sys_tz; | 137 | extern struct timezone sys_tz; |
137 | 138 | ||
139 | /* | ||
140 | * The epoch of FAT timestamp is 1980. | ||
141 | * : bits : value | ||
142 | * date: 0 - 4: day (1 - 31) | ||
143 | * date: 5 - 8: month (1 - 12) | ||
144 | * date: 9 - 15: year (0 - 127) from 1980 | ||
145 | * time: 0 - 4: sec (0 - 29) 2sec counts | ||
146 | * time: 5 - 10: min (0 - 59) | ||
147 | * time: 11 - 15: hour (0 - 23) | ||
148 | */ | ||
149 | #define SECS_PER_MIN 60 | ||
150 | #define SECS_PER_HOUR (60 * 60) | ||
151 | #define SECS_PER_DAY (SECS_PER_HOUR * 24) | ||
152 | #define UNIX_SECS_1980 315532800L | ||
153 | #if BITS_PER_LONG == 64 | ||
154 | #define UNIX_SECS_2108 4354819200L | ||
155 | #endif | ||
156 | /* days between 1.1.70 and 1.1.80 (2 leap days) */ | ||
157 | #define DAYS_DELTA (365 * 10 + 2) | ||
158 | /* 120 (2100 - 1980) isn't leap year */ | ||
159 | #define YEAR_2100 120 | ||
160 | #define IS_LEAP_YEAR(y) (!((y) & 3) && (y) != YEAR_2100) | ||
161 | |||
138 | /* Linear day numbers of the respective 1sts in non-leap years. */ | 162 | /* Linear day numbers of the respective 1sts in non-leap years. */ |
139 | static int day_n[] = { | 163 | static time_t days_in_year[] = { |
140 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ | 164 | /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ |
141 | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 | 165 | 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, |
142 | }; | 166 | }; |
143 | 167 | ||
144 | /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ | 168 | /* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ |
145 | int date_dos2unix(unsigned short time, unsigned short date, int tz_utc) | 169 | void fat_time_fat2unix(struct msdos_sb_info *sbi, struct timespec *ts, |
170 | __le16 __time, __le16 __date, u8 time_cs) | ||
146 | { | 171 | { |
147 | int month, year, secs; | 172 | u16 time = le16_to_cpu(__time), date = le16_to_cpu(__date); |
173 | time_t second, day, leap_day, month, year; | ||
148 | 174 | ||
149 | /* | 175 | year = date >> 9; |
150 | * first subtract and mask after that... Otherwise, if | 176 | month = max(1, (date >> 5) & 0xf); |
151 | * date == 0, bad things happen | 177 | day = max(1, date & 0x1f) - 1; |
152 | */ | 178 | |
153 | month = ((date >> 5) - 1) & 15; | 179 | leap_day = (year + 3) / 4; |
154 | year = date >> 9; | 180 | if (year > YEAR_2100) /* 2100 isn't leap year */ |
155 | secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* | 181 | leap_day--; |
156 | ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && | 182 | if (IS_LEAP_YEAR(year) && month > 2) |
157 | month < 2 ? 1 : 0)+3653); | 183 | leap_day++; |
158 | /* days since 1.1.70 plus 80's leap day */ | 184 | |
159 | if (!tz_utc) | 185 | second = (time & 0x1f) << 1; |
160 | secs += sys_tz.tz_minuteswest*60; | 186 | second += ((time >> 5) & 0x3f) * SECS_PER_MIN; |
161 | return secs; | 187 | second += (time >> 11) * SECS_PER_HOUR; |
188 | second += (year * 365 + leap_day | ||
189 | + days_in_year[month] + day | ||
190 | + DAYS_DELTA) * SECS_PER_DAY; | ||
191 | |||
192 | if (!sbi->options.tz_utc) | ||
193 | second += sys_tz.tz_minuteswest * SECS_PER_MIN; | ||
194 | |||
195 | if (time_cs) { | ||
196 | ts->tv_sec = second + (time_cs / 100); | ||
197 | ts->tv_nsec = (time_cs % 100) * 10000000; | ||
198 | } else { | ||
199 | ts->tv_sec = second; | ||
200 | ts->tv_nsec = 0; | ||
201 | } | ||
162 | } | 202 | } |
163 | 203 | ||
164 | /* Convert linear UNIX date to a MS-DOS time/date pair. */ | 204 | /* Convert linear UNIX date to a FAT time/date pair. */ |
165 | void fat_date_unix2dos(int unix_date, __le16 *time, __le16 *date, int tz_utc) | 205 | void fat_time_unix2fat(struct msdos_sb_info *sbi, struct timespec *ts, |
206 | __le16 *time, __le16 *date, u8 *time_cs) | ||
166 | { | 207 | { |
167 | int day, year, nl_day, month; | 208 | time_t second = ts->tv_sec; |
209 | time_t day, leap_day, month, year; | ||
168 | 210 | ||
169 | if (!tz_utc) | 211 | if (!sbi->options.tz_utc) |
170 | unix_date -= sys_tz.tz_minuteswest*60; | 212 | second -= sys_tz.tz_minuteswest * SECS_PER_MIN; |
171 | 213 | ||
172 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ | 214 | /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ |
173 | if (unix_date < 315532800) | 215 | if (second < UNIX_SECS_1980) { |
174 | unix_date = 315532800; | 216 | *time = 0; |
175 | 217 | *date = cpu_to_le16((0 << 9) | (1 << 5) | 1); | |
176 | *time = cpu_to_le16((unix_date % 60)/2+(((unix_date/60) % 60) << 5)+ | 218 | if (time_cs) |
177 | (((unix_date/3600) % 24) << 11)); | 219 | *time_cs = 0; |
178 | day = unix_date/86400-3652; | 220 | return; |
179 | year = day/365; | 221 | } |
180 | if ((year+3)/4+365*year > day) | 222 | #if BITS_PER_LONG == 64 |
223 | if (second >= UNIX_SECS_2108) { | ||
224 | *time = cpu_to_le16((23 << 11) | (59 << 5) | 29); | ||
225 | *date = cpu_to_le16((127 << 9) | (12 << 5) | 31); | ||
226 | if (time_cs) | ||
227 | *time_cs = 199; | ||
228 | return; | ||
229 | } | ||
230 | #endif | ||
231 | |||
232 | day = second / SECS_PER_DAY - DAYS_DELTA; | ||
233 | year = day / 365; | ||
234 | leap_day = (year + 3) / 4; | ||
235 | if (year > YEAR_2100) /* 2100 isn't leap year */ | ||
236 | leap_day--; | ||
237 | if (year * 365 + leap_day > day) | ||
181 | year--; | 238 | year--; |
182 | day -= (year+3)/4+365*year; | 239 | leap_day = (year + 3) / 4; |
183 | if (day == 59 && !(year & 3)) { | 240 | if (year > YEAR_2100) /* 2100 isn't leap year */ |
184 | nl_day = day; | 241 | leap_day--; |
242 | day -= year * 365 + leap_day; | ||
243 | |||
244 | if (IS_LEAP_YEAR(year) && day == days_in_year[3]) { | ||
185 | month = 2; | 245 | month = 2; |
186 | } else { | 246 | } else { |
187 | nl_day = (year & 3) || day <= 59 ? day : day-1; | 247 | if (IS_LEAP_YEAR(year) && day > days_in_year[3]) |
188 | for (month = 0; month < 12; month++) { | 248 | day--; |
189 | if (day_n[month] > nl_day) | 249 | for (month = 1; month < 12; month++) { |
250 | if (days_in_year[month + 1] > day) | ||
190 | break; | 251 | break; |
191 | } | 252 | } |
192 | } | 253 | } |
193 | *date = cpu_to_le16(nl_day-day_n[month-1]+1+(month << 5)+(year << 9)); | 254 | day -= days_in_year[month]; |
194 | } | ||
195 | 255 | ||
196 | EXPORT_SYMBOL_GPL(fat_date_unix2dos); | 256 | *time = cpu_to_le16(((second / SECS_PER_HOUR) % 24) << 11 |
257 | | ((second / SECS_PER_MIN) % 60) << 5 | ||
258 | | (second % SECS_PER_MIN) >> 1); | ||
259 | *date = cpu_to_le16((year << 9) | (month << 5) | (day + 1)); | ||
260 | if (time_cs) | ||
261 | *time_cs = (ts->tv_sec & 1) * 100 + ts->tv_nsec / 10000000; | ||
262 | } | ||
263 | EXPORT_SYMBOL_GPL(fat_time_unix2fat); | ||
197 | 264 | ||
198 | int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) | 265 | int fat_sync_bhs(struct buffer_head **bhs, int nr_bhs) |
199 | { | 266 | { |
diff --git a/fs/fat/namei_msdos.c b/fs/fat/namei_msdos.c new file mode 100644 index 000000000000..7ba03a4acbe0 --- /dev/null +++ b/fs/fat/namei_msdos.c | |||
@@ -0,0 +1,706 @@ | |||
1 | /* | ||
2 | * linux/fs/msdos/namei.c | ||
3 | * | ||
4 | * Written 1992,1993 by Werner Almesberger | ||
5 | * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> | ||
6 | * Rewritten for constant inumbers 1999 by Al Viro | ||
7 | */ | ||
8 | |||
9 | #include <linux/module.h> | ||
10 | #include <linux/time.h> | ||
11 | #include <linux/buffer_head.h> | ||
12 | #include <linux/smp_lock.h> | ||
13 | #include "fat.h" | ||
14 | |||
15 | /* Characters that are undesirable in an MS-DOS file name */ | ||
16 | static unsigned char bad_chars[] = "*?<>|\""; | ||
17 | static unsigned char bad_if_strict[] = "+=,; "; | ||
18 | |||
19 | /***** Formats an MS-DOS file name. Rejects invalid names. */ | ||
20 | static int msdos_format_name(const unsigned char *name, int len, | ||
21 | unsigned char *res, struct fat_mount_options *opts) | ||
22 | /* | ||
23 | * name is the proposed name, len is its length, res is | ||
24 | * the resulting name, opts->name_check is either (r)elaxed, | ||
25 | * (n)ormal or (s)trict, opts->dotsOK allows dots at the | ||
26 | * beginning of name (for hidden files) | ||
27 | */ | ||
28 | { | ||
29 | unsigned char *walk; | ||
30 | unsigned char c; | ||
31 | int space; | ||
32 | |||
33 | if (name[0] == '.') { /* dotfile because . and .. already done */ | ||
34 | if (opts->dotsOK) { | ||
35 | /* Get rid of dot - test for it elsewhere */ | ||
36 | name++; | ||
37 | len--; | ||
38 | } else | ||
39 | return -EINVAL; | ||
40 | } | ||
41 | /* | ||
42 | * disallow names that _really_ start with a dot | ||
43 | */ | ||
44 | space = 1; | ||
45 | c = 0; | ||
46 | for (walk = res; len && walk - res < 8; walk++) { | ||
47 | c = *name++; | ||
48 | len--; | ||
49 | if (opts->name_check != 'r' && strchr(bad_chars, c)) | ||
50 | return -EINVAL; | ||
51 | if (opts->name_check == 's' && strchr(bad_if_strict, c)) | ||
52 | return -EINVAL; | ||
53 | if (c >= 'A' && c <= 'Z' && opts->name_check == 's') | ||
54 | return -EINVAL; | ||
55 | if (c < ' ' || c == ':' || c == '\\') | ||
56 | return -EINVAL; | ||
57 | /* | ||
58 | * 0xE5 is legal as a first character, but we must substitute | ||
59 | * 0x05 because 0xE5 marks deleted files. Yes, DOS really | ||
60 | * does this. | ||
61 | * It seems that Microsoft hacked DOS to support non-US | ||
62 | * characters after the 0xE5 character was already in use to | ||
63 | * mark deleted files. | ||
64 | */ | ||
65 | if ((res == walk) && (c == 0xE5)) | ||
66 | c = 0x05; | ||
67 | if (c == '.') | ||
68 | break; | ||
69 | space = (c == ' '); | ||
70 | *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c - 32 : c; | ||
71 | } | ||
72 | if (space) | ||
73 | return -EINVAL; | ||
74 | if (opts->name_check == 's' && len && c != '.') { | ||
75 | c = *name++; | ||
76 | len--; | ||
77 | if (c != '.') | ||
78 | return -EINVAL; | ||
79 | } | ||
80 | while (c != '.' && len--) | ||
81 | c = *name++; | ||
82 | if (c == '.') { | ||
83 | while (walk - res < 8) | ||
84 | *walk++ = ' '; | ||
85 | while (len > 0 && walk - res < MSDOS_NAME) { | ||
86 | c = *name++; | ||
87 | len--; | ||
88 | if (opts->name_check != 'r' && strchr(bad_chars, c)) | ||
89 | return -EINVAL; | ||
90 | if (opts->name_check == 's' && | ||
91 | strchr(bad_if_strict, c)) | ||
92 | return -EINVAL; | ||
93 | if (c < ' ' || c == ':' || c == '\\') | ||
94 | return -EINVAL; | ||
95 | if (c == '.') { | ||
96 | if (opts->name_check == 's') | ||
97 | return -EINVAL; | ||
98 | break; | ||
99 | } | ||
100 | if (c >= 'A' && c <= 'Z' && opts->name_check == 's') | ||
101 | return -EINVAL; | ||
102 | space = c == ' '; | ||
103 | if (!opts->nocase && c >= 'a' && c <= 'z') | ||
104 | *walk++ = c - 32; | ||
105 | else | ||
106 | *walk++ = c; | ||
107 | } | ||
108 | if (space) | ||
109 | return -EINVAL; | ||
110 | if (opts->name_check == 's' && len) | ||
111 | return -EINVAL; | ||
112 | } | ||
113 | while (walk - res < MSDOS_NAME) | ||
114 | *walk++ = ' '; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | /***** Locates a directory entry. Uses unformatted name. */ | ||
120 | static int msdos_find(struct inode *dir, const unsigned char *name, int len, | ||
121 | struct fat_slot_info *sinfo) | ||
122 | { | ||
123 | struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); | ||
124 | unsigned char msdos_name[MSDOS_NAME]; | ||
125 | int err; | ||
126 | |||
127 | err = msdos_format_name(name, len, msdos_name, &sbi->options); | ||
128 | if (err) | ||
129 | return -ENOENT; | ||
130 | |||
131 | err = fat_scan(dir, msdos_name, sinfo); | ||
132 | if (!err && sbi->options.dotsOK) { | ||
133 | if (name[0] == '.') { | ||
134 | if (!(sinfo->de->attr & ATTR_HIDDEN)) | ||
135 | err = -ENOENT; | ||
136 | } else { | ||
137 | if (sinfo->de->attr & ATTR_HIDDEN) | ||
138 | err = -ENOENT; | ||
139 | } | ||
140 | if (err) | ||
141 | brelse(sinfo->bh); | ||
142 | } | ||
143 | return err; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * Compute the hash for the msdos name corresponding to the dentry. | ||
148 | * Note: if the name is invalid, we leave the hash code unchanged so | ||
149 | * that the existing dentry can be used. The msdos fs routines will | ||
150 | * return ENOENT or EINVAL as appropriate. | ||
151 | */ | ||
152 | static int msdos_hash(struct dentry *dentry, struct qstr *qstr) | ||
153 | { | ||
154 | struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; | ||
155 | unsigned char msdos_name[MSDOS_NAME]; | ||
156 | int error; | ||
157 | |||
158 | error = msdos_format_name(qstr->name, qstr->len, msdos_name, options); | ||
159 | if (!error) | ||
160 | qstr->hash = full_name_hash(msdos_name, MSDOS_NAME); | ||
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Compare two msdos names. If either of the names are invalid, | ||
166 | * we fall back to doing the standard name comparison. | ||
167 | */ | ||
168 | static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
169 | { | ||
170 | struct fat_mount_options *options = &MSDOS_SB(dentry->d_sb)->options; | ||
171 | unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME]; | ||
172 | int error; | ||
173 | |||
174 | error = msdos_format_name(a->name, a->len, a_msdos_name, options); | ||
175 | if (error) | ||
176 | goto old_compare; | ||
177 | error = msdos_format_name(b->name, b->len, b_msdos_name, options); | ||
178 | if (error) | ||
179 | goto old_compare; | ||
180 | error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME); | ||
181 | out: | ||
182 | return error; | ||
183 | |||
184 | old_compare: | ||
185 | error = 1; | ||
186 | if (a->len == b->len) | ||
187 | error = memcmp(a->name, b->name, a->len); | ||
188 | goto out; | ||
189 | } | ||
190 | |||
191 | static struct dentry_operations msdos_dentry_operations = { | ||
192 | .d_hash = msdos_hash, | ||
193 | .d_compare = msdos_cmp, | ||
194 | }; | ||
195 | |||
196 | /* | ||
197 | * AV. Wrappers for FAT sb operations. Is it wise? | ||
198 | */ | ||
199 | |||
200 | /***** Get inode using directory and name */ | ||
201 | static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry, | ||
202 | struct nameidata *nd) | ||
203 | { | ||
204 | struct super_block *sb = dir->i_sb; | ||
205 | struct fat_slot_info sinfo; | ||
206 | struct inode *inode; | ||
207 | int err; | ||
208 | |||
209 | lock_super(sb); | ||
210 | |||
211 | err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo); | ||
212 | if (err) { | ||
213 | if (err == -ENOENT) { | ||
214 | inode = NULL; | ||
215 | goto out; | ||
216 | } | ||
217 | goto error; | ||
218 | } | ||
219 | |||
220 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
221 | brelse(sinfo.bh); | ||
222 | if (IS_ERR(inode)) { | ||
223 | err = PTR_ERR(inode); | ||
224 | goto error; | ||
225 | } | ||
226 | out: | ||
227 | unlock_super(sb); | ||
228 | dentry->d_op = &msdos_dentry_operations; | ||
229 | dentry = d_splice_alias(inode, dentry); | ||
230 | if (dentry) | ||
231 | dentry->d_op = &msdos_dentry_operations; | ||
232 | return dentry; | ||
233 | |||
234 | error: | ||
235 | unlock_super(sb); | ||
236 | return ERR_PTR(err); | ||
237 | } | ||
238 | |||
239 | /***** Creates a directory entry (name is already formatted). */ | ||
240 | static int msdos_add_entry(struct inode *dir, const unsigned char *name, | ||
241 | int is_dir, int is_hid, int cluster, | ||
242 | struct timespec *ts, struct fat_slot_info *sinfo) | ||
243 | { | ||
244 | struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); | ||
245 | struct msdos_dir_entry de; | ||
246 | __le16 time, date; | ||
247 | int err; | ||
248 | |||
249 | memcpy(de.name, name, MSDOS_NAME); | ||
250 | de.attr = is_dir ? ATTR_DIR : ATTR_ARCH; | ||
251 | if (is_hid) | ||
252 | de.attr |= ATTR_HIDDEN; | ||
253 | de.lcase = 0; | ||
254 | fat_time_unix2fat(sbi, ts, &time, &date, NULL); | ||
255 | de.cdate = de.adate = 0; | ||
256 | de.ctime = 0; | ||
257 | de.ctime_cs = 0; | ||
258 | de.time = time; | ||
259 | de.date = date; | ||
260 | de.start = cpu_to_le16(cluster); | ||
261 | de.starthi = cpu_to_le16(cluster >> 16); | ||
262 | de.size = 0; | ||
263 | |||
264 | err = fat_add_entries(dir, &de, 1, sinfo); | ||
265 | if (err) | ||
266 | return err; | ||
267 | |||
268 | dir->i_ctime = dir->i_mtime = *ts; | ||
269 | if (IS_DIRSYNC(dir)) | ||
270 | (void)fat_sync_inode(dir); | ||
271 | else | ||
272 | mark_inode_dirty(dir); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | /***** Create a file */ | ||
278 | static int msdos_create(struct inode *dir, struct dentry *dentry, int mode, | ||
279 | struct nameidata *nd) | ||
280 | { | ||
281 | struct super_block *sb = dir->i_sb; | ||
282 | struct inode *inode = NULL; | ||
283 | struct fat_slot_info sinfo; | ||
284 | struct timespec ts; | ||
285 | unsigned char msdos_name[MSDOS_NAME]; | ||
286 | int err, is_hid; | ||
287 | |||
288 | lock_super(sb); | ||
289 | |||
290 | err = msdos_format_name(dentry->d_name.name, dentry->d_name.len, | ||
291 | msdos_name, &MSDOS_SB(sb)->options); | ||
292 | if (err) | ||
293 | goto out; | ||
294 | is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.'); | ||
295 | /* Have to do it due to foo vs. .foo conflicts */ | ||
296 | if (!fat_scan(dir, msdos_name, &sinfo)) { | ||
297 | brelse(sinfo.bh); | ||
298 | err = -EINVAL; | ||
299 | goto out; | ||
300 | } | ||
301 | |||
302 | ts = CURRENT_TIME_SEC; | ||
303 | err = msdos_add_entry(dir, msdos_name, 0, is_hid, 0, &ts, &sinfo); | ||
304 | if (err) | ||
305 | goto out; | ||
306 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
307 | brelse(sinfo.bh); | ||
308 | if (IS_ERR(inode)) { | ||
309 | err = PTR_ERR(inode); | ||
310 | goto out; | ||
311 | } | ||
312 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | ||
313 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | ||
314 | |||
315 | d_instantiate(dentry, inode); | ||
316 | out: | ||
317 | unlock_super(sb); | ||
318 | if (!err) | ||
319 | err = fat_flush_inodes(sb, dir, inode); | ||
320 | return err; | ||
321 | } | ||
322 | |||
323 | /***** Remove a directory */ | ||
324 | static int msdos_rmdir(struct inode *dir, struct dentry *dentry) | ||
325 | { | ||
326 | struct super_block *sb = dir->i_sb; | ||
327 | struct inode *inode = dentry->d_inode; | ||
328 | struct fat_slot_info sinfo; | ||
329 | int err; | ||
330 | |||
331 | lock_super(sb); | ||
332 | /* | ||
333 | * Check whether the directory is not in use, then check | ||
334 | * whether it is empty. | ||
335 | */ | ||
336 | err = fat_dir_empty(inode); | ||
337 | if (err) | ||
338 | goto out; | ||
339 | err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo); | ||
340 | if (err) | ||
341 | goto out; | ||
342 | |||
343 | err = fat_remove_entries(dir, &sinfo); /* and releases bh */ | ||
344 | if (err) | ||
345 | goto out; | ||
346 | drop_nlink(dir); | ||
347 | |||
348 | clear_nlink(inode); | ||
349 | inode->i_ctime = CURRENT_TIME_SEC; | ||
350 | fat_detach(inode); | ||
351 | out: | ||
352 | unlock_super(sb); | ||
353 | if (!err) | ||
354 | err = fat_flush_inodes(sb, dir, inode); | ||
355 | |||
356 | return err; | ||
357 | } | ||
358 | |||
359 | /***** Make a directory */ | ||
360 | static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
361 | { | ||
362 | struct super_block *sb = dir->i_sb; | ||
363 | struct fat_slot_info sinfo; | ||
364 | struct inode *inode; | ||
365 | unsigned char msdos_name[MSDOS_NAME]; | ||
366 | struct timespec ts; | ||
367 | int err, is_hid, cluster; | ||
368 | |||
369 | lock_super(sb); | ||
370 | |||
371 | err = msdos_format_name(dentry->d_name.name, dentry->d_name.len, | ||
372 | msdos_name, &MSDOS_SB(sb)->options); | ||
373 | if (err) | ||
374 | goto out; | ||
375 | is_hid = (dentry->d_name.name[0] == '.') && (msdos_name[0] != '.'); | ||
376 | /* foo vs .foo situation */ | ||
377 | if (!fat_scan(dir, msdos_name, &sinfo)) { | ||
378 | brelse(sinfo.bh); | ||
379 | err = -EINVAL; | ||
380 | goto out; | ||
381 | } | ||
382 | |||
383 | ts = CURRENT_TIME_SEC; | ||
384 | cluster = fat_alloc_new_dir(dir, &ts); | ||
385 | if (cluster < 0) { | ||
386 | err = cluster; | ||
387 | goto out; | ||
388 | } | ||
389 | err = msdos_add_entry(dir, msdos_name, 1, is_hid, cluster, &ts, &sinfo); | ||
390 | if (err) | ||
391 | goto out_free; | ||
392 | inc_nlink(dir); | ||
393 | |||
394 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
395 | brelse(sinfo.bh); | ||
396 | if (IS_ERR(inode)) { | ||
397 | err = PTR_ERR(inode); | ||
398 | /* the directory was completed, just return a error */ | ||
399 | goto out; | ||
400 | } | ||
401 | inode->i_nlink = 2; | ||
402 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | ||
403 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | ||
404 | |||
405 | d_instantiate(dentry, inode); | ||
406 | |||
407 | unlock_super(sb); | ||
408 | fat_flush_inodes(sb, dir, inode); | ||
409 | return 0; | ||
410 | |||
411 | out_free: | ||
412 | fat_free_clusters(dir, cluster); | ||
413 | out: | ||
414 | unlock_super(sb); | ||
415 | return err; | ||
416 | } | ||
417 | |||
418 | /***** Unlink a file */ | ||
419 | static int msdos_unlink(struct inode *dir, struct dentry *dentry) | ||
420 | { | ||
421 | struct inode *inode = dentry->d_inode; | ||
422 | struct super_block *sb= inode->i_sb; | ||
423 | struct fat_slot_info sinfo; | ||
424 | int err; | ||
425 | |||
426 | lock_super(sb); | ||
427 | err = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &sinfo); | ||
428 | if (err) | ||
429 | goto out; | ||
430 | |||
431 | err = fat_remove_entries(dir, &sinfo); /* and releases bh */ | ||
432 | if (err) | ||
433 | goto out; | ||
434 | clear_nlink(inode); | ||
435 | inode->i_ctime = CURRENT_TIME_SEC; | ||
436 | fat_detach(inode); | ||
437 | out: | ||
438 | unlock_super(sb); | ||
439 | if (!err) | ||
440 | err = fat_flush_inodes(sb, dir, inode); | ||
441 | |||
442 | return err; | ||
443 | } | ||
444 | |||
445 | static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name, | ||
446 | struct dentry *old_dentry, | ||
447 | struct inode *new_dir, unsigned char *new_name, | ||
448 | struct dentry *new_dentry, int is_hid) | ||
449 | { | ||
450 | struct buffer_head *dotdot_bh; | ||
451 | struct msdos_dir_entry *dotdot_de; | ||
452 | struct inode *old_inode, *new_inode; | ||
453 | struct fat_slot_info old_sinfo, sinfo; | ||
454 | struct timespec ts; | ||
455 | loff_t dotdot_i_pos, new_i_pos; | ||
456 | int err, old_attrs, is_dir, update_dotdot, corrupt = 0; | ||
457 | |||
458 | old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; | ||
459 | old_inode = old_dentry->d_inode; | ||
460 | new_inode = new_dentry->d_inode; | ||
461 | |||
462 | err = fat_scan(old_dir, old_name, &old_sinfo); | ||
463 | if (err) { | ||
464 | err = -EIO; | ||
465 | goto out; | ||
466 | } | ||
467 | |||
468 | is_dir = S_ISDIR(old_inode->i_mode); | ||
469 | update_dotdot = (is_dir && old_dir != new_dir); | ||
470 | if (update_dotdot) { | ||
471 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, | ||
472 | &dotdot_i_pos) < 0) { | ||
473 | err = -EIO; | ||
474 | goto out; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | old_attrs = MSDOS_I(old_inode)->i_attrs; | ||
479 | err = fat_scan(new_dir, new_name, &sinfo); | ||
480 | if (!err) { | ||
481 | if (!new_inode) { | ||
482 | /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */ | ||
483 | if (sinfo.de != old_sinfo.de) { | ||
484 | err = -EINVAL; | ||
485 | goto out; | ||
486 | } | ||
487 | if (is_hid) | ||
488 | MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; | ||
489 | else | ||
490 | MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; | ||
491 | if (IS_DIRSYNC(old_dir)) { | ||
492 | err = fat_sync_inode(old_inode); | ||
493 | if (err) { | ||
494 | MSDOS_I(old_inode)->i_attrs = old_attrs; | ||
495 | goto out; | ||
496 | } | ||
497 | } else | ||
498 | mark_inode_dirty(old_inode); | ||
499 | |||
500 | old_dir->i_version++; | ||
501 | old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC; | ||
502 | if (IS_DIRSYNC(old_dir)) | ||
503 | (void)fat_sync_inode(old_dir); | ||
504 | else | ||
505 | mark_inode_dirty(old_dir); | ||
506 | goto out; | ||
507 | } | ||
508 | } | ||
509 | |||
510 | ts = CURRENT_TIME_SEC; | ||
511 | if (new_inode) { | ||
512 | if (err) | ||
513 | goto out; | ||
514 | if (is_dir) { | ||
515 | err = fat_dir_empty(new_inode); | ||
516 | if (err) | ||
517 | goto out; | ||
518 | } | ||
519 | new_i_pos = MSDOS_I(new_inode)->i_pos; | ||
520 | fat_detach(new_inode); | ||
521 | } else { | ||
522 | err = msdos_add_entry(new_dir, new_name, is_dir, is_hid, 0, | ||
523 | &ts, &sinfo); | ||
524 | if (err) | ||
525 | goto out; | ||
526 | new_i_pos = sinfo.i_pos; | ||
527 | } | ||
528 | new_dir->i_version++; | ||
529 | |||
530 | fat_detach(old_inode); | ||
531 | fat_attach(old_inode, new_i_pos); | ||
532 | if (is_hid) | ||
533 | MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN; | ||
534 | else | ||
535 | MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN; | ||
536 | if (IS_DIRSYNC(new_dir)) { | ||
537 | err = fat_sync_inode(old_inode); | ||
538 | if (err) | ||
539 | goto error_inode; | ||
540 | } else | ||
541 | mark_inode_dirty(old_inode); | ||
542 | |||
543 | if (update_dotdot) { | ||
544 | int start = MSDOS_I(new_dir)->i_logstart; | ||
545 | dotdot_de->start = cpu_to_le16(start); | ||
546 | dotdot_de->starthi = cpu_to_le16(start >> 16); | ||
547 | mark_buffer_dirty(dotdot_bh); | ||
548 | if (IS_DIRSYNC(new_dir)) { | ||
549 | err = sync_dirty_buffer(dotdot_bh); | ||
550 | if (err) | ||
551 | goto error_dotdot; | ||
552 | } | ||
553 | drop_nlink(old_dir); | ||
554 | if (!new_inode) | ||
555 | inc_nlink(new_dir); | ||
556 | } | ||
557 | |||
558 | err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */ | ||
559 | old_sinfo.bh = NULL; | ||
560 | if (err) | ||
561 | goto error_dotdot; | ||
562 | old_dir->i_version++; | ||
563 | old_dir->i_ctime = old_dir->i_mtime = ts; | ||
564 | if (IS_DIRSYNC(old_dir)) | ||
565 | (void)fat_sync_inode(old_dir); | ||
566 | else | ||
567 | mark_inode_dirty(old_dir); | ||
568 | |||
569 | if (new_inode) { | ||
570 | drop_nlink(new_inode); | ||
571 | if (is_dir) | ||
572 | drop_nlink(new_inode); | ||
573 | new_inode->i_ctime = ts; | ||
574 | } | ||
575 | out: | ||
576 | brelse(sinfo.bh); | ||
577 | brelse(dotdot_bh); | ||
578 | brelse(old_sinfo.bh); | ||
579 | return err; | ||
580 | |||
581 | error_dotdot: | ||
582 | /* data cluster is shared, serious corruption */ | ||
583 | corrupt = 1; | ||
584 | |||
585 | if (update_dotdot) { | ||
586 | int start = MSDOS_I(old_dir)->i_logstart; | ||
587 | dotdot_de->start = cpu_to_le16(start); | ||
588 | dotdot_de->starthi = cpu_to_le16(start >> 16); | ||
589 | mark_buffer_dirty(dotdot_bh); | ||
590 | corrupt |= sync_dirty_buffer(dotdot_bh); | ||
591 | } | ||
592 | error_inode: | ||
593 | fat_detach(old_inode); | ||
594 | fat_attach(old_inode, old_sinfo.i_pos); | ||
595 | MSDOS_I(old_inode)->i_attrs = old_attrs; | ||
596 | if (new_inode) { | ||
597 | fat_attach(new_inode, new_i_pos); | ||
598 | if (corrupt) | ||
599 | corrupt |= fat_sync_inode(new_inode); | ||
600 | } else { | ||
601 | /* | ||
602 | * If new entry was not sharing the data cluster, it | ||
603 | * shouldn't be serious corruption. | ||
604 | */ | ||
605 | int err2 = fat_remove_entries(new_dir, &sinfo); | ||
606 | if (corrupt) | ||
607 | corrupt |= err2; | ||
608 | sinfo.bh = NULL; | ||
609 | } | ||
610 | if (corrupt < 0) { | ||
611 | fat_fs_panic(new_dir->i_sb, | ||
612 | "%s: Filesystem corrupted (i_pos %lld)", | ||
613 | __func__, sinfo.i_pos); | ||
614 | } | ||
615 | goto out; | ||
616 | } | ||
617 | |||
618 | /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */ | ||
619 | static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
620 | struct inode *new_dir, struct dentry *new_dentry) | ||
621 | { | ||
622 | struct super_block *sb = old_dir->i_sb; | ||
623 | unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME]; | ||
624 | int err, is_hid; | ||
625 | |||
626 | lock_super(sb); | ||
627 | |||
628 | err = msdos_format_name(old_dentry->d_name.name, | ||
629 | old_dentry->d_name.len, old_msdos_name, | ||
630 | &MSDOS_SB(old_dir->i_sb)->options); | ||
631 | if (err) | ||
632 | goto out; | ||
633 | err = msdos_format_name(new_dentry->d_name.name, | ||
634 | new_dentry->d_name.len, new_msdos_name, | ||
635 | &MSDOS_SB(new_dir->i_sb)->options); | ||
636 | if (err) | ||
637 | goto out; | ||
638 | |||
639 | is_hid = | ||
640 | (new_dentry->d_name.name[0] == '.') && (new_msdos_name[0] != '.'); | ||
641 | |||
642 | err = do_msdos_rename(old_dir, old_msdos_name, old_dentry, | ||
643 | new_dir, new_msdos_name, new_dentry, is_hid); | ||
644 | out: | ||
645 | unlock_super(sb); | ||
646 | if (!err) | ||
647 | err = fat_flush_inodes(sb, old_dir, new_dir); | ||
648 | return err; | ||
649 | } | ||
650 | |||
651 | static const struct inode_operations msdos_dir_inode_operations = { | ||
652 | .create = msdos_create, | ||
653 | .lookup = msdos_lookup, | ||
654 | .unlink = msdos_unlink, | ||
655 | .mkdir = msdos_mkdir, | ||
656 | .rmdir = msdos_rmdir, | ||
657 | .rename = msdos_rename, | ||
658 | .setattr = fat_setattr, | ||
659 | .getattr = fat_getattr, | ||
660 | }; | ||
661 | |||
662 | static int msdos_fill_super(struct super_block *sb, void *data, int silent) | ||
663 | { | ||
664 | int res; | ||
665 | |||
666 | res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0); | ||
667 | if (res) | ||
668 | return res; | ||
669 | |||
670 | sb->s_flags |= MS_NOATIME; | ||
671 | sb->s_root->d_op = &msdos_dentry_operations; | ||
672 | return 0; | ||
673 | } | ||
674 | |||
675 | static int msdos_get_sb(struct file_system_type *fs_type, | ||
676 | int flags, const char *dev_name, | ||
677 | void *data, struct vfsmount *mnt) | ||
678 | { | ||
679 | return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super, | ||
680 | mnt); | ||
681 | } | ||
682 | |||
683 | static struct file_system_type msdos_fs_type = { | ||
684 | .owner = THIS_MODULE, | ||
685 | .name = "msdos", | ||
686 | .get_sb = msdos_get_sb, | ||
687 | .kill_sb = kill_block_super, | ||
688 | .fs_flags = FS_REQUIRES_DEV, | ||
689 | }; | ||
690 | |||
691 | static int __init init_msdos_fs(void) | ||
692 | { | ||
693 | return register_filesystem(&msdos_fs_type); | ||
694 | } | ||
695 | |||
696 | static void __exit exit_msdos_fs(void) | ||
697 | { | ||
698 | unregister_filesystem(&msdos_fs_type); | ||
699 | } | ||
700 | |||
701 | MODULE_LICENSE("GPL"); | ||
702 | MODULE_AUTHOR("Werner Almesberger"); | ||
703 | MODULE_DESCRIPTION("MS-DOS filesystem support"); | ||
704 | |||
705 | module_init(init_msdos_fs) | ||
706 | module_exit(exit_msdos_fs) | ||
diff --git a/fs/fat/namei_vfat.c b/fs/fat/namei_vfat.c new file mode 100644 index 000000000000..bf326d4356a3 --- /dev/null +++ b/fs/fat/namei_vfat.c | |||
@@ -0,0 +1,1098 @@ | |||
1 | /* | ||
2 | * linux/fs/vfat/namei.c | ||
3 | * | ||
4 | * Written 1992,1993 by Werner Almesberger | ||
5 | * | ||
6 | * Windows95/Windows NT compatible extended MSDOS filesystem | ||
7 | * by Gordon Chaffee Copyright (C) 1995. Send bug reports for the | ||
8 | * VFAT filesystem to <chaffee@cs.berkeley.edu>. Specify | ||
9 | * what file operation caused you trouble and if you can duplicate | ||
10 | * the problem, send a script that demonstrates it. | ||
11 | * | ||
12 | * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> | ||
13 | * | ||
14 | * Support Multibyte characters and cleanup by | ||
15 | * OGAWA Hirofumi <hirofumi@mail.parknet.co.jp> | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/jiffies.h> | ||
20 | #include <linux/ctype.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/smp_lock.h> | ||
23 | #include <linux/buffer_head.h> | ||
24 | #include <linux/namei.h> | ||
25 | #include "fat.h" | ||
26 | |||
27 | /* | ||
28 | * If new entry was created in the parent, it could create the 8.3 | ||
29 | * alias (the shortname of logname). So, the parent may have the | ||
30 | * negative-dentry which matches the created 8.3 alias. | ||
31 | * | ||
32 | * If it happened, the negative dentry isn't actually negative | ||
33 | * anymore. So, drop it. | ||
34 | */ | ||
35 | static int vfat_revalidate_shortname(struct dentry *dentry) | ||
36 | { | ||
37 | int ret = 1; | ||
38 | spin_lock(&dentry->d_lock); | ||
39 | if (dentry->d_time != dentry->d_parent->d_inode->i_version) | ||
40 | ret = 0; | ||
41 | spin_unlock(&dentry->d_lock); | ||
42 | return ret; | ||
43 | } | ||
44 | |||
45 | static int vfat_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
46 | { | ||
47 | /* This is not negative dentry. Always valid. */ | ||
48 | if (dentry->d_inode) | ||
49 | return 1; | ||
50 | return vfat_revalidate_shortname(dentry); | ||
51 | } | ||
52 | |||
53 | static int vfat_revalidate_ci(struct dentry *dentry, struct nameidata *nd) | ||
54 | { | ||
55 | /* | ||
56 | * This is not negative dentry. Always valid. | ||
57 | * | ||
58 | * Note, rename() to existing directory entry will have ->d_inode, | ||
59 | * and will use existing name which isn't specified name by user. | ||
60 | * | ||
61 | * We may be able to drop this positive dentry here. But dropping | ||
62 | * positive dentry isn't good idea. So it's unsupported like | ||
63 | * rename("filename", "FILENAME") for now. | ||
64 | */ | ||
65 | if (dentry->d_inode) | ||
66 | return 1; | ||
67 | |||
68 | /* | ||
69 | * This may be nfsd (or something), anyway, we can't see the | ||
70 | * intent of this. So, since this can be for creation, drop it. | ||
71 | */ | ||
72 | if (!nd) | ||
73 | return 0; | ||
74 | |||
75 | /* | ||
76 | * Drop the negative dentry, in order to make sure to use the | ||
77 | * case sensitive name which is specified by user if this is | ||
78 | * for creation. | ||
79 | */ | ||
80 | if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) { | ||
81 | if (nd->flags & LOOKUP_CREATE) | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | return vfat_revalidate_shortname(dentry); | ||
86 | } | ||
87 | |||
88 | /* returns the length of a struct qstr, ignoring trailing dots */ | ||
89 | static unsigned int vfat_striptail_len(struct qstr *qstr) | ||
90 | { | ||
91 | unsigned int len = qstr->len; | ||
92 | |||
93 | while (len && qstr->name[len - 1] == '.') | ||
94 | len--; | ||
95 | return len; | ||
96 | } | ||
97 | |||
98 | /* | ||
99 | * Compute the hash for the vfat name corresponding to the dentry. | ||
100 | * Note: if the name is invalid, we leave the hash code unchanged so | ||
101 | * that the existing dentry can be used. The vfat fs routines will | ||
102 | * return ENOENT or EINVAL as appropriate. | ||
103 | */ | ||
104 | static int vfat_hash(struct dentry *dentry, struct qstr *qstr) | ||
105 | { | ||
106 | qstr->hash = full_name_hash(qstr->name, vfat_striptail_len(qstr)); | ||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * Compute the hash for the vfat name corresponding to the dentry. | ||
112 | * Note: if the name is invalid, we leave the hash code unchanged so | ||
113 | * that the existing dentry can be used. The vfat fs routines will | ||
114 | * return ENOENT or EINVAL as appropriate. | ||
115 | */ | ||
116 | static int vfat_hashi(struct dentry *dentry, struct qstr *qstr) | ||
117 | { | ||
118 | struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io; | ||
119 | const unsigned char *name; | ||
120 | unsigned int len; | ||
121 | unsigned long hash; | ||
122 | |||
123 | name = qstr->name; | ||
124 | len = vfat_striptail_len(qstr); | ||
125 | |||
126 | hash = init_name_hash(); | ||
127 | while (len--) | ||
128 | hash = partial_name_hash(nls_tolower(t, *name++), hash); | ||
129 | qstr->hash = end_name_hash(hash); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Case insensitive compare of two vfat names. | ||
136 | */ | ||
137 | static int vfat_cmpi(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
138 | { | ||
139 | struct nls_table *t = MSDOS_SB(dentry->d_inode->i_sb)->nls_io; | ||
140 | unsigned int alen, blen; | ||
141 | |||
142 | /* A filename cannot end in '.' or we treat it like it has none */ | ||
143 | alen = vfat_striptail_len(a); | ||
144 | blen = vfat_striptail_len(b); | ||
145 | if (alen == blen) { | ||
146 | if (nls_strnicmp(t, a->name, b->name, alen) == 0) | ||
147 | return 0; | ||
148 | } | ||
149 | return 1; | ||
150 | } | ||
151 | |||
152 | /* | ||
153 | * Case sensitive compare of two vfat names. | ||
154 | */ | ||
155 | static int vfat_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b) | ||
156 | { | ||
157 | unsigned int alen, blen; | ||
158 | |||
159 | /* A filename cannot end in '.' or we treat it like it has none */ | ||
160 | alen = vfat_striptail_len(a); | ||
161 | blen = vfat_striptail_len(b); | ||
162 | if (alen == blen) { | ||
163 | if (strncmp(a->name, b->name, alen) == 0) | ||
164 | return 0; | ||
165 | } | ||
166 | return 1; | ||
167 | } | ||
168 | |||
169 | static struct dentry_operations vfat_ci_dentry_ops = { | ||
170 | .d_revalidate = vfat_revalidate_ci, | ||
171 | .d_hash = vfat_hashi, | ||
172 | .d_compare = vfat_cmpi, | ||
173 | }; | ||
174 | |||
175 | static struct dentry_operations vfat_dentry_ops = { | ||
176 | .d_revalidate = vfat_revalidate, | ||
177 | .d_hash = vfat_hash, | ||
178 | .d_compare = vfat_cmp, | ||
179 | }; | ||
180 | |||
181 | /* Characters that are undesirable in an MS-DOS file name */ | ||
182 | |||
183 | static inline wchar_t vfat_bad_char(wchar_t w) | ||
184 | { | ||
185 | return (w < 0x0020) | ||
186 | || (w == '*') || (w == '?') || (w == '<') || (w == '>') | ||
187 | || (w == '|') || (w == '"') || (w == ':') || (w == '/') | ||
188 | || (w == '\\'); | ||
189 | } | ||
190 | |||
191 | static inline wchar_t vfat_replace_char(wchar_t w) | ||
192 | { | ||
193 | return (w == '[') || (w == ']') || (w == ';') || (w == ',') | ||
194 | || (w == '+') || (w == '='); | ||
195 | } | ||
196 | |||
197 | static wchar_t vfat_skip_char(wchar_t w) | ||
198 | { | ||
199 | return (w == '.') || (w == ' '); | ||
200 | } | ||
201 | |||
202 | static inline int vfat_is_used_badchars(const wchar_t *s, int len) | ||
203 | { | ||
204 | int i; | ||
205 | |||
206 | for (i = 0; i < len; i++) | ||
207 | if (vfat_bad_char(s[i])) | ||
208 | return -EINVAL; | ||
209 | |||
210 | if (s[i - 1] == ' ') /* last character cannot be space */ | ||
211 | return -EINVAL; | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int vfat_find_form(struct inode *dir, unsigned char *name) | ||
217 | { | ||
218 | struct fat_slot_info sinfo; | ||
219 | int err = fat_scan(dir, name, &sinfo); | ||
220 | if (err) | ||
221 | return -ENOENT; | ||
222 | brelse(sinfo.bh); | ||
223 | return 0; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * 1) Valid characters for the 8.3 format alias are any combination of | ||
228 | * letters, uppercase alphabets, digits, any of the | ||
229 | * following special characters: | ||
230 | * $ % ' ` - @ { } ~ ! # ( ) & _ ^ | ||
231 | * In this case Longfilename is not stored in disk. | ||
232 | * | ||
233 | * WinNT's Extension: | ||
234 | * File name and extension name is contain uppercase/lowercase | ||
235 | * only. And it is expressed by CASE_LOWER_BASE and CASE_LOWER_EXT. | ||
236 | * | ||
237 | * 2) File name is 8.3 format, but it contain the uppercase and | ||
238 | * lowercase char, muliti bytes char, etc. In this case numtail is not | ||
239 | * added, but Longfilename is stored. | ||
240 | * | ||
241 | * 3) When the one except for the above, or the following special | ||
242 | * character are contained: | ||
243 | * . [ ] ; , + = | ||
244 | * numtail is added, and Longfilename must be stored in disk . | ||
245 | */ | ||
246 | struct shortname_info { | ||
247 | unsigned char lower:1, | ||
248 | upper:1, | ||
249 | valid:1; | ||
250 | }; | ||
251 | #define INIT_SHORTNAME_INFO(x) do { \ | ||
252 | (x)->lower = 1; \ | ||
253 | (x)->upper = 1; \ | ||
254 | (x)->valid = 1; \ | ||
255 | } while (0) | ||
256 | |||
257 | static inline int to_shortname_char(struct nls_table *nls, | ||
258 | unsigned char *buf, int buf_size, | ||
259 | wchar_t *src, struct shortname_info *info) | ||
260 | { | ||
261 | int len; | ||
262 | |||
263 | if (vfat_skip_char(*src)) { | ||
264 | info->valid = 0; | ||
265 | return 0; | ||
266 | } | ||
267 | if (vfat_replace_char(*src)) { | ||
268 | info->valid = 0; | ||
269 | buf[0] = '_'; | ||
270 | return 1; | ||
271 | } | ||
272 | |||
273 | len = nls->uni2char(*src, buf, buf_size); | ||
274 | if (len <= 0) { | ||
275 | info->valid = 0; | ||
276 | buf[0] = '_'; | ||
277 | len = 1; | ||
278 | } else if (len == 1) { | ||
279 | unsigned char prev = buf[0]; | ||
280 | |||
281 | if (buf[0] >= 0x7F) { | ||
282 | info->lower = 0; | ||
283 | info->upper = 0; | ||
284 | } | ||
285 | |||
286 | buf[0] = nls_toupper(nls, buf[0]); | ||
287 | if (isalpha(buf[0])) { | ||
288 | if (buf[0] == prev) | ||
289 | info->lower = 0; | ||
290 | else | ||
291 | info->upper = 0; | ||
292 | } | ||
293 | } else { | ||
294 | info->lower = 0; | ||
295 | info->upper = 0; | ||
296 | } | ||
297 | |||
298 | return len; | ||
299 | } | ||
300 | |||
301 | /* | ||
302 | * Given a valid longname, create a unique shortname. Make sure the | ||
303 | * shortname does not exist | ||
304 | * Returns negative number on error, 0 for a normal | ||
305 | * return, and 1 for valid shortname | ||
306 | */ | ||
307 | static int vfat_create_shortname(struct inode *dir, struct nls_table *nls, | ||
308 | wchar_t *uname, int ulen, | ||
309 | unsigned char *name_res, unsigned char *lcase) | ||
310 | { | ||
311 | struct fat_mount_options *opts = &MSDOS_SB(dir->i_sb)->options; | ||
312 | wchar_t *ip, *ext_start, *end, *name_start; | ||
313 | unsigned char base[9], ext[4], buf[8], *p; | ||
314 | unsigned char charbuf[NLS_MAX_CHARSET_SIZE]; | ||
315 | int chl, chi; | ||
316 | int sz = 0, extlen, baselen, i, numtail_baselen, numtail2_baselen; | ||
317 | int is_shortname; | ||
318 | struct shortname_info base_info, ext_info; | ||
319 | |||
320 | is_shortname = 1; | ||
321 | INIT_SHORTNAME_INFO(&base_info); | ||
322 | INIT_SHORTNAME_INFO(&ext_info); | ||
323 | |||
324 | /* Now, we need to create a shortname from the long name */ | ||
325 | ext_start = end = &uname[ulen]; | ||
326 | while (--ext_start >= uname) { | ||
327 | if (*ext_start == 0x002E) { /* is `.' */ | ||
328 | if (ext_start == end - 1) { | ||
329 | sz = ulen; | ||
330 | ext_start = NULL; | ||
331 | } | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | |||
336 | if (ext_start == uname - 1) { | ||
337 | sz = ulen; | ||
338 | ext_start = NULL; | ||
339 | } else if (ext_start) { | ||
340 | /* | ||
341 | * Names which start with a dot could be just | ||
342 | * an extension eg. "...test". In this case Win95 | ||
343 | * uses the extension as the name and sets no extension. | ||
344 | */ | ||
345 | name_start = &uname[0]; | ||
346 | while (name_start < ext_start) { | ||
347 | if (!vfat_skip_char(*name_start)) | ||
348 | break; | ||
349 | name_start++; | ||
350 | } | ||
351 | if (name_start != ext_start) { | ||
352 | sz = ext_start - uname; | ||
353 | ext_start++; | ||
354 | } else { | ||
355 | sz = ulen; | ||
356 | ext_start = NULL; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | numtail_baselen = 6; | ||
361 | numtail2_baselen = 2; | ||
362 | for (baselen = i = 0, p = base, ip = uname; i < sz; i++, ip++) { | ||
363 | chl = to_shortname_char(nls, charbuf, sizeof(charbuf), | ||
364 | ip, &base_info); | ||
365 | if (chl == 0) | ||
366 | continue; | ||
367 | |||
368 | if (baselen < 2 && (baselen + chl) > 2) | ||
369 | numtail2_baselen = baselen; | ||
370 | if (baselen < 6 && (baselen + chl) > 6) | ||
371 | numtail_baselen = baselen; | ||
372 | for (chi = 0; chi < chl; chi++) { | ||
373 | *p++ = charbuf[chi]; | ||
374 | baselen++; | ||
375 | if (baselen >= 8) | ||
376 | break; | ||
377 | } | ||
378 | if (baselen >= 8) { | ||
379 | if ((chi < chl - 1) || (ip + 1) - uname < sz) | ||
380 | is_shortname = 0; | ||
381 | break; | ||
382 | } | ||
383 | } | ||
384 | if (baselen == 0) { | ||
385 | return -EINVAL; | ||
386 | } | ||
387 | |||
388 | extlen = 0; | ||
389 | if (ext_start) { | ||
390 | for (p = ext, ip = ext_start; extlen < 3 && ip < end; ip++) { | ||
391 | chl = to_shortname_char(nls, charbuf, sizeof(charbuf), | ||
392 | ip, &ext_info); | ||
393 | if (chl == 0) | ||
394 | continue; | ||
395 | |||
396 | if ((extlen + chl) > 3) { | ||
397 | is_shortname = 0; | ||
398 | break; | ||
399 | } | ||
400 | for (chi = 0; chi < chl; chi++) { | ||
401 | *p++ = charbuf[chi]; | ||
402 | extlen++; | ||
403 | } | ||
404 | if (extlen >= 3) { | ||
405 | if (ip + 1 != end) | ||
406 | is_shortname = 0; | ||
407 | break; | ||
408 | } | ||
409 | } | ||
410 | } | ||
411 | ext[extlen] = '\0'; | ||
412 | base[baselen] = '\0'; | ||
413 | |||
414 | /* Yes, it can happen. ".\xe5" would do it. */ | ||
415 | if (base[0] == DELETED_FLAG) | ||
416 | base[0] = 0x05; | ||
417 | |||
418 | /* OK, at this point we know that base is not longer than 8 symbols, | ||
419 | * ext is not longer than 3, base is nonempty, both don't contain | ||
420 | * any bad symbols (lowercase transformed to uppercase). | ||
421 | */ | ||
422 | |||
423 | memset(name_res, ' ', MSDOS_NAME); | ||
424 | memcpy(name_res, base, baselen); | ||
425 | memcpy(name_res + 8, ext, extlen); | ||
426 | *lcase = 0; | ||
427 | if (is_shortname && base_info.valid && ext_info.valid) { | ||
428 | if (vfat_find_form(dir, name_res) == 0) | ||
429 | return -EEXIST; | ||
430 | |||
431 | if (opts->shortname & VFAT_SFN_CREATE_WIN95) { | ||
432 | return (base_info.upper && ext_info.upper); | ||
433 | } else if (opts->shortname & VFAT_SFN_CREATE_WINNT) { | ||
434 | if ((base_info.upper || base_info.lower) && | ||
435 | (ext_info.upper || ext_info.lower)) { | ||
436 | if (!base_info.upper && base_info.lower) | ||
437 | *lcase |= CASE_LOWER_BASE; | ||
438 | if (!ext_info.upper && ext_info.lower) | ||
439 | *lcase |= CASE_LOWER_EXT; | ||
440 | return 1; | ||
441 | } | ||
442 | return 0; | ||
443 | } else { | ||
444 | BUG(); | ||
445 | } | ||
446 | } | ||
447 | |||
448 | if (opts->numtail == 0) | ||
449 | if (vfat_find_form(dir, name_res) < 0) | ||
450 | return 0; | ||
451 | |||
452 | /* | ||
453 | * Try to find a unique extension. This used to | ||
454 | * iterate through all possibilities sequentially, | ||
455 | * but that gave extremely bad performance. Windows | ||
456 | * only tries a few cases before using random | ||
457 | * values for part of the base. | ||
458 | */ | ||
459 | |||
460 | if (baselen > 6) { | ||
461 | baselen = numtail_baselen; | ||
462 | name_res[7] = ' '; | ||
463 | } | ||
464 | name_res[baselen] = '~'; | ||
465 | for (i = 1; i < 10; i++) { | ||
466 | name_res[baselen + 1] = i + '0'; | ||
467 | if (vfat_find_form(dir, name_res) < 0) | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | i = jiffies & 0xffff; | ||
472 | sz = (jiffies >> 16) & 0x7; | ||
473 | if (baselen > 2) { | ||
474 | baselen = numtail2_baselen; | ||
475 | name_res[7] = ' '; | ||
476 | } | ||
477 | name_res[baselen + 4] = '~'; | ||
478 | name_res[baselen + 5] = '1' + sz; | ||
479 | while (1) { | ||
480 | sprintf(buf, "%04X", i); | ||
481 | memcpy(&name_res[baselen], buf, 4); | ||
482 | if (vfat_find_form(dir, name_res) < 0) | ||
483 | break; | ||
484 | i -= 11; | ||
485 | } | ||
486 | return 0; | ||
487 | } | ||
488 | |||
489 | /* Translate a string, including coded sequences into Unicode */ | ||
490 | static int | ||
491 | xlate_to_uni(const unsigned char *name, int len, unsigned char *outname, | ||
492 | int *longlen, int *outlen, int escape, int utf8, | ||
493 | struct nls_table *nls) | ||
494 | { | ||
495 | const unsigned char *ip; | ||
496 | unsigned char nc; | ||
497 | unsigned char *op; | ||
498 | unsigned int ec; | ||
499 | int i, k, fill; | ||
500 | int charlen; | ||
501 | |||
502 | if (utf8) { | ||
503 | int name_len = strlen(name); | ||
504 | |||
505 | *outlen = utf8_mbstowcs((wchar_t *)outname, name, PATH_MAX); | ||
506 | |||
507 | /* | ||
508 | * We stripped '.'s before and set len appropriately, | ||
509 | * but utf8_mbstowcs doesn't care about len | ||
510 | */ | ||
511 | *outlen -= (name_len - len); | ||
512 | |||
513 | if (*outlen > 255) | ||
514 | return -ENAMETOOLONG; | ||
515 | |||
516 | op = &outname[*outlen * sizeof(wchar_t)]; | ||
517 | } else { | ||
518 | if (nls) { | ||
519 | for (i = 0, ip = name, op = outname, *outlen = 0; | ||
520 | i < len && *outlen <= 255; | ||
521 | *outlen += 1) | ||
522 | { | ||
523 | if (escape && (*ip == ':')) { | ||
524 | if (i > len - 5) | ||
525 | return -EINVAL; | ||
526 | ec = 0; | ||
527 | for (k = 1; k < 5; k++) { | ||
528 | nc = ip[k]; | ||
529 | ec <<= 4; | ||
530 | if (nc >= '0' && nc <= '9') { | ||
531 | ec |= nc - '0'; | ||
532 | continue; | ||
533 | } | ||
534 | if (nc >= 'a' && nc <= 'f') { | ||
535 | ec |= nc - ('a' - 10); | ||
536 | continue; | ||
537 | } | ||
538 | if (nc >= 'A' && nc <= 'F') { | ||
539 | ec |= nc - ('A' - 10); | ||
540 | continue; | ||
541 | } | ||
542 | return -EINVAL; | ||
543 | } | ||
544 | *op++ = ec & 0xFF; | ||
545 | *op++ = ec >> 8; | ||
546 | ip += 5; | ||
547 | i += 5; | ||
548 | } else { | ||
549 | if ((charlen = nls->char2uni(ip, len - i, (wchar_t *)op)) < 0) | ||
550 | return -EINVAL; | ||
551 | ip += charlen; | ||
552 | i += charlen; | ||
553 | op += 2; | ||
554 | } | ||
555 | } | ||
556 | if (i < len) | ||
557 | return -ENAMETOOLONG; | ||
558 | } else { | ||
559 | for (i = 0, ip = name, op = outname, *outlen = 0; | ||
560 | i < len && *outlen <= 255; | ||
561 | i++, *outlen += 1) | ||
562 | { | ||
563 | *op++ = *ip++; | ||
564 | *op++ = 0; | ||
565 | } | ||
566 | if (i < len) | ||
567 | return -ENAMETOOLONG; | ||
568 | } | ||
569 | } | ||
570 | |||
571 | *longlen = *outlen; | ||
572 | if (*outlen % 13) { | ||
573 | *op++ = 0; | ||
574 | *op++ = 0; | ||
575 | *outlen += 1; | ||
576 | if (*outlen % 13) { | ||
577 | fill = 13 - (*outlen % 13); | ||
578 | for (i = 0; i < fill; i++) { | ||
579 | *op++ = 0xff; | ||
580 | *op++ = 0xff; | ||
581 | } | ||
582 | *outlen += fill; | ||
583 | } | ||
584 | } | ||
585 | |||
586 | return 0; | ||
587 | } | ||
588 | |||
589 | static int vfat_build_slots(struct inode *dir, const unsigned char *name, | ||
590 | int len, int is_dir, int cluster, | ||
591 | struct timespec *ts, | ||
592 | struct msdos_dir_slot *slots, int *nr_slots) | ||
593 | { | ||
594 | struct msdos_sb_info *sbi = MSDOS_SB(dir->i_sb); | ||
595 | struct fat_mount_options *opts = &sbi->options; | ||
596 | struct msdos_dir_slot *ps; | ||
597 | struct msdos_dir_entry *de; | ||
598 | unsigned char cksum, lcase; | ||
599 | unsigned char msdos_name[MSDOS_NAME]; | ||
600 | wchar_t *uname; | ||
601 | __le16 time, date; | ||
602 | u8 time_cs; | ||
603 | int err, ulen, usize, i; | ||
604 | loff_t offset; | ||
605 | |||
606 | *nr_slots = 0; | ||
607 | |||
608 | uname = __getname(); | ||
609 | if (!uname) | ||
610 | return -ENOMEM; | ||
611 | |||
612 | err = xlate_to_uni(name, len, (unsigned char *)uname, &ulen, &usize, | ||
613 | opts->unicode_xlate, opts->utf8, sbi->nls_io); | ||
614 | if (err) | ||
615 | goto out_free; | ||
616 | |||
617 | err = vfat_is_used_badchars(uname, ulen); | ||
618 | if (err) | ||
619 | goto out_free; | ||
620 | |||
621 | err = vfat_create_shortname(dir, sbi->nls_disk, uname, ulen, | ||
622 | msdos_name, &lcase); | ||
623 | if (err < 0) | ||
624 | goto out_free; | ||
625 | else if (err == 1) { | ||
626 | de = (struct msdos_dir_entry *)slots; | ||
627 | err = 0; | ||
628 | goto shortname; | ||
629 | } | ||
630 | |||
631 | /* build the entry of long file name */ | ||
632 | cksum = fat_checksum(msdos_name); | ||
633 | |||
634 | *nr_slots = usize / 13; | ||
635 | for (ps = slots, i = *nr_slots; i > 0; i--, ps++) { | ||
636 | ps->id = i; | ||
637 | ps->attr = ATTR_EXT; | ||
638 | ps->reserved = 0; | ||
639 | ps->alias_checksum = cksum; | ||
640 | ps->start = 0; | ||
641 | offset = (i - 1) * 13; | ||
642 | fatwchar_to16(ps->name0_4, uname + offset, 5); | ||
643 | fatwchar_to16(ps->name5_10, uname + offset + 5, 6); | ||
644 | fatwchar_to16(ps->name11_12, uname + offset + 11, 2); | ||
645 | } | ||
646 | slots[0].id |= 0x40; | ||
647 | de = (struct msdos_dir_entry *)ps; | ||
648 | |||
649 | shortname: | ||
650 | /* build the entry of 8.3 alias name */ | ||
651 | (*nr_slots)++; | ||
652 | memcpy(de->name, msdos_name, MSDOS_NAME); | ||
653 | de->attr = is_dir ? ATTR_DIR : ATTR_ARCH; | ||
654 | de->lcase = lcase; | ||
655 | fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); | ||
656 | de->time = de->ctime = time; | ||
657 | de->date = de->cdate = de->adate = date; | ||
658 | de->ctime_cs = time_cs; | ||
659 | de->start = cpu_to_le16(cluster); | ||
660 | de->starthi = cpu_to_le16(cluster >> 16); | ||
661 | de->size = 0; | ||
662 | out_free: | ||
663 | __putname(uname); | ||
664 | return err; | ||
665 | } | ||
666 | |||
667 | static int vfat_add_entry(struct inode *dir, struct qstr *qname, int is_dir, | ||
668 | int cluster, struct timespec *ts, | ||
669 | struct fat_slot_info *sinfo) | ||
670 | { | ||
671 | struct msdos_dir_slot *slots; | ||
672 | unsigned int len; | ||
673 | int err, nr_slots; | ||
674 | |||
675 | len = vfat_striptail_len(qname); | ||
676 | if (len == 0) | ||
677 | return -ENOENT; | ||
678 | |||
679 | slots = kmalloc(sizeof(*slots) * MSDOS_SLOTS, GFP_NOFS); | ||
680 | if (slots == NULL) | ||
681 | return -ENOMEM; | ||
682 | |||
683 | err = vfat_build_slots(dir, qname->name, len, is_dir, cluster, ts, | ||
684 | slots, &nr_slots); | ||
685 | if (err) | ||
686 | goto cleanup; | ||
687 | |||
688 | err = fat_add_entries(dir, slots, nr_slots, sinfo); | ||
689 | if (err) | ||
690 | goto cleanup; | ||
691 | |||
692 | /* update timestamp */ | ||
693 | dir->i_ctime = dir->i_mtime = dir->i_atime = *ts; | ||
694 | if (IS_DIRSYNC(dir)) | ||
695 | (void)fat_sync_inode(dir); | ||
696 | else | ||
697 | mark_inode_dirty(dir); | ||
698 | cleanup: | ||
699 | kfree(slots); | ||
700 | return err; | ||
701 | } | ||
702 | |||
703 | static int vfat_find(struct inode *dir, struct qstr *qname, | ||
704 | struct fat_slot_info *sinfo) | ||
705 | { | ||
706 | unsigned int len = vfat_striptail_len(qname); | ||
707 | if (len == 0) | ||
708 | return -ENOENT; | ||
709 | return fat_search_long(dir, qname->name, len, sinfo); | ||
710 | } | ||
711 | |||
712 | static struct dentry *vfat_lookup(struct inode *dir, struct dentry *dentry, | ||
713 | struct nameidata *nd) | ||
714 | { | ||
715 | struct super_block *sb = dir->i_sb; | ||
716 | struct fat_slot_info sinfo; | ||
717 | struct inode *inode; | ||
718 | struct dentry *alias; | ||
719 | int err; | ||
720 | |||
721 | lock_super(sb); | ||
722 | |||
723 | err = vfat_find(dir, &dentry->d_name, &sinfo); | ||
724 | if (err) { | ||
725 | if (err == -ENOENT) { | ||
726 | inode = NULL; | ||
727 | goto out; | ||
728 | } | ||
729 | goto error; | ||
730 | } | ||
731 | |||
732 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
733 | brelse(sinfo.bh); | ||
734 | if (IS_ERR(inode)) { | ||
735 | err = PTR_ERR(inode); | ||
736 | goto error; | ||
737 | } | ||
738 | |||
739 | alias = d_find_alias(inode); | ||
740 | if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) { | ||
741 | /* | ||
742 | * This inode has non DCACHE_DISCONNECTED dentry. This | ||
743 | * means, the user did ->lookup() by an another name | ||
744 | * (longname vs 8.3 alias of it) in past. | ||
745 | * | ||
746 | * Switch to new one for reason of locality if possible. | ||
747 | */ | ||
748 | BUG_ON(d_unhashed(alias)); | ||
749 | if (!S_ISDIR(inode->i_mode)) | ||
750 | d_move(alias, dentry); | ||
751 | iput(inode); | ||
752 | unlock_super(sb); | ||
753 | return alias; | ||
754 | } | ||
755 | out: | ||
756 | unlock_super(sb); | ||
757 | dentry->d_op = sb->s_root->d_op; | ||
758 | dentry->d_time = dentry->d_parent->d_inode->i_version; | ||
759 | dentry = d_splice_alias(inode, dentry); | ||
760 | if (dentry) { | ||
761 | dentry->d_op = sb->s_root->d_op; | ||
762 | dentry->d_time = dentry->d_parent->d_inode->i_version; | ||
763 | } | ||
764 | return dentry; | ||
765 | |||
766 | error: | ||
767 | unlock_super(sb); | ||
768 | return ERR_PTR(err); | ||
769 | } | ||
770 | |||
771 | static int vfat_create(struct inode *dir, struct dentry *dentry, int mode, | ||
772 | struct nameidata *nd) | ||
773 | { | ||
774 | struct super_block *sb = dir->i_sb; | ||
775 | struct inode *inode; | ||
776 | struct fat_slot_info sinfo; | ||
777 | struct timespec ts; | ||
778 | int err; | ||
779 | |||
780 | lock_super(sb); | ||
781 | |||
782 | ts = CURRENT_TIME_SEC; | ||
783 | err = vfat_add_entry(dir, &dentry->d_name, 0, 0, &ts, &sinfo); | ||
784 | if (err) | ||
785 | goto out; | ||
786 | dir->i_version++; | ||
787 | |||
788 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
789 | brelse(sinfo.bh); | ||
790 | if (IS_ERR(inode)) { | ||
791 | err = PTR_ERR(inode); | ||
792 | goto out; | ||
793 | } | ||
794 | inode->i_version++; | ||
795 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | ||
796 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | ||
797 | |||
798 | dentry->d_time = dentry->d_parent->d_inode->i_version; | ||
799 | d_instantiate(dentry, inode); | ||
800 | out: | ||
801 | unlock_super(sb); | ||
802 | return err; | ||
803 | } | ||
804 | |||
805 | static int vfat_rmdir(struct inode *dir, struct dentry *dentry) | ||
806 | { | ||
807 | struct inode *inode = dentry->d_inode; | ||
808 | struct super_block *sb = dir->i_sb; | ||
809 | struct fat_slot_info sinfo; | ||
810 | int err; | ||
811 | |||
812 | lock_super(sb); | ||
813 | |||
814 | err = fat_dir_empty(inode); | ||
815 | if (err) | ||
816 | goto out; | ||
817 | err = vfat_find(dir, &dentry->d_name, &sinfo); | ||
818 | if (err) | ||
819 | goto out; | ||
820 | |||
821 | err = fat_remove_entries(dir, &sinfo); /* and releases bh */ | ||
822 | if (err) | ||
823 | goto out; | ||
824 | drop_nlink(dir); | ||
825 | |||
826 | clear_nlink(inode); | ||
827 | inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; | ||
828 | fat_detach(inode); | ||
829 | out: | ||
830 | unlock_super(sb); | ||
831 | |||
832 | return err; | ||
833 | } | ||
834 | |||
835 | static int vfat_unlink(struct inode *dir, struct dentry *dentry) | ||
836 | { | ||
837 | struct inode *inode = dentry->d_inode; | ||
838 | struct super_block *sb = dir->i_sb; | ||
839 | struct fat_slot_info sinfo; | ||
840 | int err; | ||
841 | |||
842 | lock_super(sb); | ||
843 | |||
844 | err = vfat_find(dir, &dentry->d_name, &sinfo); | ||
845 | if (err) | ||
846 | goto out; | ||
847 | |||
848 | err = fat_remove_entries(dir, &sinfo); /* and releases bh */ | ||
849 | if (err) | ||
850 | goto out; | ||
851 | clear_nlink(inode); | ||
852 | inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC; | ||
853 | fat_detach(inode); | ||
854 | out: | ||
855 | unlock_super(sb); | ||
856 | |||
857 | return err; | ||
858 | } | ||
859 | |||
860 | static int vfat_mkdir(struct inode *dir, struct dentry *dentry, int mode) | ||
861 | { | ||
862 | struct super_block *sb = dir->i_sb; | ||
863 | struct inode *inode; | ||
864 | struct fat_slot_info sinfo; | ||
865 | struct timespec ts; | ||
866 | int err, cluster; | ||
867 | |||
868 | lock_super(sb); | ||
869 | |||
870 | ts = CURRENT_TIME_SEC; | ||
871 | cluster = fat_alloc_new_dir(dir, &ts); | ||
872 | if (cluster < 0) { | ||
873 | err = cluster; | ||
874 | goto out; | ||
875 | } | ||
876 | err = vfat_add_entry(dir, &dentry->d_name, 1, cluster, &ts, &sinfo); | ||
877 | if (err) | ||
878 | goto out_free; | ||
879 | dir->i_version++; | ||
880 | inc_nlink(dir); | ||
881 | |||
882 | inode = fat_build_inode(sb, sinfo.de, sinfo.i_pos); | ||
883 | brelse(sinfo.bh); | ||
884 | if (IS_ERR(inode)) { | ||
885 | err = PTR_ERR(inode); | ||
886 | /* the directory was completed, just return a error */ | ||
887 | goto out; | ||
888 | } | ||
889 | inode->i_version++; | ||
890 | inode->i_nlink = 2; | ||
891 | inode->i_mtime = inode->i_atime = inode->i_ctime = ts; | ||
892 | /* timestamp is already written, so mark_inode_dirty() is unneeded. */ | ||
893 | |||
894 | dentry->d_time = dentry->d_parent->d_inode->i_version; | ||
895 | d_instantiate(dentry, inode); | ||
896 | |||
897 | unlock_super(sb); | ||
898 | return 0; | ||
899 | |||
900 | out_free: | ||
901 | fat_free_clusters(dir, cluster); | ||
902 | out: | ||
903 | unlock_super(sb); | ||
904 | return err; | ||
905 | } | ||
906 | |||
907 | static int vfat_rename(struct inode *old_dir, struct dentry *old_dentry, | ||
908 | struct inode *new_dir, struct dentry *new_dentry) | ||
909 | { | ||
910 | struct buffer_head *dotdot_bh; | ||
911 | struct msdos_dir_entry *dotdot_de; | ||
912 | struct inode *old_inode, *new_inode; | ||
913 | struct fat_slot_info old_sinfo, sinfo; | ||
914 | struct timespec ts; | ||
915 | loff_t dotdot_i_pos, new_i_pos; | ||
916 | int err, is_dir, update_dotdot, corrupt = 0; | ||
917 | struct super_block *sb = old_dir->i_sb; | ||
918 | |||
919 | old_sinfo.bh = sinfo.bh = dotdot_bh = NULL; | ||
920 | old_inode = old_dentry->d_inode; | ||
921 | new_inode = new_dentry->d_inode; | ||
922 | lock_super(sb); | ||
923 | err = vfat_find(old_dir, &old_dentry->d_name, &old_sinfo); | ||
924 | if (err) | ||
925 | goto out; | ||
926 | |||
927 | is_dir = S_ISDIR(old_inode->i_mode); | ||
928 | update_dotdot = (is_dir && old_dir != new_dir); | ||
929 | if (update_dotdot) { | ||
930 | if (fat_get_dotdot_entry(old_inode, &dotdot_bh, &dotdot_de, | ||
931 | &dotdot_i_pos) < 0) { | ||
932 | err = -EIO; | ||
933 | goto out; | ||
934 | } | ||
935 | } | ||
936 | |||
937 | ts = CURRENT_TIME_SEC; | ||
938 | if (new_inode) { | ||
939 | if (is_dir) { | ||
940 | err = fat_dir_empty(new_inode); | ||
941 | if (err) | ||
942 | goto out; | ||
943 | } | ||
944 | new_i_pos = MSDOS_I(new_inode)->i_pos; | ||
945 | fat_detach(new_inode); | ||
946 | } else { | ||
947 | err = vfat_add_entry(new_dir, &new_dentry->d_name, is_dir, 0, | ||
948 | &ts, &sinfo); | ||
949 | if (err) | ||
950 | goto out; | ||
951 | new_i_pos = sinfo.i_pos; | ||
952 | } | ||
953 | new_dir->i_version++; | ||
954 | |||
955 | fat_detach(old_inode); | ||
956 | fat_attach(old_inode, new_i_pos); | ||
957 | if (IS_DIRSYNC(new_dir)) { | ||
958 | err = fat_sync_inode(old_inode); | ||
959 | if (err) | ||
960 | goto error_inode; | ||
961 | } else | ||
962 | mark_inode_dirty(old_inode); | ||
963 | |||
964 | if (update_dotdot) { | ||
965 | int start = MSDOS_I(new_dir)->i_logstart; | ||
966 | dotdot_de->start = cpu_to_le16(start); | ||
967 | dotdot_de->starthi = cpu_to_le16(start >> 16); | ||
968 | mark_buffer_dirty(dotdot_bh); | ||
969 | if (IS_DIRSYNC(new_dir)) { | ||
970 | err = sync_dirty_buffer(dotdot_bh); | ||
971 | if (err) | ||
972 | goto error_dotdot; | ||
973 | } | ||
974 | drop_nlink(old_dir); | ||
975 | if (!new_inode) | ||
976 | inc_nlink(new_dir); | ||
977 | } | ||
978 | |||
979 | err = fat_remove_entries(old_dir, &old_sinfo); /* and releases bh */ | ||
980 | old_sinfo.bh = NULL; | ||
981 | if (err) | ||
982 | goto error_dotdot; | ||
983 | old_dir->i_version++; | ||
984 | old_dir->i_ctime = old_dir->i_mtime = ts; | ||
985 | if (IS_DIRSYNC(old_dir)) | ||
986 | (void)fat_sync_inode(old_dir); | ||
987 | else | ||
988 | mark_inode_dirty(old_dir); | ||
989 | |||
990 | if (new_inode) { | ||
991 | drop_nlink(new_inode); | ||
992 | if (is_dir) | ||
993 | drop_nlink(new_inode); | ||
994 | new_inode->i_ctime = ts; | ||
995 | } | ||
996 | out: | ||
997 | brelse(sinfo.bh); | ||
998 | brelse(dotdot_bh); | ||
999 | brelse(old_sinfo.bh); | ||
1000 | unlock_super(sb); | ||
1001 | |||
1002 | return err; | ||
1003 | |||
1004 | error_dotdot: | ||
1005 | /* data cluster is shared, serious corruption */ | ||
1006 | corrupt = 1; | ||
1007 | |||
1008 | if (update_dotdot) { | ||
1009 | int start = MSDOS_I(old_dir)->i_logstart; | ||
1010 | dotdot_de->start = cpu_to_le16(start); | ||
1011 | dotdot_de->starthi = cpu_to_le16(start >> 16); | ||
1012 | mark_buffer_dirty(dotdot_bh); | ||
1013 | corrupt |= sync_dirty_buffer(dotdot_bh); | ||
1014 | } | ||
1015 | error_inode: | ||
1016 | fat_detach(old_inode); | ||
1017 | fat_attach(old_inode, old_sinfo.i_pos); | ||
1018 | if (new_inode) { | ||
1019 | fat_attach(new_inode, new_i_pos); | ||
1020 | if (corrupt) | ||
1021 | corrupt |= fat_sync_inode(new_inode); | ||
1022 | } else { | ||
1023 | /* | ||
1024 | * If new entry was not sharing the data cluster, it | ||
1025 | * shouldn't be serious corruption. | ||
1026 | */ | ||
1027 | int err2 = fat_remove_entries(new_dir, &sinfo); | ||
1028 | if (corrupt) | ||
1029 | corrupt |= err2; | ||
1030 | sinfo.bh = NULL; | ||
1031 | } | ||
1032 | if (corrupt < 0) { | ||
1033 | fat_fs_panic(new_dir->i_sb, | ||
1034 | "%s: Filesystem corrupted (i_pos %lld)", | ||
1035 | __func__, sinfo.i_pos); | ||
1036 | } | ||
1037 | goto out; | ||
1038 | } | ||
1039 | |||
1040 | static const struct inode_operations vfat_dir_inode_operations = { | ||
1041 | .create = vfat_create, | ||
1042 | .lookup = vfat_lookup, | ||
1043 | .unlink = vfat_unlink, | ||
1044 | .mkdir = vfat_mkdir, | ||
1045 | .rmdir = vfat_rmdir, | ||
1046 | .rename = vfat_rename, | ||
1047 | .setattr = fat_setattr, | ||
1048 | .getattr = fat_getattr, | ||
1049 | }; | ||
1050 | |||
1051 | static int vfat_fill_super(struct super_block *sb, void *data, int silent) | ||
1052 | { | ||
1053 | int res; | ||
1054 | |||
1055 | res = fat_fill_super(sb, data, silent, &vfat_dir_inode_operations, 1); | ||
1056 | if (res) | ||
1057 | return res; | ||
1058 | |||
1059 | if (MSDOS_SB(sb)->options.name_check != 's') | ||
1060 | sb->s_root->d_op = &vfat_ci_dentry_ops; | ||
1061 | else | ||
1062 | sb->s_root->d_op = &vfat_dentry_ops; | ||
1063 | |||
1064 | return 0; | ||
1065 | } | ||
1066 | |||
1067 | static int vfat_get_sb(struct file_system_type *fs_type, | ||
1068 | int flags, const char *dev_name, | ||
1069 | void *data, struct vfsmount *mnt) | ||
1070 | { | ||
1071 | return get_sb_bdev(fs_type, flags, dev_name, data, vfat_fill_super, | ||
1072 | mnt); | ||
1073 | } | ||
1074 | |||
1075 | static struct file_system_type vfat_fs_type = { | ||
1076 | .owner = THIS_MODULE, | ||
1077 | .name = "vfat", | ||
1078 | .get_sb = vfat_get_sb, | ||
1079 | .kill_sb = kill_block_super, | ||
1080 | .fs_flags = FS_REQUIRES_DEV, | ||
1081 | }; | ||
1082 | |||
1083 | static int __init init_vfat_fs(void) | ||
1084 | { | ||
1085 | return register_filesystem(&vfat_fs_type); | ||
1086 | } | ||
1087 | |||
1088 | static void __exit exit_vfat_fs(void) | ||
1089 | { | ||
1090 | unregister_filesystem(&vfat_fs_type); | ||
1091 | } | ||
1092 | |||
1093 | MODULE_LICENSE("GPL"); | ||
1094 | MODULE_DESCRIPTION("VFAT filesystem support"); | ||
1095 | MODULE_AUTHOR("Gordon Chaffee"); | ||
1096 | |||
1097 | module_init(init_vfat_fs) | ||
1098 | module_exit(exit_vfat_fs) | ||