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>
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>
29 #include <linux/ctype.h>
31 #include <asm/uaccess.h>
37 struct file_operations fat_dir_operations
= {
38 read
: generic_read_dir
,
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.
55 uni16_to_x8(unsigned char *ascii
, wchar_t *uni
, int uni_xlate
,
56 struct nls_table
*nls
)
59 unsigned char *op
, nc
;
68 if ( (charlen
= nls
->uni2char(ec
, op
, 3)) > 0) {
73 for (k
= 4; k
> 0; k
--) {
75 op
[k
] = nc
> 9 ? nc
+ ('a' - 10)
84 /* We have some slack there, so it's OK */
95 static void dump_de(struct msdos_dir_entry
*de
)
98 unsigned char *p
= (unsigned char *) de
;
101 for (i
= 0; i
< 32; i
++, p
++) {
108 static inline unsigned char
109 fat_tolower(struct nls_table
*t
, unsigned char c
)
111 unsigned char nc
= t
->charset2lower
[c
];
117 fat_short2lower_uni(struct nls_table
*t
, unsigned char c
, wchar_t *uni
)
120 unsigned char nc
= t
->charset2lower
[c
];
125 if ( (charlen
= t
->char2uni(&nc
, 1, uni
)) < 0) {
126 *uni
= 0x003f; /* a question mark */
132 fat_strnicmp(struct nls_table
*t
, const unsigned char *s1
,
133 const unsigned char *s2
, int len
)
136 if (fat_tolower(t
, *s1
++) != fat_tolower(t
, *s2
++))
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;
163 if (fat_get_entry(inode
,&cpos
,&bh
,&de
,&ino
) == -1)
167 if (de
->name
[0] == (__s8
) DELETED_FLAG
)
169 if (de
->attr
!= ATTR_EXT
&& (de
->attr
& ATTR_VOLUME
))
171 if (de
->attr
!= ATTR_EXT
&& IS_FREE(de
->name
))
173 if (de
->attr
== ATTR_EXT
) {
174 struct msdos_dir_slot
*ds
;
180 unsigned char alias_checksum
;
183 unicode
= (unsigned char *)
184 __get_free_page(GFP_KERNEL
);
193 ds
= (struct msdos_dir_slot
*) de
;
198 if (slots
> 20 || !slots
) /* ceil(256 * 2 / 26) */
201 alias_checksum
= ds
->alias_checksum
;
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);
214 unicode
[offset
+1] = 0;
216 if (fat_get_entry(inode
,&cpos
,&bh
,&de
,&ino
)<0)
220 ds
= (struct msdos_dir_slot
*) de
;
221 if (ds
->attr
!= ATTR_EXT
)
223 if ((ds
->id
& ~0x40) != slot
)
225 if (ds
->alias_checksum
!= alias_checksum
)
228 if (de
->name
[0] == (__s8
) DELETED_FLAG
)
230 if (de
->attr
== ATTR_EXT
)
232 if (IS_FREE(de
->name
) || (de
->attr
& ATTR_VOLUME
))
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
)
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
++]);
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
++]);
258 memset(&bufuname
[last
], 0, sizeof(wchar_t));
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
,
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
)
274 if ((!anycase
&& !memcmp(name
, bufname
, xlate_len
)) ||
275 (anycase
&& !fat_strnicmp(nls_io
, name
, bufname
,
282 res
= long_slots
+ 1;
283 *spos
= cpos
- sizeof(struct msdos_dir_entry
);
284 *lpos
= cpos
- res
*sizeof(struct msdos_dir_entry
);
288 free_page((unsigned long) unicode
);
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;
313 /* Fake . and .. for the root directory. */
314 if (inode
->i_ino
== MSDOS_ROOT_INO
) {
316 if (filldir(dirent
, "..", cpos
+1, cpos
, MSDOS_ROOT_INO
, DT_DIR
) < 0)
327 if (cpos
& (sizeof(struct msdos_dir_entry
)-1))
333 if (fat_get_entry(inode
,&cpos
,&bh
,&de
,&ino
) == -1)
335 /* Check for long filename entry */
337 if (de
->name
[0] == (__s8
) DELETED_FLAG
)
339 if (de
->attr
!= ATTR_EXT
&& (de
->attr
& ATTR_VOLUME
))
341 if (de
->attr
!= ATTR_EXT
&& IS_FREE(de
->name
))
344 if ((de
->attr
& ATTR_VOLUME
) || IS_FREE(de
->name
))
348 if (isvfat
&& de
->attr
== ATTR_EXT
) {
349 struct msdos_dir_slot
*ds
;
355 unsigned char alias_checksum
;
358 unicode
= (unsigned char *)
359 __get_free_page(GFP_KERNEL
);
369 ds
= (struct msdos_dir_slot
*) de
;
374 if (slots
> 20 || !slots
) /* ceil(256 * 2 / 26) */
377 alias_checksum
= ds
->alias_checksum
;
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);
390 unicode
[offset
+1] = 0;
392 if (fat_get_entry(inode
,&cpos
,&bh
,&de
,&ino
) == -1)
396 ds
= (struct msdos_dir_slot
*) de
;
397 if (ds
->attr
!= ATTR_EXT
)
398 goto RecEnd
; /* XXX */
399 if ((ds
->id
& ~0x40) != slot
)
401 if (ds
->alias_checksum
!= alias_checksum
)
404 if (de
->name
[0] == (__s8
) DELETED_FLAG
)
406 if (de
->attr
== ATTR_EXT
)
408 if (IS_FREE(de
->name
) || (de
->attr
& ATTR_VOLUME
))
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
)
416 if ((de
->attr
& ATTR_HIDDEN
) && MSDOS_SB(sb
)->options
.dotsOK
) {
417 fat_short2lower_uni(nls_disk
, '.', ptuname
);
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
;
431 fat_short2lower_uni(nls_disk
, '.', &ptuname
[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
;
443 i
= last
+ dotoffset
;
445 lpos
= cpos
- (long_slots
+1)*sizeof(struct msdos_dir_entry
);
446 if (!memcmp(de
->name
,MSDOS_DOT
,11))
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
;
452 struct inode
*tmp
= fat_iget(sb
, ino
);
457 inum
= iunique(sb
, MSDOS_ROOT_INO
);
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
) {
469 if (filldir(dirent
, bufname
, i
, *furrfu
, inum
,
470 (de
->attr
& ATTR_DIR
) ? DT_DIR
: DT_REG
) < 0)
474 unsigned char long_len
= utf8
475 ? utf8_wcstombs(longname
, (wchar_t *) unicode
, 275)
476 : uni16_to_x8(longname
, (wchar_t *) unicode
, uni_xlate
,
479 memcpy(&longname
[long_len
+1], bufname
, i
);
482 if (filldir(dirent
, longname
, long_len
, *furrfu
, inum
,
483 (de
->attr
& ATTR_DIR
) ? DT_DIR
: DT_REG
) < 0)
497 free_page((unsigned long) unicode
);
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(
516 struct dirent
*d1
= (struct dirent
*)buf
;
517 struct dirent
*d2
= d1
+ 1;
521 get_user(len
, &d1
->d_reclen
);
526 if ((name_len
== 1 && name
[0] == '.') ||
527 (name_len
== 2 && name
[0] == '.' && name
[1] == '.')) {
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
);
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
));
557 int fat_dir_ioctl(struct inode
* inode
, struct file
* filp
,
558 unsigned int cmd
, unsigned long arg
)
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.
568 case VFAT_IOCTL_READDIR_BOTH
: {
569 struct dirent
*d1
= (struct dirent
*)arg
;
570 err
= verify_area(VERIFY_WRITE
, d1
, sizeof(struct dirent
[2]));
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]));
583 return fat_readdirx(inode
,filp
,(void *)arg
,
584 vfat_ioctl_fill
, 1, 1);
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
);
598 /***** See if directory is empty */
599 int fat_dir_empty(struct inode
*dir
)
602 struct buffer_head
*bh
;
603 struct msdos_dir_entry
*de
;
608 while (fat_get_entry(dir
,&pos
,&bh
,&de
,&ino
) > -1) {
609 /* Ignore vfat longname entries */
610 if (de
->attr
== ATTR_EXT
)
612 if (!IS_FREE(de
->name
) &&
613 strncmp(de
->name
,MSDOS_DOT
, MSDOS_NAME
) &&
614 strncmp(de
->name
,MSDOS_DOTDOT
, MSDOS_NAME
)) {
620 fat_brelse(dir
->i_sb
, bh
);
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
;
633 struct buffer_head
*new_bh
;
638 while (fat_get_entry(dir
,&curr
,bh
,de
,ino
) > -1) {
639 if (IS_FREE((*de
)->name
)) {
647 if ((dir
->i_ino
== MSDOS_ROOT_INO
) && (MSDOS_SB(sb
)->fat_bits
!= 32))
649 new_bh
= fat_extend_dir(dir
);
652 fat_brelse(sb
, new_bh
);
653 do fat_get_entry(dir
,&curr
,bh
,de
,ino
); while (++row
<slots
);
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
;
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
);
684 dir
->i_atime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
685 mark_inode_dirty(dir
);
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
695 * ---------------------------------------------------------------------------
698 * c-brace-imaginary-offset: 0
700 * c-argdecl-indent: 8
702 * c-continued-statement-offset: 8
703 * c-continued-brace-offset: 0