- pre5:
[davej-history.git] / fs / fat / dir.c
blob272baa936422cf2f37cf43b2572af69fdaf35283
1 /*
2 * linux/fs/fat/dir.c
4 * directory handling functions for fat-based filesystems
6 * Written 1992,1993 by Werner Almesberger
8 * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
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 by Wolfram Pienkoss <wp@bszh.de>
16 #define ASC_LINUX_VERSION(V, P, S) (((V) * 65536) + ((P) * 256) + (S))
18 #include <linux/version.h>
19 #include <linux/fs.h>
20 #include <linux/msdos_fs.h>
21 #include <linux/nls.h>
22 #include <linux/kernel.h>
23 #include <linux/errno.h>
24 #include <linux/stat.h>
25 #include <linux/string.h>
26 #include <linux/ioctl.h>
27 #include <linux/dirent.h>
28 #include <linux/mm.h>
29 #include <linux/ctype.h>
31 #include <asm/uaccess.h>
33 #include "msbuffer.h"
35 #define PRINTK(X)
37 struct file_operations fat_dir_operations = {
38 read: generic_read_dir,
39 readdir: fat_readdir,
40 ioctl: fat_dir_ioctl,
41 fsync: file_fsync,
45 * Convert Unicode 16 to UTF8, translated Unicode, or ASCII.
46 * If uni_xlate is enabled and we can't get a 1:1 conversion, use a
47 * colon as an escape character since it is normally invalid on the vfat
48 * filesystem. The following four characters are the hexadecimal digits
49 * of Unicode value. This lets us do a full dump and restore of Unicode
50 * filenames. We could get into some trouble with long Unicode names,
51 * but ignore that right now.
52 * Ahem... Stack smashing in ring 0 isn't fun. Fixed.
54 static int
55 uni16_to_x8(unsigned char *ascii, wchar_t *uni, int uni_xlate,
56 struct nls_table *nls)
58 wchar_t *ip, ec;
59 unsigned char *op, nc;
60 int charlen;
61 int k;
63 ip = uni;
64 op = ascii;
66 while (*ip) {
67 ec = *ip++;
68 if ( (charlen = nls->uni2char(ec, op, 3)) > 0) {
69 op += charlen;
70 } else {
71 if (uni_xlate == 1) {
72 *op = ':';
73 for (k = 4; k > 0; k--) {
74 nc = ec & 0xF;
75 op[k] = nc > 9 ? nc + ('a' - 10)
76 : nc + '0';
77 ec >>= 4;
79 op += 5;
80 } else {
81 *op++ = '?';
84 /* We have some slack there, so it's OK */
85 if (op>ascii+256) {
86 op = ascii + 256;
87 break;
90 *op = 0;
91 return (op - ascii);
94 #if 0
95 static void dump_de(struct msdos_dir_entry *de)
97 int i;
98 unsigned char *p = (unsigned char *) de;
99 printk("[");
101 for (i = 0; i < 32; i++, p++) {
102 printk("%02x ", *p);
104 printk("]\n");
106 #endif
108 static inline unsigned char
109 fat_tolower(struct nls_table *t, unsigned char c)
111 unsigned char nc = t->charset2lower[c];
113 return nc ? nc : c;
116 static inline int
117 fat_short2lower_uni(struct nls_table *t, unsigned char c, wchar_t *uni)
119 int charlen;
120 unsigned char nc = t->charset2lower[c];
122 if (!nc)
123 nc = c;
125 if ( (charlen = t->char2uni(&nc, 1, uni)) < 0) {
126 *uni = 0x003f; /* a question mark */
128 return charlen;
131 static int
132 fat_strnicmp(struct nls_table *t, const unsigned char *s1,
133 const unsigned char *s2, int len)
135 while(len--)
136 if (fat_tolower(t, *s1++) != fat_tolower(t, *s2++))
137 return 1;
139 return 0;
143 * Return values: negative -> error, 0 -> not found, positive -> found,
144 * value is the total amount of slots, including the shortname entry.
146 int fat_search_long(struct inode *inode, const char *name, int name_len,
147 int anycase, loff_t *spos, loff_t *lpos)
149 struct super_block *sb = inode->i_sb;
150 struct buffer_head *bh = NULL;
151 struct msdos_dir_entry *de;
152 struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
153 struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
154 wchar_t bufuname[14];
155 unsigned char xlate_len, long_slots, *unicode = NULL;
156 char c, bufname[260]; /* 256 + 4 */
157 int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
158 int utf8 = MSDOS_SB(sb)->options.utf8;
159 int ino, i, i2, last, res = 0;
160 loff_t cpos = 0;
162 while(1) {
163 if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
164 goto EODir;
165 parse_record:
166 long_slots = 0;
167 if (de->name[0] == (__s8) DELETED_FLAG)
168 continue;
169 if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
170 continue;
171 if (de->attr != ATTR_EXT && IS_FREE(de->name))
172 continue;
173 if (de->attr == ATTR_EXT) {
174 struct msdos_dir_slot *ds;
175 int offset;
176 unsigned char id;
177 unsigned char slot;
178 unsigned char slots;
179 unsigned char sum;
180 unsigned char alias_checksum;
182 if (!unicode) {
183 unicode = (unsigned char *)
184 __get_free_page(GFP_KERNEL);
185 if (!unicode) {
186 fat_brelse(sb, bh);
187 return -ENOMEM;
190 parse_long:
191 slots = 0;
192 offset = 0;
193 ds = (struct msdos_dir_slot *) de;
194 id = ds->id;
195 if (!(id & 0x40))
196 continue;
197 slots = id & ~0x40;
198 if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
199 continue;
200 long_slots = slots;
201 alias_checksum = ds->alias_checksum;
203 slot = slots;
204 while (1) {
205 slot--;
206 offset = slot * 26;
207 memcpy(&unicode[offset], ds->name0_4, 10);
208 memcpy(&unicode[offset+10], ds->name5_10, 12);
209 memcpy(&unicode[offset+22], ds->name11_12, 4);
210 offset += 26;
212 if (ds->id & 0x40) {
213 unicode[offset] = 0;
214 unicode[offset+1] = 0;
216 if (fat_get_entry(inode,&cpos,&bh,&de,&ino)<0)
217 goto EODir;
218 if (slot == 0)
219 break;
220 ds = (struct msdos_dir_slot *) de;
221 if (ds->attr != ATTR_EXT)
222 goto parse_record;
223 if ((ds->id & ~0x40) != slot)
224 goto parse_long;
225 if (ds->alias_checksum != alias_checksum)
226 goto parse_long;
228 if (de->name[0] == (__s8) DELETED_FLAG)
229 continue;
230 if (de->attr == ATTR_EXT)
231 goto parse_long;
232 if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
233 continue;
234 for (sum = 0, i = 0; i < 11; i++)
235 sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
236 if (sum != alias_checksum)
237 long_slots = 0;
240 for (i = 0, last = 0; i < 8;) {
241 if (!(c = de->name[i])) break;
242 if (c == 0x05) c = 0xE5;
243 fat_short2lower_uni(nls_disk, c, &bufuname[i++]);
244 if (c != ' ')
245 last = i;
247 i = last;
248 fat_short2lower_uni(nls_disk, '.', &bufuname[i++]);
249 for (i2 = 0; i2 < 3; i2++) {
250 if (!(c = de->ext[i2])) break;
251 fat_short2lower_uni(nls_disk, c, &bufuname[i++]);
252 if (c != ' ')
253 last = i;
255 if (!last)
256 continue;
258 memset(&bufuname[last], 0, sizeof(wchar_t));
259 xlate_len = utf8
260 ?utf8_wcstombs(bufname, bufuname, 260)
261 :uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
262 if (xlate_len == name_len)
263 if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
264 (anycase && !fat_strnicmp(nls_io, name, bufname,
265 xlate_len)))
266 goto Found;
268 if (long_slots) {
269 xlate_len = utf8
270 ?utf8_wcstombs(bufname, (wchar_t *) unicode, 260)
271 :uni16_to_x8(bufname, (wchar_t *) unicode, uni_xlate, nls_io);
272 if (xlate_len != name_len)
273 continue;
274 if ((!anycase && !memcmp(name, bufname, xlate_len)) ||
275 (anycase && !fat_strnicmp(nls_io, name, bufname,
276 xlate_len)))
277 goto Found;
281 Found:
282 res = long_slots + 1;
283 *spos = cpos - sizeof(struct msdos_dir_entry);
284 *lpos = cpos - res*sizeof(struct msdos_dir_entry);
285 EODir:
286 fat_brelse(sb, bh);
287 if (unicode) {
288 free_page((unsigned long) unicode);
290 return res;
293 static int fat_readdirx(struct inode *inode, struct file *filp, void *dirent,
294 filldir_t filldir, int shortnames, int both)
296 struct super_block *sb = inode->i_sb;
297 struct buffer_head *bh;
298 struct msdos_dir_entry *de;
299 struct nls_table *nls_io = MSDOS_SB(sb)->nls_io;
300 struct nls_table *nls_disk = MSDOS_SB(sb)->nls_disk;
301 wchar_t bufuname[14], *ptuname = &bufuname[0];
302 unsigned char long_slots, *unicode = NULL;
303 char c, bufname[56], *ptname = bufname;
304 unsigned long lpos, dummy, *furrfu = &lpos;
305 int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate;
306 int isvfat = MSDOS_SB(sb)->options.isvfat;
307 int utf8 = MSDOS_SB(sb)->options.utf8;
308 int nocase = MSDOS_SB(sb)->options.nocase;
309 int ino,inum,i,i2,last, dotoffset = 0;
310 loff_t cpos;
312 cpos = filp->f_pos;
313 /* Fake . and .. for the root directory. */
314 if (inode->i_ino == MSDOS_ROOT_INO) {
315 while (cpos < 2) {
316 if (filldir(dirent, "..", cpos+1, cpos, MSDOS_ROOT_INO, DT_DIR) < 0)
317 return 0;
318 cpos++;
319 filp->f_pos++;
321 if (cpos == 2) {
322 dummy = 2;
323 furrfu = &dummy;
324 cpos = 0;
327 if (cpos & (sizeof(struct msdos_dir_entry)-1))
328 return -ENOENT;
330 bh = NULL;
331 GetNew:
332 long_slots = 0;
333 if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
334 goto EODir;
335 /* Check for long filename entry */
336 if (isvfat) {
337 if (de->name[0] == (__s8) DELETED_FLAG)
338 goto RecEnd;
339 if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
340 goto RecEnd;
341 if (de->attr != ATTR_EXT && IS_FREE(de->name))
342 goto RecEnd;
343 } else {
344 if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
345 goto RecEnd;
348 if (isvfat && de->attr == ATTR_EXT) {
349 struct msdos_dir_slot *ds;
350 int offset;
351 unsigned char id;
352 unsigned char slot;
353 unsigned char slots;
354 unsigned char sum;
355 unsigned char alias_checksum;
357 if (!unicode) {
358 unicode = (unsigned char *)
359 __get_free_page(GFP_KERNEL);
360 if (!unicode) {
361 filp->f_pos = cpos;
362 fat_brelse(sb, bh);
363 return -ENOMEM;
366 ParseLong:
367 slots = 0;
368 offset = 0;
369 ds = (struct msdos_dir_slot *) de;
370 id = ds->id;
371 if (!(id & 0x40))
372 goto RecEnd;
373 slots = id & ~0x40;
374 if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */
375 goto RecEnd;
376 long_slots = slots;
377 alias_checksum = ds->alias_checksum;
379 slot = slots;
380 while (1) {
381 slot--;
382 offset = slot * 26;
383 memcpy(&unicode[offset], ds->name0_4, 10);
384 memcpy(&unicode[offset+10], ds->name5_10, 12);
385 memcpy(&unicode[offset+22], ds->name11_12, 4);
386 offset += 26;
388 if (ds->id & 0x40) {
389 unicode[offset] = 0;
390 unicode[offset+1] = 0;
392 if (fat_get_entry(inode,&cpos,&bh,&de,&ino) == -1)
393 goto EODir;
394 if (slot == 0)
395 break;
396 ds = (struct msdos_dir_slot *) de;
397 if (ds->attr != ATTR_EXT)
398 goto RecEnd; /* XXX */
399 if ((ds->id & ~0x40) != slot)
400 goto ParseLong;
401 if (ds->alias_checksum != alias_checksum)
402 goto ParseLong;
404 if (de->name[0] == (__s8) DELETED_FLAG)
405 goto RecEnd;
406 if (de->attr == ATTR_EXT)
407 goto ParseLong;
408 if (IS_FREE(de->name) || (de->attr & ATTR_VOLUME))
409 goto RecEnd;
410 for (sum = 0, i = 0; i < 11; i++)
411 sum = (((sum&1)<<7)|((sum&0xfe)>>1)) + de->name[i];
412 if (sum != alias_checksum)
413 long_slots = 0;
416 if ((de->attr & ATTR_HIDDEN) && MSDOS_SB(sb)->options.dotsOK) {
417 fat_short2lower_uni(nls_disk, '.', ptuname);
418 *ptname++ = '.';
419 dotoffset = 1;
421 for (i = 0, last = 0; i < 8;) {
422 if (!(c = de->name[i])) break;
423 /* see namei.c, msdos_format_name */
424 if (c == 0x05) c = 0xE5;
425 fat_short2lower_uni(nls_disk, c, &ptuname[i]);
426 ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
427 if (c != ' ')
428 last = i;
430 i = last;
431 fat_short2lower_uni(nls_disk, '.', &ptuname[i]);
432 ptname[i++] = '.';
433 for (i2 = 0; i2 < 3; i2++) {
434 if (!(c = de->ext[i2])) break;
435 fat_short2lower_uni(nls_disk, c, &ptuname[i]);
436 ptname[i++] = (!nocase && c>='A' && c<='Z') ? c+32 : c;
437 if (c != ' ')
438 last = i;
440 if (!last)
441 goto RecEnd;
443 i = last + dotoffset;
445 lpos = cpos - (long_slots+1)*sizeof(struct msdos_dir_entry);
446 if (!memcmp(de->name,MSDOS_DOT,11))
447 inum = inode->i_ino;
448 else if (!memcmp(de->name,MSDOS_DOTDOT,11)) {
449 /* inum = fat_parent_ino(inode,0); */
450 inum = filp->f_dentry->d_parent->d_inode->i_ino;
451 } else {
452 struct inode *tmp = fat_iget(sb, ino);
453 if (tmp) {
454 inum = tmp->i_ino;
455 iput(tmp);
456 } else
457 inum = iunique(sb, MSDOS_ROOT_INO);
460 if (isvfat) {
461 memset(&bufuname[i], 0, sizeof(wchar_t));
462 i = utf8 ? utf8_wcstombs(bufname, bufuname, 56)
463 : uni16_to_x8(bufname, bufuname, uni_xlate, nls_io);
466 if (!long_slots||shortnames) {
467 if (both)
468 bufname[i] = '\0';
469 if (filldir(dirent, bufname, i, *furrfu, inum,
470 (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
471 goto FillFailed;
472 } else {
473 char longname[275];
474 unsigned char long_len = utf8
475 ? utf8_wcstombs(longname, (wchar_t *) unicode, 275)
476 : uni16_to_x8(longname, (wchar_t *) unicode, uni_xlate,
477 nls_io);
478 if (both) {
479 memcpy(&longname[long_len+1], bufname, i);
480 long_len += i;
482 if (filldir(dirent, longname, long_len, *furrfu, inum,
483 (de->attr & ATTR_DIR) ? DT_DIR : DT_REG) < 0)
484 goto FillFailed;
487 RecEnd:
488 furrfu = &lpos;
489 filp->f_pos = cpos;
490 goto GetNew;
491 EODir:
492 filp->f_pos = cpos;
493 FillFailed:
494 if (bh)
495 fat_brelse(sb, bh);
496 if (unicode) {
497 free_page((unsigned long) unicode);
499 return 0;
502 int fat_readdir(struct file *filp, void *dirent, filldir_t filldir)
504 struct inode *inode = filp->f_dentry->d_inode;
505 return fat_readdirx(inode, filp, dirent, filldir, 0, 0);
508 static int vfat_ioctl_fill(
509 void * buf,
510 const char * name,
511 int name_len,
512 off_t offset,
513 ino_t ino,
514 unsigned int d_type)
516 struct dirent *d1 = (struct dirent *)buf;
517 struct dirent *d2 = d1 + 1;
518 int len, slen;
519 int dotdir;
521 get_user(len, &d1->d_reclen);
522 if (len != 0) {
523 return -1;
526 if ((name_len == 1 && name[0] == '.') ||
527 (name_len == 2 && name[0] == '.' && name[1] == '.')) {
528 dotdir = 1;
529 len = name_len;
530 } else {
531 dotdir = 0;
532 len = strlen(name);
534 if (len != name_len) {
535 copy_to_user(d2->d_name, name, len);
536 put_user(0, d2->d_name + len);
537 put_user(len, &d2->d_reclen);
538 put_user(ino, &d2->d_ino);
539 put_user(offset, &d2->d_off);
540 slen = name_len - len;
541 copy_to_user(d1->d_name, name+len+1, slen);
542 put_user(0, d1->d_name+slen);
543 put_user(slen, &d1->d_reclen);
544 } else {
545 put_user(0, d2->d_name);
546 put_user(0, &d2->d_reclen);
547 copy_to_user(d1->d_name, name, len);
548 put_user(0, d1->d_name+len);
549 put_user(len, &d1->d_reclen);
551 PRINTK(("FAT d1=%p d2=%p len=%d, name_len=%d\n",
552 d1, d2, len, name_len));
554 return 0;
557 int fat_dir_ioctl(struct inode * inode, struct file * filp,
558 unsigned int cmd, unsigned long arg)
560 int err;
562 * We want to provide an interface for Samba to be able
563 * to get the short filename for a given long filename.
564 * Samba should use this ioctl instead of readdir() to
565 * get the information it needs.
567 switch (cmd) {
568 case VFAT_IOCTL_READDIR_BOTH: {
569 struct dirent *d1 = (struct dirent *)arg;
570 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
571 if (err)
572 return err;
573 put_user(0, &d1->d_reclen);
574 return fat_readdirx(inode,filp,(void *)arg,
575 vfat_ioctl_fill, 0, 1);
577 case VFAT_IOCTL_READDIR_SHORT: {
578 struct dirent *d1 = (struct dirent *)arg;
579 put_user(0, &d1->d_reclen);
580 err = verify_area(VERIFY_WRITE, d1, sizeof(struct dirent[2]));
581 if (err)
582 return err;
583 return fat_readdirx(inode,filp,(void *)arg,
584 vfat_ioctl_fill, 1, 1);
586 default:
587 /* forward ioctl to CVF extension */
588 if (MSDOS_SB(inode->i_sb)->cvf_format &&
589 MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
590 return MSDOS_SB(inode->i_sb)->cvf_format
591 ->cvf_dir_ioctl(inode,filp,cmd,arg);
592 return -EINVAL;
595 return 0;
598 /***** See if directory is empty */
599 int fat_dir_empty(struct inode *dir)
601 loff_t pos;
602 struct buffer_head *bh;
603 struct msdos_dir_entry *de;
604 int ino,result = 0;
606 pos = 0;
607 bh = NULL;
608 while (fat_get_entry(dir,&pos,&bh,&de,&ino) > -1) {
609 /* Ignore vfat longname entries */
610 if (de->attr == ATTR_EXT)
611 continue;
612 if (!IS_FREE(de->name) &&
613 strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
614 strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
615 result = -ENOTEMPTY;
616 break;
619 if (bh)
620 fat_brelse(dir->i_sb, bh);
622 return result;
625 /* This assumes that size of cluster is above the 32*slots */
627 int fat_add_entries(struct inode *dir,int slots, struct buffer_head **bh,
628 struct msdos_dir_entry **de, int *ino)
630 struct super_block *sb = dir->i_sb;
631 loff_t offset, curr;
632 int row;
633 struct buffer_head *new_bh;
635 offset = curr = 0;
636 *bh = NULL;
637 row = 0;
638 while (fat_get_entry(dir,&curr,bh,de,ino) > -1) {
639 if (IS_FREE((*de)->name)) {
640 if (++row == slots)
641 return offset;
642 } else {
643 row = 0;
644 offset = curr;
647 if ((dir->i_ino == MSDOS_ROOT_INO) && (MSDOS_SB(sb)->fat_bits != 32))
648 return -ENOSPC;
649 new_bh = fat_extend_dir(dir);
650 if (!new_bh)
651 return -ENOSPC;
652 fat_brelse(sb, new_bh);
653 do fat_get_entry(dir,&curr,bh,de,ino); while (++row<slots);
654 return offset;
657 int fat_new_dir(struct inode *dir, struct inode *parent, int is_vfat)
659 struct super_block *sb = dir->i_sb;
660 struct buffer_head *bh;
661 struct msdos_dir_entry *de;
662 __u16 date, time;
664 if ((bh = fat_extend_dir(dir)) == NULL) return -ENOSPC;
665 /* zeroed out, so... */
666 fat_date_unix2dos(dir->i_mtime,&time,&date);
667 de = (struct msdos_dir_entry*)&bh->b_data[0];
668 memcpy(de[0].name,MSDOS_DOT,MSDOS_NAME);
669 memcpy(de[1].name,MSDOS_DOTDOT,MSDOS_NAME);
670 de[0].attr = de[1].attr = ATTR_DIR;
671 de[0].time = de[1].time = CT_LE_W(time);
672 de[0].date = de[1].date = CT_LE_W(date);
673 if (is_vfat) { /* extra timestamps */
674 de[0].ctime = de[1].ctime = CT_LE_W(time);
675 de[0].adate = de[0].cdate =
676 de[1].adate = de[1].cdate = CT_LE_W(date);
678 de[0].start = CT_LE_W(MSDOS_I(dir)->i_logstart);
679 de[0].starthi = CT_LE_W(MSDOS_I(dir)->i_logstart>>16);
680 de[1].start = CT_LE_W(MSDOS_I(parent)->i_logstart);
681 de[1].starthi = CT_LE_W(MSDOS_I(parent)->i_logstart>>16);
682 fat_mark_buffer_dirty(sb, bh);
683 fat_brelse(sb, bh);
684 dir->i_atime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
685 mark_inode_dirty(dir);
687 return 0;
691 * Overrides for Emacs so that we follow Linus's tabbing style.
692 * Emacs will notice this stuff at the end of the file and automatically
693 * adjust the settings for this buffer only. This must remain at the end
694 * of the file.
695 * ---------------------------------------------------------------------------
696 * Local variables:
697 * c-indent-level: 8
698 * c-brace-imaginary-offset: 0
699 * c-brace-offset: -8
700 * c-argdecl-indent: 8
701 * c-label-offset: -8
702 * c-continued-statement-offset: 8
703 * c-continued-brace-offset: 0
704 * End: