diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/fat/dir.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/fat/dir.c')
-rw-r--r-- | fs/fat/dir.c | 1271 |
1 files changed, 1271 insertions, 0 deletions
diff --git a/fs/fat/dir.c b/fs/fat/dir.c new file mode 100644 index 000000000000..e5ae1b720dde --- /dev/null +++ b/fs/fat/dir.c | |||
@@ -0,0 +1,1271 @@ | |||
1 | /* | ||
2 | * linux/fs/fat/dir.c | ||
3 | * | ||
4 | * directory handling functions for fat-based filesystems | ||
5 | * | ||
6 | * Written 1992,1993 by Werner Almesberger | ||
7 | * | ||
8 | * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> | ||
9 | * | ||
10 | * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> | ||
11 | * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> | ||
12 | * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV | ||
13 | * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/time.h> | ||
19 | #include <linux/msdos_fs.h> | ||
20 | #include <linux/dirent.h> | ||
21 | #include <linux/smp_lock.h> | ||
22 | #include <linux/buffer_head.h> | ||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | static inline loff_t fat_make_i_pos(struct super_block *sb, | ||
26 | struct buffer_head *bh, | ||
27 | struct msdos_dir_entry *de) | ||
28 | { | ||
29 | return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits) | ||
30 | | (de - (struct msdos_dir_entry *)bh->b_data); | ||
31 | } | ||
32 | |||
33 | /* Returns the inode number of the directory entry at offset pos. If bh is | ||
34 | non-NULL, it is brelse'd before. Pos is incremented. The buffer header is | ||
35 | returned in bh. | ||
36 | AV. Most often we do it item-by-item. Makes sense to optimize. | ||
37 | AV. OK, there we go: if both bh and de are non-NULL we assume that we just | ||
38 | AV. want the next entry (took one explicit de=NULL in vfat/namei.c). | ||
39 | AV. It's done in fat_get_entry() (inlined), here the slow case lives. | ||
40 | AV. Additionally, when we return -1 (i.e. reached the end of directory) | ||
41 | AV. we make bh NULL. | ||
42 | */ | ||
43 | static int fat__get_entry(struct inode *dir, loff_t *pos, | ||
44 | struct buffer_head **bh, struct msdos_dir_entry **de) | ||
45 | { | ||
46 | struct super_block *sb = dir->i_sb; | ||
47 | sector_t phys, iblock; | ||
48 | int offset; | ||
49 | int err; | ||
50 | |||
51 | next: | ||
52 | if (*bh) | ||
53 | brelse(*bh); | ||
54 | |||
55 | *bh = NULL; | ||
56 | iblock = *pos >> sb->s_blocksize_bits; | ||
57 | err = fat_bmap(dir, iblock, &phys); | ||
58 | if (err || !phys) | ||
59 | return -1; /* beyond EOF or error */ | ||
60 | |||
61 | *bh = sb_bread(sb, phys); | ||
62 | if (*bh == NULL) { | ||
63 | printk(KERN_ERR "FAT: Directory bread(block %llu) failed\n", | ||
64 | (unsigned long long)phys); | ||
65 | /* skip this block */ | ||
66 | *pos = (iblock + 1) << sb->s_blocksize_bits; | ||
67 | goto next; | ||
68 | } | ||
69 | |||
70 | offset = *pos & (sb->s_blocksize - 1); | ||
71 | *pos += sizeof(struct msdos_dir_entry); | ||
72 | *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); | ||
73 | |||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | static inline int fat_get_entry(struct inode *dir, loff_t *pos, | ||
78 | struct buffer_head **bh, | ||
79 | struct msdos_dir_entry **de) | ||
80 | { | ||
81 | /* Fast stuff first */ | ||
82 | if (*bh && *de && | ||
83 | (*de - (struct msdos_dir_entry *)(*bh)->b_data) < MSDOS_SB(dir->i_sb)->dir_per_block - 1) { | ||
84 | *pos += sizeof(struct msdos_dir_entry); | ||
85 | (*de)++; | ||
86 | return 0; | ||
87 | } | ||
88 | return fat__get_entry(dir, pos, bh, de); | ||
89 | } | ||
90 | |||
91 | /* | ||
92 | * Convert Unicode 16 to UTF8, translated Unicode, or ASCII. | ||
93 | * If uni_xlate is enabled and we can't get a 1:1 conversion, use a | ||
94 | * colon as an escape character since it is normally invalid on the vfat | ||
95 | * filesystem. The following four characters are the hexadecimal digits | ||
96 | * of Unicode value. This lets us do a full dump and restore of Unicode | ||
97 | * filenames. We could get into some trouble with long Unicode names, | ||
98 | * but ignore that right now. | ||
99 | * Ahem... Stack smashing in ring 0 isn't fun. Fixed. | ||
100 | */ | ||
101 | static int uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate, | ||
102 | struct nls_table *nls) | ||
103 | { | ||
104 | wchar_t *ip, ec; | ||
105 | unsigned char *op, nc; | ||
106 | int charlen; | ||
107 | int k; | ||
108 | |||
109 | ip = uni; | ||
110 | op = ascii; | ||
111 | |||
112 | while (*ip) { | ||
113 | ec = *ip++; | ||
114 | if ( (charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE)) > 0) { | ||
115 | op += charlen; | ||
116 | } else { | ||
117 | if (uni_xlate == 1) { | ||
118 | *op = ':'; | ||
119 | for (k = 4; k > 0; k--) { | ||
120 | nc = ec & 0xF; | ||
121 | op[k] = nc > 9 ? nc + ('a' - 10) | ||
122 | : nc + '0'; | ||
123 | ec >>= 4; | ||
124 | } | ||
125 | op += 5; | ||
126 | } else { | ||
127 | *op++ = '?'; | ||
128 | } | ||
129 | } | ||
130 | /* We have some slack there, so it's OK */ | ||
131 | if (op>ascii+256) { | ||
132 | op = ascii + 256; | ||
133 | break; | ||
134 | } | ||
135 | } | ||
136 | *op = 0; | ||
137 | return (op - ascii); | ||
138 | } | ||
139 | |||
140 | static inline int | ||
141 | fat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) | ||
142 | { | ||
143 | int charlen; | ||
144 | |||
145 | charlen = t->char2uni(c, clen, uni); | ||
146 | if (charlen < 0) { | ||
147 | *uni = 0x003f; /* a question mark */ | ||
148 | charlen = 1; | ||
149 | } | ||
150 | return charlen; | ||
151 | } | ||
152 | |||
153 | static inline int | ||
154 | fat_short2lower_uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) | ||
155 | { | ||
156 | int charlen; | ||
157 | wchar_t wc; | ||
158 | |||
159 | charlen = t->char2uni(c, clen, &wc); | ||
160 | if (charlen < 0) { | ||
161 | *uni = 0x003f; /* a question mark */ | ||
162 | charlen = 1; | ||
163 | } else if (charlen <= 1) { | ||
164 | unsigned char nc = t->charset2lower[*c]; | ||
165 | |||
166 | if (!nc) | ||
167 | nc = *c; | ||
168 | |||
169 | if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) { | ||
170 | *uni = 0x003f; /* a question mark */ | ||
171 | charlen = 1; | ||
172 | } | ||
173 | } else | ||
174 | *uni = wc; | ||
175 | |||
176 | return charlen; | ||
177 | } | ||
178 | |||
179 | static inline int | ||
180 | fat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, | ||
181 | wchar_t *uni_buf, unsigned short opt, int lower) | ||
182 | { | ||
183 | int len = 0; | ||
184 | |||
185 | if (opt & VFAT_SFN_DISPLAY_LOWER) | ||
186 | len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); | ||
187 | else if (opt & VFAT_SFN_DISPLAY_WIN95) | ||
188 | len = fat_short2uni(nls, buf, buf_size, uni_buf); | ||
189 | else if (opt & VFAT_SFN_DISPLAY_WINNT) { | ||
190 | if (lower) | ||
191 | len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); | ||
192 | else | ||
193 | len = fat_short2uni(nls, buf, buf_size, uni_buf); | ||
194 | } else | ||
195 | len = fat_short2uni(nls, buf, buf_size, uni_buf); | ||
196 | |||
197 | return len; | ||
198 | } | ||
199 | |||
200 | /* | ||
201 | * Return values: negative -> error, 0 -> not found, positive -> found, | ||
202 | * value is the total amount of slots, including the shortname entry. | ||
203 | */ | ||
204 | int fat_search_long(struct inode *inode, const unsigned char *name, | ||
205 | int name_len, struct fat_slot_info *sinfo) | ||
206 | { | ||
207 | struct super_block *sb = inode->i_sb; | ||
208 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
209 | struct buffer_head *bh = NULL; | ||
210 | struct msdos_dir_entry *de; | ||
211 | struct nls_table *nls_io = sbi->nls_io; | ||
212 | struct nls_table *nls_disk = sbi->nls_disk; | ||
213 | wchar_t bufuname[14]; | ||
214 | unsigned char xlate_len, nr_slots; | ||
215 | wchar_t *unicode = NULL; | ||
216 | unsigned char work[8], bufname[260]; /* 256 + 4 */ | ||
217 | int uni_xlate = sbi->options.unicode_xlate; | ||
218 | int utf8 = sbi->options.utf8; | ||
219 | int anycase = (sbi->options.name_check != 's'); | ||
220 | unsigned short opt_shortname = sbi->options.shortname; | ||
221 | loff_t cpos = 0; | ||
222 | int chl, i, j, last_u, err; | ||
223 | |||
224 | err = -ENOENT; | ||
225 | while(1) { | ||
226 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | ||
227 | goto EODir; | ||
228 | parse_record: | ||
229 | nr_slots = 0; | ||
230 | if (de->name[0] == DELETED_FLAG) | ||
231 | continue; | ||
232 | if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) | ||
233 | continue; | ||
234 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) | ||
235 | continue; | ||
236 | if (de->attr == ATTR_EXT) { | ||
237 | struct msdos_dir_slot *ds; | ||
238 | unsigned char id; | ||
239 | unsigned char slot; | ||
240 | unsigned char slots; | ||
241 | unsigned char sum; | ||
242 | unsigned char alias_checksum; | ||
243 | |||
244 | if (!unicode) { | ||
245 | unicode = (wchar_t *) | ||
246 | __get_free_page(GFP_KERNEL); | ||
247 | if (!unicode) { | ||
248 | brelse(bh); | ||
249 | return -ENOMEM; | ||
250 | } | ||
251 | } | ||
252 | parse_long: | ||
253 | slots = 0; | ||
254 | ds = (struct msdos_dir_slot *) de; | ||
255 | id = ds->id; | ||
256 | if (!(id & 0x40)) | ||
257 | continue; | ||
258 | slots = id & ~0x40; | ||
259 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
260 | continue; | ||
261 | nr_slots = slots; | ||
262 | alias_checksum = ds->alias_checksum; | ||
263 | |||
264 | slot = slots; | ||
265 | while (1) { | ||
266 | int offset; | ||
267 | |||
268 | slot--; | ||
269 | offset = slot * 13; | ||
270 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
271 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
272 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
273 | |||
274 | if (ds->id & 0x40) { | ||
275 | unicode[offset + 13] = 0; | ||
276 | } | ||
277 | if (fat_get_entry(inode, &cpos, &bh, &de) < 0) | ||
278 | goto EODir; | ||
279 | if (slot == 0) | ||
280 | break; | ||
281 | ds = (struct msdos_dir_slot *) de; | ||
282 | if (ds->attr != ATTR_EXT) | ||
283 | goto parse_record; | ||
284 | if ((ds->id & ~0x40) != slot) | ||
285 | goto parse_long; | ||
286 | if (ds->alias_checksum != alias_checksum) | ||
287 | goto parse_long; | ||
288 | } | ||
289 | if (de->name[0] == DELETED_FLAG) | ||
290 | continue; | ||
291 | if (de->attr == ATTR_EXT) | ||
292 | goto parse_long; | ||
293 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
294 | continue; | ||
295 | for (sum = 0, i = 0; i < 11; i++) | ||
296 | sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; | ||
297 | if (sum != alias_checksum) | ||
298 | nr_slots = 0; | ||
299 | } | ||
300 | |||
301 | memcpy(work, de->name, sizeof(de->name)); | ||
302 | /* see namei.c, msdos_format_name */ | ||
303 | if (work[0] == 0x05) | ||
304 | work[0] = 0xE5; | ||
305 | for (i = 0, j = 0, last_u = 0; i < 8;) { | ||
306 | if (!work[i]) break; | ||
307 | chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, | ||
308 | &bufuname[j++], opt_shortname, | ||
309 | de->lcase & CASE_LOWER_BASE); | ||
310 | if (chl <= 1) { | ||
311 | if (work[i] != ' ') | ||
312 | last_u = j; | ||
313 | } else { | ||
314 | last_u = j; | ||
315 | } | ||
316 | i += chl; | ||
317 | } | ||
318 | j = last_u; | ||
319 | fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); | ||
320 | for (i = 0; i < 3;) { | ||
321 | if (!de->ext[i]) break; | ||
322 | chl = fat_shortname2uni(nls_disk, &de->ext[i], 3 - i, | ||
323 | &bufuname[j++], opt_shortname, | ||
324 | de->lcase & CASE_LOWER_EXT); | ||
325 | if (chl <= 1) { | ||
326 | if (de->ext[i] != ' ') | ||
327 | last_u = j; | ||
328 | } else { | ||
329 | last_u = j; | ||
330 | } | ||
331 | i += chl; | ||
332 | } | ||
333 | if (!last_u) | ||
334 | continue; | ||
335 | |||
336 | bufuname[last_u] = 0x0000; | ||
337 | xlate_len = utf8 | ||
338 | ?utf8_wcstombs(bufname, bufuname, sizeof(bufname)) | ||
339 | :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); | ||
340 | if (xlate_len == name_len) | ||
341 | if ((!anycase && !memcmp(name, bufname, xlate_len)) || | ||
342 | (anycase && !nls_strnicmp(nls_io, name, bufname, | ||
343 | xlate_len))) | ||
344 | goto Found; | ||
345 | |||
346 | if (nr_slots) { | ||
347 | xlate_len = utf8 | ||
348 | ?utf8_wcstombs(bufname, unicode, sizeof(bufname)) | ||
349 | :uni16_to_x8(bufname, unicode, uni_xlate, nls_io); | ||
350 | if (xlate_len != name_len) | ||
351 | continue; | ||
352 | if ((!anycase && !memcmp(name, bufname, xlate_len)) || | ||
353 | (anycase && !nls_strnicmp(nls_io, name, bufname, | ||
354 | xlate_len))) | ||
355 | goto Found; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | Found: | ||
360 | nr_slots++; /* include the de */ | ||
361 | sinfo->slot_off = cpos - nr_slots * sizeof(*de); | ||
362 | sinfo->nr_slots = nr_slots; | ||
363 | sinfo->de = de; | ||
364 | sinfo->bh = bh; | ||
365 | sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); | ||
366 | err = 0; | ||
367 | EODir: | ||
368 | if (unicode) | ||
369 | free_page((unsigned long)unicode); | ||
370 | |||
371 | return err; | ||
372 | } | ||
373 | |||
374 | EXPORT_SYMBOL(fat_search_long); | ||
375 | |||
376 | struct fat_ioctl_filldir_callback { | ||
377 | struct dirent __user *dirent; | ||
378 | int result; | ||
379 | /* for dir ioctl */ | ||
380 | const char *longname; | ||
381 | int long_len; | ||
382 | const char *shortname; | ||
383 | int short_len; | ||
384 | }; | ||
385 | |||
386 | static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent, | ||
387 | filldir_t filldir, int short_only, int both) | ||
388 | { | ||
389 | struct super_block *sb = inode->i_sb; | ||
390 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
391 | struct buffer_head *bh; | ||
392 | struct msdos_dir_entry *de; | ||
393 | struct nls_table *nls_io = sbi->nls_io; | ||
394 | struct nls_table *nls_disk = sbi->nls_disk; | ||
395 | unsigned char long_slots; | ||
396 | const char *fill_name; | ||
397 | int fill_len; | ||
398 | wchar_t bufuname[14]; | ||
399 | wchar_t *unicode = NULL; | ||
400 | unsigned char c, work[8], bufname[56], *ptname = bufname; | ||
401 | unsigned long lpos, dummy, *furrfu = &lpos; | ||
402 | int uni_xlate = sbi->options.unicode_xlate; | ||
403 | int isvfat = sbi->options.isvfat; | ||
404 | int utf8 = sbi->options.utf8; | ||
405 | int nocase = sbi->options.nocase; | ||
406 | unsigned short opt_shortname = sbi->options.shortname; | ||
407 | unsigned long inum; | ||
408 | int chi, chl, i, i2, j, last, last_u, dotoffset = 0; | ||
409 | loff_t cpos; | ||
410 | int ret = 0; | ||
411 | |||
412 | lock_kernel(); | ||
413 | |||
414 | cpos = filp->f_pos; | ||
415 | /* Fake . and .. for the root directory. */ | ||
416 | if (inode->i_ino == MSDOS_ROOT_INO) { | ||
417 | while (cpos < 2) { | ||
418 | if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0) | ||
419 | goto out; | ||
420 | cpos++; | ||
421 | filp->f_pos++; | ||
422 | } | ||
423 | if (cpos == 2) { | ||
424 | dummy = 2; | ||
425 | furrfu = &dummy; | ||
426 | cpos = 0; | ||
427 | } | ||
428 | } | ||
429 | if (cpos & (sizeof(struct msdos_dir_entry)-1)) { | ||
430 | ret = -ENOENT; | ||
431 | goto out; | ||
432 | } | ||
433 | |||
434 | bh = NULL; | ||
435 | GetNew: | ||
436 | long_slots = 0; | ||
437 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | ||
438 | goto EODir; | ||
439 | /* Check for long filename entry */ | ||
440 | if (isvfat) { | ||
441 | if (de->name[0] == DELETED_FLAG) | ||
442 | goto RecEnd; | ||
443 | if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) | ||
444 | goto RecEnd; | ||
445 | if (de->attr != ATTR_EXT && IS_FREE(de->name)) | ||
446 | goto RecEnd; | ||
447 | } else { | ||
448 | if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) | ||
449 | goto RecEnd; | ||
450 | } | ||
451 | |||
452 | if (isvfat && de->attr == ATTR_EXT) { | ||
453 | struct msdos_dir_slot *ds; | ||
454 | unsigned char id; | ||
455 | unsigned char slot; | ||
456 | unsigned char slots; | ||
457 | unsigned char sum; | ||
458 | unsigned char alias_checksum; | ||
459 | |||
460 | if (!unicode) { | ||
461 | unicode = (wchar_t *)__get_free_page(GFP_KERNEL); | ||
462 | if (!unicode) { | ||
463 | filp->f_pos = cpos; | ||
464 | brelse(bh); | ||
465 | ret = -ENOMEM; | ||
466 | goto out; | ||
467 | } | ||
468 | } | ||
469 | ParseLong: | ||
470 | slots = 0; | ||
471 | ds = (struct msdos_dir_slot *) de; | ||
472 | id = ds->id; | ||
473 | if (!(id & 0x40)) | ||
474 | goto RecEnd; | ||
475 | slots = id & ~0x40; | ||
476 | if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ | ||
477 | goto RecEnd; | ||
478 | long_slots = slots; | ||
479 | alias_checksum = ds->alias_checksum; | ||
480 | |||
481 | slot = slots; | ||
482 | while (1) { | ||
483 | int offset; | ||
484 | |||
485 | slot--; | ||
486 | offset = slot * 13; | ||
487 | fat16_towchar(unicode + offset, ds->name0_4, 5); | ||
488 | fat16_towchar(unicode + offset + 5, ds->name5_10, 6); | ||
489 | fat16_towchar(unicode + offset + 11, ds->name11_12, 2); | ||
490 | |||
491 | if (ds->id & 0x40) { | ||
492 | unicode[offset + 13] = 0; | ||
493 | } | ||
494 | if (fat_get_entry(inode, &cpos, &bh, &de) == -1) | ||
495 | goto EODir; | ||
496 | if (slot == 0) | ||
497 | break; | ||
498 | ds = (struct msdos_dir_slot *) de; | ||
499 | if (ds->attr != ATTR_EXT) | ||
500 | goto RecEnd; /* XXX */ | ||
501 | if ((ds->id & ~0x40) != slot) | ||
502 | goto ParseLong; | ||
503 | if (ds->alias_checksum != alias_checksum) | ||
504 | goto ParseLong; | ||
505 | } | ||
506 | if (de->name[0] == DELETED_FLAG) | ||
507 | goto RecEnd; | ||
508 | if (de->attr == ATTR_EXT) | ||
509 | goto ParseLong; | ||
510 | if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME)) | ||
511 | goto RecEnd; | ||
512 | for (sum = 0, i = 0; i < 11; i++) | ||
513 | sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i]; | ||
514 | if (sum != alias_checksum) | ||
515 | long_slots = 0; | ||
516 | } | ||
517 | |||
518 | if (sbi->options.dotsOK) { | ||
519 | ptname = bufname; | ||
520 | dotoffset = 0; | ||
521 | if (de->attr & ATTR_HIDDEN) { | ||
522 | *ptname++ = '.'; | ||
523 | dotoffset = 1; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | memcpy(work, de->name, sizeof(de->name)); | ||
528 | /* see namei.c, msdos_format_name */ | ||
529 | if (work[0] == 0x05) | ||
530 | work[0] = 0xE5; | ||
531 | for (i = 0, j = 0, last = 0, last_u = 0; i < 8;) { | ||
532 | if (!(c = work[i])) break; | ||
533 | chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, | ||
534 | &bufuname[j++], opt_shortname, | ||
535 | de->lcase & CASE_LOWER_BASE); | ||
536 | if (chl <= 1) { | ||
537 | ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; | ||
538 | if (c != ' ') { | ||
539 | last = i; | ||
540 | last_u = j; | ||
541 | } | ||
542 | } else { | ||
543 | last_u = j; | ||
544 | for (chi = 0; chi < chl && i < 8; chi++) { | ||
545 | ptname[i] = work[i]; | ||
546 | i++; last = i; | ||
547 | } | ||
548 | } | ||
549 | } | ||
550 | i = last; | ||
551 | j = last_u; | ||
552 | fat_short2uni(nls_disk, ".", 1, &bufuname[j++]); | ||
553 | ptname[i++] = '.'; | ||
554 | for (i2 = 0; i2 < 3;) { | ||
555 | if (!(c = de->ext[i2])) break; | ||
556 | chl = fat_shortname2uni(nls_disk, &de->ext[i2], 3 - i2, | ||
557 | &bufuname[j++], opt_shortname, | ||
558 | de->lcase & CASE_LOWER_EXT); | ||
559 | if (chl <= 1) { | ||
560 | i2++; | ||
561 | ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c; | ||
562 | if (c != ' ') { | ||
563 | last = i; | ||
564 | last_u = j; | ||
565 | } | ||
566 | } else { | ||
567 | last_u = j; | ||
568 | for (chi = 0; chi < chl && i2 < 3; chi++) { | ||
569 | ptname[i++] = de->ext[i2++]; | ||
570 | last = i; | ||
571 | } | ||
572 | } | ||
573 | } | ||
574 | if (!last) | ||
575 | goto RecEnd; | ||
576 | |||
577 | i = last + dotoffset; | ||
578 | j = last_u; | ||
579 | |||
580 | lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry); | ||
581 | if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) | ||
582 | inum = inode->i_ino; | ||
583 | else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { | ||
584 | inum = parent_ino(filp->f_dentry); | ||
585 | } else { | ||
586 | loff_t i_pos = fat_make_i_pos(sb, bh, de); | ||
587 | struct inode *tmp = fat_iget(sb, i_pos); | ||
588 | if (tmp) { | ||
589 | inum = tmp->i_ino; | ||
590 | iput(tmp); | ||
591 | } else | ||
592 | inum = iunique(sb, MSDOS_ROOT_INO); | ||
593 | } | ||
594 | |||
595 | if (isvfat) { | ||
596 | bufuname[j] = 0x0000; | ||
597 | i = utf8 ? utf8_wcstombs(bufname, bufuname, sizeof(bufname)) | ||
598 | : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io); | ||
599 | } | ||
600 | |||
601 | fill_name = bufname; | ||
602 | fill_len = i; | ||
603 | if (!short_only && long_slots) { | ||
604 | /* convert the unicode long name. 261 is maximum size | ||
605 | * of unicode buffer. (13 * slots + nul) */ | ||
606 | void *longname = unicode + 261; | ||
607 | int buf_size = PAGE_SIZE - (261 * sizeof(unicode[0])); | ||
608 | int long_len = utf8 | ||
609 | ? utf8_wcstombs(longname, unicode, buf_size) | ||
610 | : uni16_to_x8(longname, unicode, uni_xlate, nls_io); | ||
611 | |||
612 | if (!both) { | ||
613 | fill_name = longname; | ||
614 | fill_len = long_len; | ||
615 | } else { | ||
616 | /* hack for fat_ioctl_filldir() */ | ||
617 | struct fat_ioctl_filldir_callback *p = dirent; | ||
618 | |||
619 | p->longname = longname; | ||
620 | p->long_len = long_len; | ||
621 | p->shortname = bufname; | ||
622 | p->short_len = i; | ||
623 | fill_name = NULL; | ||
624 | fill_len = 0; | ||
625 | } | ||
626 | } | ||
627 | if (filldir(dirent, fill_name, fill_len, *furrfu, inum, | ||
628 | (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0) | ||
629 | goto FillFailed; | ||
630 | |||
631 | RecEnd: | ||
632 | furrfu = &lpos; | ||
633 | filp->f_pos = cpos; | ||
634 | goto GetNew; | ||
635 | EODir: | ||
636 | filp->f_pos = cpos; | ||
637 | FillFailed: | ||
638 | if (bh) | ||
639 | brelse(bh); | ||
640 | if (unicode) | ||
641 | free_page((unsigned long)unicode); | ||
642 | out: | ||
643 | unlock_kernel(); | ||
644 | return ret; | ||
645 | } | ||
646 | |||
647 | static int fat_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
648 | { | ||
649 | struct inode *inode = filp->f_dentry->d_inode; | ||
650 | return fat_readdirx(inode, filp, dirent, filldir, 0, 0); | ||
651 | } | ||
652 | |||
653 | static int fat_ioctl_filldir(void *__buf, const char *name, int name_len, | ||
654 | loff_t offset, ino_t ino, unsigned int d_type) | ||
655 | { | ||
656 | struct fat_ioctl_filldir_callback *buf = __buf; | ||
657 | struct dirent __user *d1 = buf->dirent; | ||
658 | struct dirent __user *d2 = d1 + 1; | ||
659 | |||
660 | if (buf->result) | ||
661 | return -EINVAL; | ||
662 | buf->result++; | ||
663 | |||
664 | if (name != NULL) { | ||
665 | /* dirent has only short name */ | ||
666 | if (name_len >= sizeof(d1->d_name)) | ||
667 | name_len = sizeof(d1->d_name) - 1; | ||
668 | |||
669 | if (put_user(0, d2->d_name) || | ||
670 | put_user(0, &d2->d_reclen) || | ||
671 | copy_to_user(d1->d_name, name, name_len) || | ||
672 | put_user(0, d1->d_name + name_len) || | ||
673 | put_user(name_len, &d1->d_reclen)) | ||
674 | goto efault; | ||
675 | } else { | ||
676 | /* dirent has short and long name */ | ||
677 | const char *longname = buf->longname; | ||
678 | int long_len = buf->long_len; | ||
679 | const char *shortname = buf->shortname; | ||
680 | int short_len = buf->short_len; | ||
681 | |||
682 | if (long_len >= sizeof(d1->d_name)) | ||
683 | long_len = sizeof(d1->d_name) - 1; | ||
684 | if (short_len >= sizeof(d1->d_name)) | ||
685 | short_len = sizeof(d1->d_name) - 1; | ||
686 | |||
687 | if (copy_to_user(d2->d_name, longname, long_len) || | ||
688 | put_user(0, d2->d_name + long_len) || | ||
689 | put_user(long_len, &d2->d_reclen) || | ||
690 | put_user(ino, &d2->d_ino) || | ||
691 | put_user(offset, &d2->d_off) || | ||
692 | copy_to_user(d1->d_name, shortname, short_len) || | ||
693 | put_user(0, d1->d_name + short_len) || | ||
694 | put_user(short_len, &d1->d_reclen)) | ||
695 | goto efault; | ||
696 | } | ||
697 | return 0; | ||
698 | efault: | ||
699 | buf->result = -EFAULT; | ||
700 | return -EFAULT; | ||
701 | } | ||
702 | |||
703 | static int fat_dir_ioctl(struct inode * inode, struct file * filp, | ||
704 | unsigned int cmd, unsigned long arg) | ||
705 | { | ||
706 | struct fat_ioctl_filldir_callback buf; | ||
707 | struct dirent __user *d1; | ||
708 | int ret, short_only, both; | ||
709 | |||
710 | switch (cmd) { | ||
711 | case VFAT_IOCTL_READDIR_SHORT: | ||
712 | short_only = 1; | ||
713 | both = 0; | ||
714 | break; | ||
715 | case VFAT_IOCTL_READDIR_BOTH: | ||
716 | short_only = 0; | ||
717 | both = 1; | ||
718 | break; | ||
719 | default: | ||
720 | return fat_generic_ioctl(inode, filp, cmd, arg); | ||
721 | } | ||
722 | |||
723 | d1 = (struct dirent __user *)arg; | ||
724 | if (!access_ok(VERIFY_WRITE, d1, sizeof(struct dirent[2]))) | ||
725 | return -EFAULT; | ||
726 | /* | ||
727 | * Yes, we don't need this put_user() absolutely. However old | ||
728 | * code didn't return the right value. So, app use this value, | ||
729 | * in order to check whether it is EOF. | ||
730 | */ | ||
731 | if (put_user(0, &d1->d_reclen)) | ||
732 | return -EFAULT; | ||
733 | |||
734 | buf.dirent = d1; | ||
735 | buf.result = 0; | ||
736 | down(&inode->i_sem); | ||
737 | ret = -ENOENT; | ||
738 | if (!IS_DEADDIR(inode)) { | ||
739 | ret = fat_readdirx(inode, filp, &buf, fat_ioctl_filldir, | ||
740 | short_only, both); | ||
741 | } | ||
742 | up(&inode->i_sem); | ||
743 | if (ret >= 0) | ||
744 | ret = buf.result; | ||
745 | return ret; | ||
746 | } | ||
747 | |||
748 | struct file_operations fat_dir_operations = { | ||
749 | .read = generic_read_dir, | ||
750 | .readdir = fat_readdir, | ||
751 | .ioctl = fat_dir_ioctl, | ||
752 | .fsync = file_fsync, | ||
753 | }; | ||
754 | |||
755 | static int fat_get_short_entry(struct inode *dir, loff_t *pos, | ||
756 | struct buffer_head **bh, | ||
757 | struct msdos_dir_entry **de) | ||
758 | { | ||
759 | while (fat_get_entry(dir, pos, bh, de) >= 0) { | ||
760 | /* free entry or long name entry or volume label */ | ||
761 | if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME)) | ||
762 | return 0; | ||
763 | } | ||
764 | return -ENOENT; | ||
765 | } | ||
766 | |||
767 | /* | ||
768 | * The ".." entry can not provide the "struct fat_slot_info" informations | ||
769 | * for inode. So, this function provide the some informations only. | ||
770 | */ | ||
771 | int fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, | ||
772 | struct msdos_dir_entry **de, loff_t *i_pos) | ||
773 | { | ||
774 | loff_t offset; | ||
775 | |||
776 | offset = 0; | ||
777 | *bh = NULL; | ||
778 | while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { | ||
779 | if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) { | ||
780 | *i_pos = fat_make_i_pos(dir->i_sb, *bh, *de); | ||
781 | return 0; | ||
782 | } | ||
783 | } | ||
784 | return -ENOENT; | ||
785 | } | ||
786 | |||
787 | EXPORT_SYMBOL(fat_get_dotdot_entry); | ||
788 | |||
789 | /* See if directory is empty */ | ||
790 | int fat_dir_empty(struct inode *dir) | ||
791 | { | ||
792 | struct buffer_head *bh; | ||
793 | struct msdos_dir_entry *de; | ||
794 | loff_t cpos; | ||
795 | int result = 0; | ||
796 | |||
797 | bh = NULL; | ||
798 | cpos = 0; | ||
799 | while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) { | ||
800 | if (strncmp(de->name, MSDOS_DOT , MSDOS_NAME) && | ||
801 | strncmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { | ||
802 | result = -ENOTEMPTY; | ||
803 | break; | ||
804 | } | ||
805 | } | ||
806 | brelse(bh); | ||
807 | return result; | ||
808 | } | ||
809 | |||
810 | EXPORT_SYMBOL(fat_dir_empty); | ||
811 | |||
812 | /* | ||
813 | * fat_subdirs counts the number of sub-directories of dir. It can be run | ||
814 | * on directories being created. | ||
815 | */ | ||
816 | int fat_subdirs(struct inode *dir) | ||
817 | { | ||
818 | struct buffer_head *bh; | ||
819 | struct msdos_dir_entry *de; | ||
820 | loff_t cpos; | ||
821 | int count = 0; | ||
822 | |||
823 | bh = NULL; | ||
824 | cpos = 0; | ||
825 | while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) { | ||
826 | if (de->attr & ATTR_DIR) | ||
827 | count++; | ||
828 | } | ||
829 | brelse(bh); | ||
830 | return count; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Scans a directory for a given file (name points to its formatted name). | ||
835 | * Returns an error code or zero. | ||
836 | */ | ||
837 | int fat_scan(struct inode *dir, const unsigned char *name, | ||
838 | struct fat_slot_info *sinfo) | ||
839 | { | ||
840 | struct super_block *sb = dir->i_sb; | ||
841 | |||
842 | sinfo->slot_off = 0; | ||
843 | sinfo->bh = NULL; | ||
844 | while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, | ||
845 | &sinfo->de) >= 0) { | ||
846 | if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) { | ||
847 | sinfo->slot_off -= sizeof(*sinfo->de); | ||
848 | sinfo->nr_slots = 1; | ||
849 | sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); | ||
850 | return 0; | ||
851 | } | ||
852 | } | ||
853 | return -ENOENT; | ||
854 | } | ||
855 | |||
856 | EXPORT_SYMBOL(fat_scan); | ||
857 | |||
858 | static int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) | ||
859 | { | ||
860 | struct super_block *sb = dir->i_sb; | ||
861 | struct buffer_head *bh; | ||
862 | struct msdos_dir_entry *de, *endp; | ||
863 | int err = 0, orig_slots; | ||
864 | |||
865 | while (nr_slots) { | ||
866 | bh = NULL; | ||
867 | if (fat_get_entry(dir, &pos, &bh, &de) < 0) { | ||
868 | err = -EIO; | ||
869 | break; | ||
870 | } | ||
871 | |||
872 | orig_slots = nr_slots; | ||
873 | endp = (struct msdos_dir_entry *)(bh->b_data + sb->s_blocksize); | ||
874 | while (nr_slots && de < endp) { | ||
875 | de->name[0] = DELETED_FLAG; | ||
876 | de++; | ||
877 | nr_slots--; | ||
878 | } | ||
879 | mark_buffer_dirty(bh); | ||
880 | if (IS_DIRSYNC(dir)) | ||
881 | err = sync_dirty_buffer(bh); | ||
882 | brelse(bh); | ||
883 | if (err) | ||
884 | break; | ||
885 | |||
886 | /* pos is *next* de's position, so this does `- sizeof(de)' */ | ||
887 | pos += ((orig_slots - nr_slots) * sizeof(*de)) - sizeof(*de); | ||
888 | } | ||
889 | |||
890 | return err; | ||
891 | } | ||
892 | |||
893 | int fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) | ||
894 | { | ||
895 | struct msdos_dir_entry *de; | ||
896 | struct buffer_head *bh; | ||
897 | int err = 0, nr_slots; | ||
898 | |||
899 | /* | ||
900 | * First stage: Remove the shortname. By this, the directory | ||
901 | * entry is removed. | ||
902 | */ | ||
903 | nr_slots = sinfo->nr_slots; | ||
904 | de = sinfo->de; | ||
905 | sinfo->de = NULL; | ||
906 | bh = sinfo->bh; | ||
907 | sinfo->bh = NULL; | ||
908 | while (nr_slots && de >= (struct msdos_dir_entry *)bh->b_data) { | ||
909 | de->name[0] = DELETED_FLAG; | ||
910 | de--; | ||
911 | nr_slots--; | ||
912 | } | ||
913 | mark_buffer_dirty(bh); | ||
914 | if (IS_DIRSYNC(dir)) | ||
915 | err = sync_dirty_buffer(bh); | ||
916 | brelse(bh); | ||
917 | if (err) | ||
918 | return err; | ||
919 | dir->i_version++; | ||
920 | |||
921 | if (nr_slots) { | ||
922 | /* | ||
923 | * Second stage: remove the remaining longname slots. | ||
924 | * (This directory entry is already removed, and so return | ||
925 | * the success) | ||
926 | */ | ||
927 | err = __fat_remove_entries(dir, sinfo->slot_off, nr_slots); | ||
928 | if (err) { | ||
929 | printk(KERN_WARNING | ||
930 | "FAT: Couldn't remove the long name slots\n"); | ||
931 | } | ||
932 | } | ||
933 | |||
934 | dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC; | ||
935 | if (IS_DIRSYNC(dir)) | ||
936 | (void)fat_sync_inode(dir); | ||
937 | else | ||
938 | mark_inode_dirty(dir); | ||
939 | |||
940 | return 0; | ||
941 | } | ||
942 | |||
943 | EXPORT_SYMBOL(fat_remove_entries); | ||
944 | |||
945 | static int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, | ||
946 | struct buffer_head **bhs, int nr_bhs) | ||
947 | { | ||
948 | struct super_block *sb = dir->i_sb; | ||
949 | sector_t last_blknr = blknr + MSDOS_SB(sb)->sec_per_clus; | ||
950 | int err, i, n; | ||
951 | |||
952 | /* Zeroing the unused blocks on this cluster */ | ||
953 | blknr += nr_used; | ||
954 | n = nr_used; | ||
955 | while (blknr < last_blknr) { | ||
956 | bhs[n] = sb_getblk(sb, blknr); | ||
957 | if (!bhs[n]) { | ||
958 | err = -ENOMEM; | ||
959 | goto error; | ||
960 | } | ||
961 | memset(bhs[n]->b_data, 0, sb->s_blocksize); | ||
962 | set_buffer_uptodate(bhs[n]); | ||
963 | mark_buffer_dirty(bhs[n]); | ||
964 | |||
965 | n++; | ||
966 | blknr++; | ||
967 | if (n == nr_bhs) { | ||
968 | if (IS_DIRSYNC(dir)) { | ||
969 | err = fat_sync_bhs(bhs, n); | ||
970 | if (err) | ||
971 | goto error; | ||
972 | } | ||
973 | for (i = 0; i < n; i++) | ||
974 | brelse(bhs[i]); | ||
975 | n = 0; | ||
976 | } | ||
977 | } | ||
978 | if (IS_DIRSYNC(dir)) { | ||
979 | err = fat_sync_bhs(bhs, n); | ||
980 | if (err) | ||
981 | goto error; | ||
982 | } | ||
983 | for (i = 0; i < n; i++) | ||
984 | brelse(bhs[i]); | ||
985 | |||
986 | return 0; | ||
987 | |||
988 | error: | ||
989 | for (i = 0; i < n; i++) | ||
990 | bforget(bhs[i]); | ||
991 | return err; | ||
992 | } | ||
993 | |||
994 | int fat_alloc_new_dir(struct inode *dir, struct timespec *ts) | ||
995 | { | ||
996 | struct super_block *sb = dir->i_sb; | ||
997 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
998 | struct buffer_head *bhs[MAX_BUF_PER_PAGE]; | ||
999 | struct msdos_dir_entry *de; | ||
1000 | sector_t blknr; | ||
1001 | __le16 date, time; | ||
1002 | int err, cluster; | ||
1003 | |||
1004 | err = fat_alloc_clusters(dir, &cluster, 1); | ||
1005 | if (err) | ||
1006 | goto error; | ||
1007 | |||
1008 | blknr = fat_clus_to_blknr(sbi, cluster); | ||
1009 | bhs[0] = sb_getblk(sb, blknr); | ||
1010 | if (!bhs[0]) { | ||
1011 | err = -ENOMEM; | ||
1012 | goto error_free; | ||
1013 | } | ||
1014 | |||
1015 | fat_date_unix2dos(ts->tv_sec, &time, &date); | ||
1016 | |||
1017 | de = (struct msdos_dir_entry *)bhs[0]->b_data; | ||
1018 | /* filling the new directory slots ("." and ".." entries) */ | ||
1019 | memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); | ||
1020 | memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); | ||
1021 | de->attr = de[1].attr = ATTR_DIR; | ||
1022 | de[0].lcase = de[1].lcase = 0; | ||
1023 | de[0].time = de[1].time = time; | ||
1024 | de[0].date = de[1].date = date; | ||
1025 | de[0].ctime_cs = de[1].ctime_cs = 0; | ||
1026 | if (sbi->options.isvfat) { | ||
1027 | /* extra timestamps */ | ||
1028 | de[0].ctime = de[1].ctime = time; | ||
1029 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; | ||
1030 | } else { | ||
1031 | de[0].ctime = de[1].ctime = 0; | ||
1032 | de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; | ||
1033 | } | ||
1034 | de[0].start = cpu_to_le16(cluster); | ||
1035 | de[0].starthi = cpu_to_le16(cluster >> 16); | ||
1036 | de[1].start = cpu_to_le16(MSDOS_I(dir)->i_logstart); | ||
1037 | de[1].starthi = cpu_to_le16(MSDOS_I(dir)->i_logstart >> 16); | ||
1038 | de[0].size = de[1].size = 0; | ||
1039 | memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); | ||
1040 | set_buffer_uptodate(bhs[0]); | ||
1041 | mark_buffer_dirty(bhs[0]); | ||
1042 | |||
1043 | err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); | ||
1044 | if (err) | ||
1045 | goto error_free; | ||
1046 | |||
1047 | return cluster; | ||
1048 | |||
1049 | error_free: | ||
1050 | fat_free_clusters(dir, cluster); | ||
1051 | error: | ||
1052 | return err; | ||
1053 | } | ||
1054 | |||
1055 | EXPORT_SYMBOL(fat_alloc_new_dir); | ||
1056 | |||
1057 | static int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, | ||
1058 | int *nr_cluster, struct msdos_dir_entry **de, | ||
1059 | struct buffer_head **bh, loff_t *i_pos) | ||
1060 | { | ||
1061 | struct super_block *sb = dir->i_sb; | ||
1062 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
1063 | struct buffer_head *bhs[MAX_BUF_PER_PAGE]; | ||
1064 | sector_t blknr, start_blknr, last_blknr; | ||
1065 | unsigned long size, copy; | ||
1066 | int err, i, n, offset, cluster[2]; | ||
1067 | |||
1068 | /* | ||
1069 | * The minimum cluster size is 512bytes, and maximum entry | ||
1070 | * size is 32*slots (672bytes). So, iff the cluster size is | ||
1071 | * 512bytes, we may need two clusters. | ||
1072 | */ | ||
1073 | size = nr_slots * sizeof(struct msdos_dir_entry); | ||
1074 | *nr_cluster = (size + (sbi->cluster_size - 1)) >> sbi->cluster_bits; | ||
1075 | BUG_ON(*nr_cluster > 2); | ||
1076 | |||
1077 | err = fat_alloc_clusters(dir, cluster, *nr_cluster); | ||
1078 | if (err) | ||
1079 | goto error; | ||
1080 | |||
1081 | /* | ||
1082 | * First stage: Fill the directory entry. NOTE: This cluster | ||
1083 | * is not referenced from any inode yet, so updates order is | ||
1084 | * not important. | ||
1085 | */ | ||
1086 | i = n = copy = 0; | ||
1087 | do { | ||
1088 | start_blknr = blknr = fat_clus_to_blknr(sbi, cluster[i]); | ||
1089 | last_blknr = start_blknr + sbi->sec_per_clus; | ||
1090 | while (blknr < last_blknr) { | ||
1091 | bhs[n] = sb_getblk(sb, blknr); | ||
1092 | if (!bhs[n]) { | ||
1093 | err = -ENOMEM; | ||
1094 | goto error_nomem; | ||
1095 | } | ||
1096 | |||
1097 | /* fill the directory entry */ | ||
1098 | copy = min(size, sb->s_blocksize); | ||
1099 | memcpy(bhs[n]->b_data, slots, copy); | ||
1100 | slots += copy; | ||
1101 | size -= copy; | ||
1102 | set_buffer_uptodate(bhs[n]); | ||
1103 | mark_buffer_dirty(bhs[n]); | ||
1104 | if (!size) | ||
1105 | break; | ||
1106 | n++; | ||
1107 | blknr++; | ||
1108 | } | ||
1109 | } while (++i < *nr_cluster); | ||
1110 | |||
1111 | memset(bhs[n]->b_data + copy, 0, sb->s_blocksize - copy); | ||
1112 | offset = copy - sizeof(struct msdos_dir_entry); | ||
1113 | get_bh(bhs[n]); | ||
1114 | *bh = bhs[n]; | ||
1115 | *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); | ||
1116 | *i_pos = fat_make_i_pos(sb, *bh, *de); | ||
1117 | |||
1118 | /* Second stage: clear the rest of cluster, and write outs */ | ||
1119 | err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE); | ||
1120 | if (err) | ||
1121 | goto error_free; | ||
1122 | |||
1123 | return cluster[0]; | ||
1124 | |||
1125 | error_free: | ||
1126 | brelse(*bh); | ||
1127 | *bh = NULL; | ||
1128 | n = 0; | ||
1129 | error_nomem: | ||
1130 | for (i = 0; i < n; i++) | ||
1131 | bforget(bhs[i]); | ||
1132 | fat_free_clusters(dir, cluster[0]); | ||
1133 | error: | ||
1134 | return err; | ||
1135 | } | ||
1136 | |||
1137 | int fat_add_entries(struct inode *dir, void *slots, int nr_slots, | ||
1138 | struct fat_slot_info *sinfo) | ||
1139 | { | ||
1140 | struct super_block *sb = dir->i_sb; | ||
1141 | struct msdos_sb_info *sbi = MSDOS_SB(sb); | ||
1142 | struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ | ||
1143 | struct msdos_dir_entry *de; | ||
1144 | int err, free_slots, i, nr_bhs; | ||
1145 | loff_t pos, i_pos; | ||
1146 | |||
1147 | sinfo->nr_slots = nr_slots; | ||
1148 | |||
1149 | /* First stage: search free direcotry entries */ | ||
1150 | free_slots = nr_bhs = 0; | ||
1151 | bh = prev = NULL; | ||
1152 | pos = 0; | ||
1153 | err = -ENOSPC; | ||
1154 | while (fat_get_entry(dir, &pos, &bh, &de) > -1) { | ||
1155 | /* check the maximum size of directory */ | ||
1156 | if (pos >= FAT_MAX_DIR_SIZE) | ||
1157 | goto error; | ||
1158 | |||
1159 | if (IS_FREE(de->name)) { | ||
1160 | if (prev != bh) { | ||
1161 | get_bh(bh); | ||
1162 | bhs[nr_bhs] = prev = bh; | ||
1163 | nr_bhs++; | ||
1164 | } | ||
1165 | free_slots++; | ||
1166 | if (free_slots == nr_slots) | ||
1167 | goto found; | ||
1168 | } else { | ||
1169 | for (i = 0; i < nr_bhs; i++) | ||
1170 | brelse(bhs[i]); | ||
1171 | prev = NULL; | ||
1172 | free_slots = nr_bhs = 0; | ||
1173 | } | ||
1174 | } | ||
1175 | if (dir->i_ino == MSDOS_ROOT_INO) { | ||
1176 | if (sbi->fat_bits != 32) | ||
1177 | goto error; | ||
1178 | } else if (MSDOS_I(dir)->i_start == 0) { | ||
1179 | printk(KERN_ERR "FAT: Corrupted directory (i_pos %lld)\n", | ||
1180 | MSDOS_I(dir)->i_pos); | ||
1181 | err = -EIO; | ||
1182 | goto error; | ||
1183 | } | ||
1184 | |||
1185 | found: | ||
1186 | err = 0; | ||
1187 | pos -= free_slots * sizeof(*de); | ||
1188 | nr_slots -= free_slots; | ||
1189 | if (free_slots) { | ||
1190 | /* | ||
1191 | * Second stage: filling the free entries with new entries. | ||
1192 | * NOTE: If this slots has shortname, first, we write | ||
1193 | * the long name slots, then write the short name. | ||
1194 | */ | ||
1195 | int size = free_slots * sizeof(*de); | ||
1196 | int offset = pos & (sb->s_blocksize - 1); | ||
1197 | int long_bhs = nr_bhs - (nr_slots == 0); | ||
1198 | |||
1199 | /* Fill the long name slots. */ | ||
1200 | for (i = 0; i < long_bhs; i++) { | ||
1201 | int copy = min_t(int, sb->s_blocksize - offset, size); | ||
1202 | memcpy(bhs[i]->b_data + offset, slots, copy); | ||
1203 | mark_buffer_dirty(bhs[i]); | ||
1204 | offset = 0; | ||
1205 | slots += copy; | ||
1206 | size -= copy; | ||
1207 | } | ||
1208 | if (long_bhs && IS_DIRSYNC(dir)) | ||
1209 | err = fat_sync_bhs(bhs, long_bhs); | ||
1210 | if (!err && i < nr_bhs) { | ||
1211 | /* Fill the short name slot. */ | ||
1212 | int copy = min_t(int, sb->s_blocksize - offset, size); | ||
1213 | memcpy(bhs[i]->b_data + offset, slots, copy); | ||
1214 | mark_buffer_dirty(bhs[i]); | ||
1215 | if (IS_DIRSYNC(dir)) | ||
1216 | err = sync_dirty_buffer(bhs[i]); | ||
1217 | } | ||
1218 | for (i = 0; i < nr_bhs; i++) | ||
1219 | brelse(bhs[i]); | ||
1220 | if (err) | ||
1221 | goto error_remove; | ||
1222 | } | ||
1223 | |||
1224 | if (nr_slots) { | ||
1225 | int cluster, nr_cluster; | ||
1226 | |||
1227 | /* | ||
1228 | * Third stage: allocate the cluster for new entries. | ||
1229 | * And initialize the cluster with new entries, then | ||
1230 | * add the cluster to dir. | ||
1231 | */ | ||
1232 | cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, | ||
1233 | &de, &bh, &i_pos); | ||
1234 | if (cluster < 0) { | ||
1235 | err = cluster; | ||
1236 | goto error_remove; | ||
1237 | } | ||
1238 | err = fat_chain_add(dir, cluster, nr_cluster); | ||
1239 | if (err) { | ||
1240 | fat_free_clusters(dir, cluster); | ||
1241 | goto error_remove; | ||
1242 | } | ||
1243 | if (dir->i_size & (sbi->cluster_size - 1)) { | ||
1244 | fat_fs_panic(sb, "Odd directory size"); | ||
1245 | dir->i_size = (dir->i_size + sbi->cluster_size - 1) | ||
1246 | & ~((loff_t)sbi->cluster_size - 1); | ||
1247 | } | ||
1248 | dir->i_size += nr_cluster << sbi->cluster_bits; | ||
1249 | MSDOS_I(dir)->mmu_private += nr_cluster << sbi->cluster_bits; | ||
1250 | } | ||
1251 | sinfo->slot_off = pos; | ||
1252 | sinfo->de = de; | ||
1253 | sinfo->bh = bh; | ||
1254 | sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); | ||
1255 | |||
1256 | return 0; | ||
1257 | |||
1258 | error: | ||
1259 | brelse(bh); | ||
1260 | for (i = 0; i < nr_bhs; i++) | ||
1261 | brelse(bhs[i]); | ||
1262 | return err; | ||
1263 | |||
1264 | error_remove: | ||
1265 | brelse(bh); | ||
1266 | if (free_slots) | ||
1267 | __fat_remove_entries(dir, pos, free_slots); | ||
1268 | return err; | ||
1269 | } | ||
1270 | |||
1271 | EXPORT_SYMBOL(fat_add_entries); | ||