2 * linux/fs/vfat/namei.c
4 * Written 1992,1993 by Werner Almesberger
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.
13 #define __NO_VERSION__
14 #include <linux/module.h>
16 #include <linux/sched.h>
17 #include <linux/msdos_fs.h>
18 #include <linux/nls.h>
19 #include <linux/kernel.h>
20 #include <linux/errno.h>
21 #include <linux/string.h>
22 #include <linux/ctype.h>
23 #include <linux/stat.h>
25 #include <linux/malloc.h>
27 #include "../fat/msbuffer.h"
30 #if (DEBUG_LEVEL >= 1)
31 # define PRINTK1(x) printk x
35 #if (DEBUG_LEVEL >= 2)
36 # define PRINTK2(x) printk x
40 #if (DEBUG_LEVEL >= 3)
41 # define PRINTK3(x) printk x
49 # define CHECK_STACK check_stack(__FILE__, __LINE__)
52 struct vfat_find_info
{
66 void vfat_read_inode(struct inode
*inode
);
67 static int vfat_valid_shortname(const char *,int, int, int);
68 static int vfat_format_name(const char *, int, char *, int, int);
69 static int vfat_valid_longname(const char *, int, int, int);
70 static int vfat_hashi(struct dentry
*parent
, struct qstr
*qstr
);
71 static int vfat_hash(struct dentry
*parent
, struct qstr
*qstr
);
72 static int vfat_cmpi(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
);
73 static int vfat_cmp(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
);
74 static int vfat_revalidate(struct dentry
*dentry
);
76 static struct dentry_operations vfat_dentry_ops
[4] = {
78 NULL
, /* d_revalidate */
90 NULL
, /* d_revalidate */
103 void vfat_put_super(struct super_block
*sb
)
109 static int vfat_revalidate(struct dentry
*dentry
)
111 PRINTK1(("vfat_revalidate: %s\n", dentry
->d_name
.name
));
112 if (dentry
->d_time
== dentry
->d_parent
->d_inode
->i_version
) {
118 static struct super_operations vfat_sops
= {
125 NULL
, /* write_super */
130 static int simple_getbool(char *s
, int *setval
)
133 if (!strcmp(s
,"1") || !strcmp(s
,"yes") || !strcmp(s
,"true")) {
135 } else if (!strcmp(s
,"0") || !strcmp(s
,"no") || !strcmp(s
,"false")) {
146 static int parse_options(char *options
, struct fat_mount_options
*opts
)
148 char *this_char
,*value
,save
,*savep
;
151 opts
->unicode_xlate
= opts
->posixfs
= 0;
155 if (!options
) return 1;
159 for (this_char
= strtok(options
,","); this_char
; this_char
= strtok(NULL
,",")) {
160 if ((value
= strchr(this_char
,'=')) != NULL
) {
165 if (!strcmp(this_char
,"utf8")) {
166 ret
= simple_getbool(value
, &val
);
167 if (ret
) opts
->utf8
= val
;
168 } else if (!strcmp(this_char
,"uni_xlate")) {
169 ret
= simple_getbool(value
, &val
);
170 if (ret
) opts
->unicode_xlate
= val
;
171 } else if (!strcmp(this_char
,"posix")) {
172 ret
= simple_getbool(value
, &val
);
173 if (ret
) opts
->posixfs
= val
;
174 } else if (!strcmp(this_char
,"nonumtail")) {
175 ret
= simple_getbool(value
, &val
);
177 opts
->numtail
= !val
;
180 if (this_char
!= options
)
181 *(this_char
-1) = ',';
189 if (opts
->unicode_xlate
) {
196 * Compute the hash for the vfat name corresponding to the dentry.
197 * Note: if the name is invalid, we leave the hash code unchanged so
198 * that the existing dentry can be used. The vfat fs routines will
199 * return ENOENT or EINVAL as appropriate.
201 static int vfat_hash(struct dentry
*dentry
, struct qstr
*qstr
)
208 while (len
&& name
[len
-1] == '.')
211 qstr
->hash
= full_name_hash(name
, len
);
217 * Compute the hash for the vfat name corresponding to the dentry.
218 * Note: if the name is invalid, we leave the hash code unchanged so
219 * that the existing dentry can be used. The vfat fs routines will
220 * return ENOENT or EINVAL as appropriate.
222 static int vfat_hashi(struct dentry
*dentry
, struct qstr
*qstr
)
231 while (len
&& name
[len
-1] == '.')
234 hash
= init_name_hash();
236 c
= tolower(*name
++);
237 hash
= partial_name_hash(tolower(c
), hash
);
239 qstr
->hash
= end_name_hash(hash
);
245 * Case insensitive compare of two vfat names.
247 static int vfat_cmpi(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
)
251 /* A filename cannot end in '.' or we treat it like it has none */
254 while (alen
&& a
->name
[alen
-1] == '.')
256 while (blen
&& b
->name
[blen
-1] == '.')
259 if (strnicmp(a
->name
, b
->name
, alen
) == 0)
266 * Case sensitive compare of two vfat names.
268 static int vfat_cmp(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
)
272 /* A filename cannot end in '.' or we treat it like it has none */
275 while (alen
&& a
->name
[alen
-1] == '.')
277 while (blen
&& b
->name
[blen
-1] == '.')
280 if (strncmp(a
->name
, b
->name
, alen
) == 0)
286 struct super_block
*vfat_read_super(struct super_block
*sb
,void *data
,
289 struct super_block
*res
;
293 MSDOS_SB(sb
)->options
.isvfat
= 1;
295 sb
->s_op
= &vfat_sops
;
296 res
= fat_read_super(sb
, data
, silent
);
303 if (!parse_options((char *) data
, &(MSDOS_SB(sb
)->options
))) {
306 MSDOS_SB(sb
)->options
.dotsOK
= 0;
307 if (MSDOS_SB(sb
)->options
.posixfs
) {
308 MSDOS_SB(sb
)->options
.name_check
= 's';
310 if (MSDOS_SB(sb
)->options
.name_check
!= 's') {
311 sb
->s_root
->d_op
= &vfat_dentry_ops
[0];
313 sb
->s_root
->d_op
= &vfat_dentry_ops
[2];
323 check_stack(const char *fname
, int lineno
)
328 stack_level
= (long)(&pg_dir
)-current
->kernel_stack_page
;
330 printk("*-*-*-* vfat kstack overflow in %s line %d: SL=%d\n",
331 fname
, lineno
, stack_level
);
332 else if (stack_level
< 500)
333 printk("*-*-*-* vfat kstack low in %s line %d: SL=%d\n",
334 fname
, lineno
, stack_level
);
337 printk("------- vfat kstack ok in %s line %d: SL=%d\n",
338 fname
, lineno
, stack_level
);
341 if (*(unsigned long *) current
->kernel_stack_page
!= STACK_MAGIC
) {
342 printk("******* vfat stack corruption detected in %s at line %d\n",
348 static int debug
= 0;
349 static void dump_fat(struct super_block
*sb
,int start
)
354 start
= fat_access(sb
,start
,-1);
359 if (start
== -1) break;
364 static void dump_de(struct msdos_dir_entry
*de
)
367 unsigned char *p
= (unsigned char *) de
;
370 for (i
= 0; i
< 32; i
++, p
++) {
378 /* MS-DOS "device special files" */
380 static const char *reserved3_names
[] = {
381 "con ", "prn ", "nul ", "aux ", NULL
384 static const char *reserved4_names
[] = {
385 "com1 ", "com2 ", "com3 ", "com4 ", "com5 ",
386 "com6 ", "com7 ", "com8 ", "com9 ",
387 "lpt1 ", "lpt2 ", "lpt3 ", "lpt4 ", "lpt5 ",
388 "lpt6 ", "lpt7 ", "lpt8 ", "lpt9 ",
392 /* Characters that are undesirable in an MS-DOS file name */
394 static char bad_chars
[] = "*?<>|\":/\\";
395 static char replace_chars
[] = "[];,+=";
397 static int vfat_find(struct inode
*dir
,struct qstr
* name
,
398 int new_filename
,int is_dir
,
399 struct vfat_slot_info
*sinfo_out
);
401 /* Checks the validity of a long MS-DOS filename */
402 /* Returns negative number on error, 0 for a normal
403 * return, and 1 for . or .. */
405 static int vfat_valid_longname(const char *name
, int len
, int dot_dirs
,
408 const char **reserved
, *walk
;
412 if (IS_FREE(name
)) return -EINVAL
;
413 if (name
[0] == '.' && (len
== 1 || (len
== 2 && name
[1] == '.'))) {
414 if (!dot_dirs
) return -EEXIST
;
418 if (len
&& name
[len
-1] == ' ') return -EINVAL
;
419 if (len
>= 256) return -EINVAL
;
420 for (i
= 0; i
< len
; i
++) {
422 if (xlate
&& c
== ':') continue;
423 if (strchr(bad_chars
,c
)) {
427 if (len
< 3) return 0;
429 for (walk
= name
; *walk
!= 0 && *walk
!= '.'; walk
++);
430 baselen
= walk
- name
;
433 for (reserved
= reserved3_names
; *reserved
; reserved
++) {
434 if (!strnicmp(name
,*reserved
,baselen
))
437 } else if (baselen
== 4) {
438 for (reserved
= reserved4_names
; *reserved
; reserved
++) {
439 if (!strnicmp(name
,*reserved
,baselen
))
446 static int vfat_valid_shortname(const char *name
,int len
,
447 int dot_dirs
, int utf8
)
454 if (name
[0] == '.' && (len
== 1 || (len
== 2 && name
[1] == '.'))) {
455 if (!dot_dirs
) return -EEXIST
;
459 space
= 1; /* disallow names starting with a dot */
461 for (walk
= name
; len
&& walk
-name
< 8;) {
464 if (utf8
&& (c
& 0x80)) return -EINVAL
;
465 if (strchr(replace_chars
,c
)) return -EINVAL
;
466 if (c
>= 'A' && c
<= 'Z') return -EINVAL
;
467 if (c
< ' '|| c
==':') return -EINVAL
;
471 if (space
) return -EINVAL
;
472 if (len
&& c
!= '.') {
475 if (c
!= '.') return -EINVAL
;
477 baselen
= walk
- name
;
480 if (len
>= 4) return -EINVAL
;
484 if (utf8
&& (c
& 0x80)) return -EINVAL
;
485 if (strchr(replace_chars
,c
))
487 if (c
< ' ' || c
== '.'|| c
==':')
489 if (c
>= 'A' && c
<= 'Z') return -EINVAL
;
492 if (space
) return -EINVAL
;
498 static int vfat_find_form(struct inode
*dir
,char *name
)
500 struct msdos_dir_entry
*de
;
501 struct buffer_head
*bh
= NULL
;
504 while(fat_get_entry(dir
, &pos
, &bh
, &de
) >= 0) {
505 if (de
->attr
== ATTR_EXT
)
507 if (memcmp(de
->name
,name
,MSDOS_NAME
))
516 static int vfat_format_name(const char *name
,int len
,char *res
,
517 int dot_dirs
,int utf8
)
523 if (name
[0] == '.' && (len
== 1 || (len
== 2 && name
[1] == '.'))) {
524 if (!dot_dirs
) return -EEXIST
;
525 memset(res
+1,' ',10);
526 while (len
--) *res
++ = '.';
530 space
= 1; /* disallow names starting with a dot */
531 for (walk
= res
; len
-- && (c
=*name
++)!='.' ; walk
++) {
532 if (walk
-res
== 8) return -EINVAL
;
533 if (utf8
&& (c
& 0x80)) return -EINVAL
;
534 if (strchr(replace_chars
,c
)) return -EINVAL
;
535 if (c
>= 'A' && c
<= 'Z') return -EINVAL
;
536 if (c
< ' '|| c
==':') return -EINVAL
;
538 *walk
= c
>= 'a' && c
<= 'z' ? c
-32 : c
;
540 if (space
) return -EINVAL
;
542 while (walk
-res
< 8) *walk
++ = ' ';
543 while (len
> 0 && walk
-res
< MSDOS_NAME
) {
546 if (utf8
&& (c
& 0x80)) return -EINVAL
;
547 if (strchr(replace_chars
,c
))
549 if (c
< ' ' || c
== '.'|| c
==':')
551 if (c
>= 'A' && c
<= 'Z') return -EINVAL
;
553 *walk
++ = c
>= 'a' && c
<= 'z' ? c
-32 : c
;
555 if (space
) return -EINVAL
;
556 if (len
) return -EINVAL
;
558 while (walk
-res
< MSDOS_NAME
) *walk
++ = ' ';
563 static char skip_chars
[] = ".:\"?<>| ";
565 /* Given a valid longname, create a unique shortname. Make sure the
566 * shortname does not exist
568 static int vfat_create_shortname(struct inode
*dir
, const char *name
,
569 int len
, char *name_res
, int utf8
)
571 const char *ip
, *ext_start
, *end
;
573 int sz
, extlen
, baselen
;
575 char base
[9], ext
[4];
578 const char *name_start
;
580 PRINTK2(("Entering vfat_create_shortname: name=%s, len=%d\n", name
, len
));
581 sz
= 0; /* Make compiler happy */
582 if (len
&& name
[len
-1]==' ') return -EINVAL
;
584 /* Do a case insensitive search if the name would be a valid
585 * shortname if is were all capitalized. However, do not
586 * allow spaces in short names because Win95 scandisk does
588 for (i
= 0, p
= msdos_name
, ip
= name
; ; i
++, p
++, ip
++) {
590 if (vfat_format_name(msdos_name
,
591 len
, name_res
, 1, utf8
) < 0)
593 PRINTK3(("vfat_create_shortname 1\n"));
594 if (vfat_find_form(dir
, name_res
) < 0)
601 if (*ip
>= 'A' && *ip
<= 'Z') {
609 PRINTK3(("vfat_create_shortname 3\n"));
610 /* Now, we need to create a shortname from the long name */
611 ext_start
= end
= &name
[len
];
612 while (--ext_start
>= name
) {
613 if (*ext_start
== '.') {
614 if (ext_start
== end
- 1) {
621 if (ext_start
== name
- 1) {
624 } else if (ext_start
) {
626 * Names which start with a dot could be just
627 * an extension eg. "...test". In this case Win95
628 * uses the extension as the name and sets no extension.
630 name_start
= &name
[0];
631 while (name_start
< ext_start
)
633 if (!strchr(skip_chars
,*name_start
)) break;
636 if (name_start
!= ext_start
) {
637 sz
= ext_start
- name
;
645 for (baselen
= i
= 0, p
= base
, ip
= name
; i
< sz
&& baselen
< 8; i
++)
647 if (utf8
&& (*ip
& 0x80)) {
650 } else if (!strchr(skip_chars
, *ip
)) {
651 if (*ip
>= 'a' && *ip
<= 'z') {
656 if (strchr(replace_chars
, *p
)) *p
='_';
667 for (p
= ext
, ip
= ext_start
; extlen
< 3 && ip
< end
; ip
++) {
668 if (utf8
&& (*ip
& 0x80)) {
671 } else if (!strchr(skip_chars
, *ip
)) {
672 if (*ip
>= 'a' && *ip
<= 'z') {
677 if (strchr(replace_chars
, *p
)) *p
='_';
684 base
[baselen
] = '\0';
686 /* Yes, it can happen. ".\xe5" would do it. */
690 /* OK, at this point we know that base is not longer than 8 symbols,
691 * ext is not longer than 3, base is nonempty, both don't contain
692 * any bad symbols (lowercase transformed to uppercase).
695 memset(name_res
, ' ', MSDOS_NAME
);
696 memcpy(name_res
,base
,baselen
);
697 memcpy(name_res
+8,ext
,extlen
);
698 if (MSDOS_SB(dir
->i_sb
)->options
.numtail
== 0)
699 if (vfat_find_form(dir
, name_res
) < 0)
703 * Try to find a unique extension. This used to
704 * iterate through all possibilities sequentially,
705 * but that gave extremely bad performance. Windows
706 * only tries a few cases before using random
707 * values for part of the base.
712 name_res
[baselen
] = '~';
713 for (i
= 1; i
< 10; i
++) {
714 name_res
[baselen
+1] = i
+ '0';
715 if (vfat_find_form(dir
, name_res
) < 0)
719 i
= jiffies
& 0xffff;
720 sz
= (jiffies
>> 16) & 0x7;
723 name_res
[baselen
+4] = '~';
724 name_res
[baselen
+5] = '1' + sz
;
726 sprintf(buf
, "%04X", i
);
727 memcpy(&name_res
[baselen
], buf
, 4);
728 if (vfat_find_form(dir
, name_res
) < 0)
735 static loff_t
vfat_find_free_slots(struct inode
*dir
,int slots
)
737 struct super_block
*sb
= dir
->i_sb
;
739 struct msdos_dir_entry
*de
;
740 struct buffer_head
*bh
;
748 PRINTK2(("vfat_find_free_slots: find %d free slots\n", slots
));
752 ino
= fat_get_entry(dir
,&curr
,&bh
,&de
);
754 for (added
= 0; added
< 2; added
++) {
756 done
= IS_FREE(de
->name
);
758 inode
= iget(sb
,ino
);
760 /* Directory slots of busy deleted files aren't available yet. */
761 done
= !MSDOS_I(inode
)->i_busy
;
762 /* PRINTK3(("inode %d still busy\n", ino)); */
770 /* printk("----- Free offset at %d\n", offset); */
777 ino
= fat_get_entry(dir
,&curr
,&bh
,&de
);
780 if ((dir
->i_ino
== MSDOS_ROOT_INO
) &&
781 (MSDOS_SB(sb
)->fat_bits
!= 32))
783 if ((res
= fat_add_cluster(dir
)) < 0) return res
;
784 ino
= fat_get_entry(dir
,&curr
,&bh
,&de
);
789 /* Translate a string, including coded sequences into Unicode */
791 xlate_to_uni(const char *name
, int len
, char *outname
, int *outlen
,
792 int escape
, int utf8
, struct nls_table
*nls
)
795 const unsigned char *ip
;
798 unsigned char c1
, c2
, c3
;
801 *outlen
= utf8_mbstowcs((__u16
*) outname
, name
, PAGE_SIZE
);
802 if (name
[len
-1] == '.')
804 op
= &outname
[*outlen
* sizeof(__u16
)];
806 if (name
[len
-1] == '.')
810 for (i
= 0, ip
= name
, op
= outname
, *outlen
= 0;
811 i
< len
&& *outlen
<= 260; i
++, *outlen
+= 1)
813 if (escape
&& (*ip
== ':')) {
814 if (i
> len
- 4) return -EINVAL
;
815 c1
= fat_esc2uni
[ip
[1]];
816 c2
= fat_esc2uni
[ip
[2]];
817 c3
= fat_esc2uni
[ip
[3]];
818 if (c1
== 255 || c2
== 255 || c3
== 255)
820 *op
++ = (c1
<< 4) + (c2
>> 2);
821 *op
++ = ((c2
& 0x3) << 6) + c3
;
824 *op
++ = nls
->charset2uni
[*ip
].uni1
;
825 *op
++ = nls
->charset2uni
[*ip
].uni2
;
830 for (i
= 0, ip
= name
, op
= outname
, *outlen
= 0;
831 i
< len
&& *outlen
<= 260; i
++, *outlen
+= 1)
839 return -ENAMETOOLONG
;
846 fill
= 13 - (*outlen
% 13);
847 for (i
= 0; i
< fill
; i
++) {
859 vfat_fill_long_slots(struct msdos_dir_slot
*ds
, const char *name
, int len
,
860 char *msdos_name
, int *slots
,
861 int uni_xlate
, int utf8
, struct nls_table
*nls
)
863 struct msdos_dir_slot
*ps
;
864 struct msdos_dir_entry
*de
;
875 if (name
[len
-1] == '.') len
--;
876 if(!(page
= __get_free_page(GFP_KERNEL
)))
878 uniname
= (char *) page
;
879 res
= xlate_to_uni(name
, len
, uniname
, &unilen
, uni_xlate
, utf8
, nls
);
885 *slots
= unilen
/ 13;
886 for (cksum
= i
= 0; i
< 11; i
++) {
887 cksum
= (((cksum
&1)<<7)|((cksum
&0xfe)>>1)) + msdos_name
[i
];
889 PRINTK3(("vfat_fill_long_slots 3: slots=%d\n",*slots
));
891 for (ps
= ds
, slot
= *slots
; slot
> 0; slot
--, ps
++) {
894 PRINTK3(("vfat_fill_long_slots 4\n"));
898 ps
->alias_checksum
= cksum
;
900 PRINTK3(("vfat_fill_long_slots 5: uniname=%s\n",uniname
));
901 offset
= (slot
- 1) * 26;
902 ip
= &uniname
[offset
];
905 for (i
= 0; i
< 10; i
+= 2) {
906 ps
->name0_4
[i
] = *ip
++;
907 ps
->name0_4
[i
+1] = *ip
++;
909 PRINTK3(("vfat_fill_long_slots 6\n"));
910 for (i
= 0; i
< 12; i
+= 2) {
911 ps
->name5_10
[i
] = *ip
++;
912 ps
->name5_10
[i
+1] = *ip
++;
914 PRINTK3(("vfat_fill_long_slots 7\n"));
915 for (i
= 0; i
< 4; i
+= 2) {
916 ps
->name11_12
[i
] = *ip
++;
917 ps
->name11_12
[i
+1] = *ip
++;
920 PRINTK3(("vfat_fill_long_slots 8\n"));
923 de
= (struct msdos_dir_entry
*) ps
;
924 PRINTK3(("vfat_fill_long_slots 9\n"));
925 strncpy(de
->name
, msdos_name
, MSDOS_NAME
);
931 static int vfat_build_slots(struct inode
*dir
,const char *name
,int len
,
932 struct msdos_dir_slot
*ds
, int *slots
, int *is_long
)
934 struct msdos_dir_entry
*de
;
935 char msdos_name
[MSDOS_NAME
];
936 int res
, xlate
, utf8
;
937 struct nls_table
*nls
;
939 PRINTK2(("Entering vfat_build_slots: name=%s, len=%d\n", name
, len
));
940 de
= (struct msdos_dir_entry
*) ds
;
941 xlate
= MSDOS_SB(dir
->i_sb
)->options
.unicode_xlate
;
942 utf8
= MSDOS_SB(dir
->i_sb
)->options
.utf8
;
943 nls
= MSDOS_SB(dir
->i_sb
)->nls_io
;
947 if (len
== 1 && name
[0] == '.') {
948 strncpy(de
->name
, MSDOS_DOT
, MSDOS_NAME
);
949 } else if (len
== 2 && name
[0] == '.' && name
[1] == '.') {
950 strncpy(de
->name
, MSDOS_DOT
, MSDOS_NAME
);
952 PRINTK3(("vfat_build_slots 4\n"));
953 res
= vfat_valid_longname(name
, len
, 1, xlate
);
957 res
= vfat_valid_shortname(name
, len
, 1, utf8
);
959 PRINTK3(("vfat_build_slots 5a\n"));
960 res
= vfat_format_name(name
, len
, de
->name
, 1, utf8
);
961 PRINTK3(("vfat_build_slots 5b\n"));
963 res
= vfat_create_shortname(dir
, name
, len
, msdos_name
, utf8
);
970 return vfat_fill_long_slots(ds
, name
, len
, msdos_name
,
971 slots
, xlate
, utf8
, nls
);
977 static int vfat_readdir_cb(
988 struct vfat_find_info
*vf
= (struct vfat_find_info
*) buf
;
993 if (debug
) printk("cb: vf.name=%s, len=%d, name=%s, name_len=%d\n",
994 vf
->name
, vf
->len
, name
, name_len
);
997 if (vf
->len
!= name_len
) {
1001 s1
= name
; s2
= vf
->name
;
1002 for (i
= 0; i
< name_len
; i
++) {
1003 if (vf
->anycase
|| (vf
->new_filename
&& !vf
->posix
)) {
1004 if (tolower(*s1
) != tolower(*s2
))
1013 vf
->is_long
= is_long
;
1014 vf
->offset
= (offset
== 2) ? 0 : offset
;
1015 vf
->short_offset
= (short_offset
== 2) ? 0 : short_offset
;
1016 vf
->long_slots
= long_slots
;
1021 static int vfat_find(struct inode
*dir
,struct qstr
* qname
,
1022 int new_filename
,int is_dir
,struct vfat_slot_info
*sinfo_out
)
1024 struct super_block
*sb
= dir
->i_sb
;
1025 struct vfat_find_info vf
;
1027 struct buffer_head
*bh
;
1028 struct msdos_dir_entry
*de
;
1029 struct msdos_dir_slot
*ps
;
1031 struct msdos_dir_slot
*ds
;
1036 PRINTK2(("Entering vfat_find\n"));
1038 ds
= (struct msdos_dir_slot
*)
1039 kmalloc(sizeof(struct msdos_dir_slot
)*MSDOS_SLOTS
, GFP_KERNEL
);
1040 if (ds
== NULL
) return -ENOMEM
;
1043 vf
.name
= qname
->name
;
1044 vf
.len
= qname
->len
;
1045 while (vf
.len
&& vf
.name
[vf
.len
-1] == '.') {
1048 vf
.new_filename
= new_filename
;
1050 vf
.posix
= MSDOS_SB(sb
)->options
.posixfs
;
1051 vf
.anycase
= (MSDOS_SB(sb
)->options
.name_check
!= 's');
1052 res
= fat_readdirx(dir
,&fil
,(void *)&vf
,vfat_readdir_cb
,NULL
,1,1,0);
1053 PRINTK3(("vfat_find: Debug 1\n"));
1054 if (res
< 0) goto cleanup
;
1060 sinfo_out
->longname_offset
= vf
.offset
;
1061 sinfo_out
->shortname_offset
= vf
.short_offset
;
1062 sinfo_out
->is_long
= vf
.is_long
;
1063 sinfo_out
->long_slots
= vf
.long_slots
;
1064 sinfo_out
->total_slots
= vf
.long_slots
+ 1;
1065 sinfo_out
->ino
= vf
.ino
;
1067 PRINTK3(("vfat_find: Debug 2\n"));
1072 PRINTK3(("vfat_find: Debug 3\n"));
1073 if (!new_filename
) {
1078 res
= vfat_build_slots(dir
, qname
->name
, vf
.len
, ds
,
1080 /* Here we either have is_long and slots>=0 or slots==1 */
1081 if (res
< 0) goto cleanup
;
1083 de
= (struct msdos_dir_entry
*) ds
;
1087 PRINTK3(("vfat_find: create file 1\n"));
1088 if (is_long
) slots
++;
1089 offset
= vfat_find_free_slots(dir
, slots
);
1095 PRINTK3(("vfat_find: create file 2\n"));
1096 /* Now create the new entry */
1098 for (slot
= 0, ps
= ds
; slot
< slots
; slot
++, ps
++) {
1099 PRINTK3(("vfat_find: create file 3, slot=%d\n",slot
));
1100 sinfo_out
->ino
= fat_get_entry(dir
,&offset
,&bh
,&de
);
1101 if (sinfo_out
->ino
< 0) {
1102 PRINTK3(("vfat_find: problem\n"));
1103 res
= sinfo_out
->ino
;
1106 memcpy(de
, ps
, sizeof(struct msdos_dir_slot
));
1107 fat_mark_buffer_dirty(sb
, bh
, 1);
1110 PRINTK3(("vfat_find: create file 4\n"));
1111 dir
->i_ctime
= dir
->i_mtime
= dir
->i_atime
= CURRENT_TIME
;
1112 mark_inode_dirty(dir
);
1114 PRINTK3(("vfat_find: create file 5\n"));
1116 fat_date_unix2dos(dir
->i_mtime
,&de
->time
,&de
->date
);
1118 de
->ctime
= de
->time
;
1119 de
->adate
= de
->cdate
= de
->date
;
1123 de
->attr
= is_dir
? ATTR_DIR
: ATTR_ARCH
;
1124 de
->lcase
= CASE_LOWER_BASE
| CASE_LOWER_EXT
;
1127 fat_mark_buffer_dirty(sb
, bh
, 1);
1130 /* slots can't be less than 1 */
1131 sinfo_out
->is_long
= (slots
> 1);
1132 sinfo_out
->long_slots
= slots
- 1;
1133 sinfo_out
->total_slots
= slots
;
1134 sinfo_out
->shortname_offset
= offset
- sizeof(struct msdos_dir_slot
);
1135 sinfo_out
->longname_offset
= offset
- sizeof(struct msdos_dir_slot
) * slots
;
1143 int vfat_lookup(struct inode
*dir
,struct dentry
*dentry
)
1146 struct vfat_slot_info sinfo
;
1147 struct inode
*result
;
1150 PRINTK2(("vfat_lookup: name=%s, len=%d\n",
1151 dentry
->d_name
.name
, dentry
->d_name
.len
));
1153 table
= (MSDOS_SB(dir
->i_sb
)->options
.name_check
== 's') ? 2 : 0;
1154 dentry
->d_op
= &vfat_dentry_ops
[table
];
1157 if ((res
= vfat_find(dir
,&dentry
->d_name
,0,0,&sinfo
)) < 0) {
1162 PRINTK3(("vfat_lookup 4.5\n"));
1163 if (!(result
= iget(dir
->i_sb
,sinfo
.ino
)))
1165 PRINTK3(("vfat_lookup 5\n"));
1166 if (MSDOS_I(result
)->i_busy
) { /* mkdir in progress */
1172 PRINTK3(("vfat_lookup 6\n"));
1174 dentry
->d_op
= &vfat_dentry_ops
[table
];
1175 dentry
->d_time
= dentry
->d_parent
->d_inode
->i_version
;
1176 d_add(dentry
,result
);
1181 static int vfat_create_entry(struct inode
*dir
,struct qstr
* qname
,
1182 int is_dir
, struct inode
**result
)
1184 struct super_block
*sb
= dir
->i_sb
;
1187 struct buffer_head
*bh
;
1188 struct msdos_dir_entry
*de
;
1189 struct vfat_slot_info sinfo
;
1192 PRINTK1(("vfat_create_entry: Entering\n"));
1193 res
= vfat_find(dir
, qname
, 1, is_dir
, &sinfo
);
1198 offset
= sinfo
.shortname_offset
;
1200 PRINTK3(("vfat_create_entry 2\n"));
1202 ino
= fat_get_entry(dir
, &offset
, &bh
, &de
);
1204 PRINTK3(("vfat_mkdir problem\n"));
1209 PRINTK3(("vfat_create_entry 3\n"));
1211 if ((*result
= iget(dir
->i_sb
,ino
)) != NULL
)
1212 vfat_read_inode(*result
);
1216 (*result
)->i_mtime
= (*result
)->i_atime
= (*result
)->i_ctime
=
1218 mark_inode_dirty(*result
);
1219 (*result
)->i_version
= ++event
;
1220 dir
->i_version
= event
;
1225 int vfat_create(struct inode
*dir
,struct dentry
* dentry
,int mode
)
1228 struct inode
*result
;
1231 fat_lock_creation();
1232 res
= vfat_create_entry(dir
,&dentry
->d_name
,0,&result
);
1233 fat_unlock_creation();
1235 PRINTK3(("vfat_create: unable to get new entry\n"));
1237 dentry
->d_time
= dentry
->d_parent
->d_inode
->i_version
;
1238 d_instantiate(dentry
,result
);
1243 static int vfat_create_a_dotdir(struct inode
*dir
,struct inode
*parent
,
1244 struct buffer_head
*bh
,
1245 struct msdos_dir_entry
*de
,int ino
,const char *name
, int isdot
)
1247 struct super_block
*sb
= dir
->i_sb
;
1250 PRINTK2(("vfat_create_a_dotdir: Entering\n"));
1253 * XXX all times should be set by caller upon successful completion.
1255 dir
->i_atime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
1256 mark_inode_dirty(dir
);
1257 memcpy(de
->name
,name
,MSDOS_NAME
);
1259 de
->attr
= ATTR_DIR
;
1262 fat_date_unix2dos(dir
->i_mtime
,&de
->time
,&de
->date
);
1264 de
->ctime
= de
->time
;
1265 de
->adate
= de
->cdate
= de
->date
;
1267 fat_mark_buffer_dirty(sb
, bh
, 1);
1268 dot
= iget(dir
->i_sb
,ino
);
1271 vfat_read_inode(dot
);
1272 dot
->i_mtime
= dot
->i_atime
= CURRENT_TIME
;
1273 mark_inode_dirty(dot
);
1275 dot
->i_size
= dir
->i_size
;
1276 MSDOS_I(dot
)->i_start
= MSDOS_I(dir
)->i_start
;
1277 MSDOS_I(dot
)->i_logstart
= MSDOS_I(dir
)->i_logstart
;
1278 dot
->i_nlink
= dir
->i_nlink
;
1280 dot
->i_size
= parent
->i_size
;
1281 MSDOS_I(dot
)->i_start
= MSDOS_I(parent
)->i_start
;
1282 MSDOS_I(dot
)->i_logstart
= MSDOS_I(parent
)->i_logstart
;
1283 dot
->i_nlink
= parent
->i_nlink
;
1288 PRINTK3(("vfat_create_a_dotdir 2\n"));
1292 static int vfat_create_dotdirs(struct inode
*dir
, struct inode
*parent
)
1294 struct super_block
*sb
= dir
->i_sb
;
1296 struct buffer_head
*bh
;
1297 struct msdos_dir_entry
*de
;
1300 PRINTK2(("vfat_create_dotdirs: Entering\n"));
1301 if ((res
= fat_add_cluster(dir
)) < 0) return res
;
1303 PRINTK3(("vfat_create_dotdirs 2\n"));
1306 if ((res
= fat_get_entry(dir
,&offset
,&bh
,&de
)) < 0) return res
;
1308 PRINTK3(("vfat_create_dotdirs 3\n"));
1309 res
= vfat_create_a_dotdir(dir
, parent
, bh
, de
, res
, MSDOS_DOT
, 1);
1310 PRINTK3(("vfat_create_dotdirs 4\n"));
1315 PRINTK3(("vfat_create_dotdirs 5\n"));
1317 if ((res
= fat_get_entry(dir
,&offset
,&bh
,&de
)) < 0) {
1321 PRINTK3(("vfat_create_dotdirs 6\n"));
1323 res
= vfat_create_a_dotdir(dir
, parent
, bh
, de
, res
, MSDOS_DOTDOT
, 0);
1324 PRINTK3(("vfat_create_dotdirs 7\n"));
1330 /***** See if directory is empty */
1331 static int vfat_empty(struct inode
*dir
)
1333 struct super_block
*sb
= dir
->i_sb
;
1335 struct buffer_head
*bh
;
1336 struct msdos_dir_entry
*de
;
1338 if (MSDOS_I(dir
)->i_start
) { /* may be zero in mkdir */
1341 while (fat_get_entry(dir
,&pos
,&bh
,&de
) > -1) {
1342 /* Skip extended filename entries */
1343 if (de
->attr
== ATTR_EXT
) continue;
1345 if (!IS_FREE(de
->name
) && strncmp(de
->name
,MSDOS_DOT
,
1346 MSDOS_NAME
) && strncmp(de
->name
,MSDOS_DOTDOT
,
1358 static void vfat_free_ino(struct inode
*dir
,struct buffer_head
*bh
,
1359 struct msdos_dir_entry
*de
,struct inode
* victim
)
1361 struct super_block
*sb
= dir
->i_sb
;
1362 victim
->i_nlink
= 0;
1363 victim
->i_mtime
= dir
->i_mtime
= CURRENT_TIME
;
1364 victim
->i_atime
= dir
->i_atime
= CURRENT_TIME
;
1365 dir
->i_version
= ++event
;
1366 MSDOS_I(victim
)->i_busy
= 1;
1367 mark_inode_dirty(dir
);
1368 mark_inode_dirty(victim
);
1369 de
->name
[0] = DELETED_FLAG
;
1370 fat_mark_buffer_dirty(sb
, bh
, 1);
1373 static int vfat_remove_entry(struct inode
*dir
,struct vfat_slot_info
*sinfo
,
1374 struct inode
* victim
)
1376 struct super_block
*sb
= dir
->i_sb
;
1378 struct buffer_head
*bh
=NULL
;
1379 struct msdos_dir_entry
*de
;
1382 /* remove the shortname */
1383 offset
= sinfo
->shortname_offset
;
1384 res
= fat_get_entry(dir
, &offset
, &bh
, &de
);
1385 if (res
< 0) return res
;
1386 vfat_free_ino(dir
,bh
,de
,victim
);
1387 /* remove the longname */
1388 offset
= sinfo
->longname_offset
;
1389 for (i
= sinfo
->long_slots
; i
> 0; --i
) {
1390 if (fat_get_entry(dir
, &offset
, &bh
, &de
) < 0)
1392 de
->name
[0] = DELETED_FLAG
;
1394 fat_mark_buffer_dirty(sb
, bh
, 1);
1396 if (bh
) fat_brelse(sb
, bh
);
1400 /* Drop all aliases */
1401 static void drop_aliases(struct dentry
*dentry
)
1403 struct list_head
*head
, *next
, *tmp
;
1404 struct dentry
*alias
;
1406 PRINTK1(("drop_replace_inodes: dentry=%p, inode=%p\n", dentry
, inode
));
1407 head
= &dentry
->d_inode
->i_dentry
;
1408 if (dentry
->d_inode
) {
1409 next
= dentry
->d_inode
->i_dentry
.next
;
1410 while (next
!= head
) {
1413 alias
= list_entry(tmp
, struct dentry
, d_alias
);
1414 if (alias
== dentry
)
1422 static int vfat_rmdirx(struct inode
*dir
,struct dentry
* dentry
)
1425 struct vfat_slot_info sinfo
;
1427 PRINTK1(("vfat_rmdirx: dentry=%p\n", dentry
));
1428 res
= vfat_find(dir
,&dentry
->d_name
,0,0,&sinfo
);
1430 if (res
>= 0 && sinfo
.total_slots
> 0) {
1431 if (!list_empty(&dentry
->d_hash
))
1433 /* Take care of aliases */
1434 if (dentry
->d_inode
->i_count
> 1) {
1435 shrink_dcache_parent(dentry
->d_parent
);
1436 if (dentry
->d_inode
->i_count
> 1)
1439 res
= vfat_empty(dentry
->d_inode
);
1443 res
= vfat_remove_entry(dir
,&sinfo
,dentry
->d_inode
);
1452 /***** Remove a directory */
1453 int vfat_rmdir(struct inode
*dir
,struct dentry
* dentry
)
1456 PRINTK1(("vfat_rmdir: dentry=%p, inode=%p\n", dentry
, dentry
->d_inode
));
1459 if (list_empty(&dentry
->d_hash
)) {
1460 res
= vfat_rmdirx(dir
, dentry
);
1461 /* If that went OK all aliases are already dropped */
1466 static int vfat_unlinkx(
1468 struct dentry
* dentry
,
1469 int nospc
) /* Flag special file ? */
1472 struct vfat_slot_info sinfo
;
1474 PRINTK1(("vfat_unlinkx: dentry=%p, inode=%p\n", dentry
, dentry
->d_inode
));
1475 res
= vfat_find(dir
,&dentry
->d_name
,0,0,&sinfo
);
1477 if (res
>= 0 && sinfo
.total_slots
> 0) {
1478 if (!S_ISREG(dentry
->d_inode
->i_mode
) && nospc
) {
1481 res
= vfat_remove_entry(dir
,&sinfo
,dentry
->d_inode
);
1491 int vfat_mkdir(struct inode
*dir
,struct dentry
* dentry
,int mode
)
1493 struct inode
*inode
;
1494 struct vfat_slot_info sinfo
;
1497 PRINTK1(("vfat_mkdir: dentry=%p, inode=%p\n", dentry
, dentry
->d_inode
));
1498 fat_lock_creation();
1499 if ((res
= vfat_create_entry(dir
,&dentry
->d_name
,1,&inode
)) < 0) {
1500 fat_unlock_creation();
1505 inode
->i_nlink
= 2; /* no need to mark them dirty */
1507 res
= vfat_create_dotdirs(inode
, dir
);
1510 fat_unlock_creation();
1511 dentry
->d_time
= dentry
->d_parent
->d_inode
->i_version
;
1512 d_instantiate(dentry
,inode
);
1516 fat_unlock_creation();
1517 if (vfat_find(dir
,&dentry
->d_name
,0,0,&sinfo
) < 0)
1519 if (vfat_remove_entry(dir
, &sinfo
, inode
) < 0)
1526 dir
->i_version
= ++event
;
1527 fat_fs_panic(dir
->i_sb
,"rmdir in mkdir failed");
1531 /***** Unlink, as called for msdosfs */
1532 int vfat_unlink(struct inode
*dir
,struct dentry
* dentry
)
1536 PRINTK1(("vfat_unlink: dentry=%p, inode=%p\n", dentry
, dentry
->d_inode
));
1537 res
= vfat_unlinkx (dir
,dentry
,1);
1539 drop_aliases(dentry
);
1545 /***** Unlink, as called for uvfatfs */
1546 int vfat_unlink_uvfat(struct inode
*dir
,struct dentry
*dentry
)
1550 res
= vfat_unlinkx (dir
,dentry
,0);
1555 int vfat_rename(struct inode
*old_dir
,struct dentry
*old_dentry
,
1556 struct inode
*new_dir
,struct dentry
*new_dentry
)
1558 struct super_block
*sb
= old_dir
->i_sb
;
1559 struct buffer_head
*old_bh
,*new_bh
,*dotdot_bh
;
1560 struct msdos_dir_entry
*old_de
,*dotdot_de
;
1561 loff_t old_offset
,new_offset
,old_longname_offset
;
1562 int old_slots
,old_ino
,new_ino
,dotdot_ino
;
1563 struct inode
*old_inode
, *new_inode
, *dotdot_inode
;
1564 struct dentry
*walk
;
1567 struct vfat_slot_info sinfo
;
1569 PRINTK1(("vfat_rename: Entering: old_dentry=%p, old_inode=%p, old ino=%ld, new_dentry=%p, new_inode=%p, new ino=%ld\n",
1570 old_dentry
, old_dentry
->d_inode
, old_dentry
->d_inode
->i_ino
,
1571 new_dentry
, new_dentry
->d_inode
,
1572 new_dentry
->d_inode
? new_dentry
->d_inode
->i_ino
: 0));
1574 * POSIX is braindead (surprise, surprise). It requires that rename()
1575 * should return 0 and do nothing if the target has the same inode as
1576 * the source. Somebody, get a time machine, return to '89 and tell
1577 * RMS & Co *not* to do that idiocy, FAST!
1579 if (old_dentry
->d_inode
== new_dentry
->d_inode
)
1582 old_bh
= new_bh
= NULL
;
1583 old_inode
= new_inode
= NULL
;
1584 res
= vfat_find(old_dir
,&old_dentry
->d_name
,0,0,&sinfo
);
1585 PRINTK3(("vfat_rename 2\n"));
1586 if (res
< 0) goto rename_done
;
1588 old_slots
= sinfo
.total_slots
;
1589 old_longname_offset
= sinfo
.longname_offset
;
1590 old_offset
= sinfo
.shortname_offset
;
1591 old_ino
= sinfo
.ino
;
1592 res
= fat_get_entry(old_dir
, &old_offset
, &old_bh
, &old_de
);
1593 PRINTK3(("vfat_rename 3\n"));
1594 if (res
< 0) goto rename_done
;
1597 old_inode
= old_dentry
->d_inode
;
1598 is_dir
= S_ISDIR(old_inode
->i_mode
);
1601 * Race: we can be hit by another rename after this check.
1602 * For the time being use fat_lock_creation(), but it's
1606 fat_lock_creation(); locked
= 1;
1609 /* We can't use d_subdir() here. Arrgh. */
1610 for (walk
=new_dentry
;walk
!=walk
->d_parent
;walk
=walk
->d_parent
) {
1611 if (walk
->d_inode
!= old_dentry
->d_inode
)
1618 if (new_dentry
->d_inode
) {
1620 * OK, we have to remove the target. We should do it so
1621 * that nobody might go and find it negative. Actually we
1622 * should give warranties wrt preserving target over the
1623 * possible crash, but that's another story. We can't
1624 * get here with the target unhashed, so the directory entry
1628 new_inode
= new_dentry
->d_inode
;
1629 res
= vfat_find(new_dir
,&new_dentry
->d_name
,0,is_dir
,&sinfo
);
1630 if (res
< 0 || new_inode
->i_ino
!= sinfo
.ino
) {
1631 /* WTF??? Cry and fail. */
1632 printk(KERN_WARNING
"vfat_rename: fs corrupted\n");
1638 * Target is a directory. No other owners will
1642 if (d_invalidate(new_dentry
) < 0)
1645 * OK, let's try to get rid of other dentries.
1646 * No need to do it if i_count is 1.
1648 if (new_inode
->i_count
>1) {
1649 shrink_dcache_parent(new_dentry
->d_parent
);
1650 if (new_inode
->i_count
>1)
1653 res
= vfat_empty(new_inode
);
1657 drop_aliases(new_dentry
);
1659 res
= vfat_remove_entry(new_dir
,&sinfo
,new_inode
);
1664 /* Serious lossage here. FAT uses braindead inode numbers scheme,
1665 * so we can't simply cannibalize the entry. It means that we have
1666 * no warranties that crash here will not make target disappear
1667 * after reboot. Lose, lose. Nothing to do with that until we'll
1668 * separate the functions of i_ino: it serves both as a search key
1669 * in icache and as a part of stat output. It would kill all the
1670 * 'busy' stuff on the spot. Later.
1676 res
= vfat_find(new_dir
,&new_dentry
->d_name
,1,is_dir
,&sinfo
);
1678 if (res
< 0) goto rename_done
;
1680 new_offset
= sinfo
.shortname_offset
;
1681 new_ino
= sinfo
.ino
;
1683 /* XXX: take care of other owners */
1685 remove_inode_hash(old_inode
);
1686 fat_cache_inval_inode(old_inode
);
1687 old_inode
->i_ino
= new_ino
;
1688 old_inode
->i_version
= ++event
;
1689 insert_inode_hash(old_inode
);
1690 mark_inode_dirty(old_inode
);
1692 old_dir
->i_version
= ++event
;
1693 new_dir
->i_version
= ++event
;
1695 /* remove the old entry */
1696 for (i
= old_slots
; i
> 0; --i
) {
1697 res
= fat_get_entry(old_dir
, &old_longname_offset
, &old_bh
, &old_de
);
1699 printk("vfat_rename: problem 1\n");
1702 old_de
->name
[0] = DELETED_FLAG
;
1704 fat_mark_buffer_dirty(sb
, old_bh
, 1);
1708 if ((res
= fat_scan(old_inode
,MSDOS_DOTDOT
,&dotdot_bh
,
1709 &dotdot_de
,&dotdot_ino
,SCAN_ANY
)) < 0) goto rename_done
;
1710 if (!(dotdot_inode
= iget(old_inode
->i_sb
,dotdot_ino
))) {
1711 fat_brelse(sb
, dotdot_bh
);
1715 MSDOS_I(dotdot_inode
)->i_start
= MSDOS_I(new_dir
)->i_start
;
1716 MSDOS_I(dotdot_inode
)->i_logstart
= MSDOS_I(new_dir
)->i_logstart
;
1717 dotdot_de
->start
= CT_LE_W(MSDOS_I(new_dir
)->i_logstart
);
1718 dotdot_de
->starthi
= CT_LE_W((MSDOS_I(new_dir
)->i_logstart
) >> 16);
1719 mark_inode_dirty(dotdot_inode
);
1720 fat_mark_buffer_dirty(sb
, dotdot_bh
, 1);
1723 /* no need to mark them dirty */
1724 dotdot_inode
->i_nlink
= new_dir
->i_nlink
;
1726 fat_brelse(sb
, dotdot_bh
);
1730 if (new_inode
&& is_dir
)
1731 d_rehash(new_dentry
);
1732 d_move(old_dentry
, new_dentry
);
1738 fat_unlock_creation();
1740 fat_brelse(sb
, old_bh
);
1742 fat_brelse(sb
, new_bh
);
1749 /* Public inode operations for the VFAT fs */
1750 struct inode_operations vfat_dir_inode_operations
= {
1751 &fat_dir_operations
, /* default directory file-ops */
1752 vfat_create
, /* create */
1753 vfat_lookup
, /* lookup */
1755 vfat_unlink
, /* unlink */
1757 vfat_mkdir
, /* mkdir */
1758 vfat_rmdir
, /* rmdir */
1760 vfat_rename
, /* rename */
1761 NULL
, /* readlink */
1762 NULL
, /* followlink */
1763 NULL
, /* readpage */
1764 NULL
, /* writepage */
1766 NULL
, /* truncate */
1767 NULL
/* permission */
1771 void vfat_read_inode(struct inode
*inode
)
1773 fat_read_inode(inode
, &vfat_dir_inode_operations
);
1777 int init_module(void)
1779 return init_vfat_fs();
1782 void cleanup_module(void)
1784 unregister_filesystem(&vfat_fs_type
);
1787 #endif /* ifdef MODULE */