2 * linux/fs/msdos/namei.c
4 * Written 1992,1993 by Werner Almesberger
5 * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
6 * Rewritten for constant inumbers 1999 by Al Viro
9 #include <linux/module.h>
10 #include <linux/time.h>
11 #include <linux/buffer_head.h>
12 #include <linux/msdos_fs.h>
13 #include <linux/smp_lock.h>
15 /* MS-DOS "device special files" */
16 static const unsigned char *reserved_names
[] = {
17 "CON ", "PRN ", "NUL ", "AUX ",
18 "LPT1 ", "LPT2 ", "LPT3 ", "LPT4 ",
19 "COM1 ", "COM2 ", "COM3 ", "COM4 ",
23 /* Characters that are undesirable in an MS-DOS file name */
24 static unsigned char bad_chars
[] = "*?<>|\"";
25 static unsigned char bad_if_strict_pc
[] = "+=,; ";
26 /* GEMDOS is less restrictive */
27 static unsigned char bad_if_strict_atari
[] = " ";
29 #define bad_if_strict(opts) \
30 ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
32 /***** Formats an MS-DOS file name. Rejects invalid names. */
33 static int msdos_format_name(const unsigned char *name
, int len
,
34 unsigned char *res
, struct fat_mount_options
*opts
)
36 * name is the proposed name, len is its length, res is
37 * the resulting name, opts->name_check is either (r)elaxed,
38 * (n)ormal or (s)trict, opts->dotsOK allows dots at the
39 * beginning of name (for hidden files)
43 const unsigned char **reserved
;
47 if (name
[0] == '.') { /* dotfile because . and .. already done */
49 /* Get rid of dot - test for it elsewhere */
52 } else if (!opts
->atari
)
56 * disallow names that _really_ start with a dot for MS-DOS,
57 * GEMDOS does not care
61 for (walk
= res
; len
&& walk
- res
< 8; walk
++) {
64 if (opts
->name_check
!= 'r' && strchr(bad_chars
, c
))
66 if (opts
->name_check
== 's' && strchr(bad_if_strict(opts
), c
))
68 if (c
>= 'A' && c
<= 'Z' && opts
->name_check
== 's')
70 if (c
< ' ' || c
== ':' || c
== '\\')
73 * 0xE5 is legal as a first character, but we must substitute
74 * 0x05 because 0xE5 marks deleted files. Yes, DOS really
76 * It seems that Microsoft hacked DOS to support non-US
77 * characters after the 0xE5 character was already in use to
80 if ((res
== walk
) && (c
== 0xE5))
85 *walk
= (!opts
->nocase
&& c
>= 'a' && c
<= 'z') ? c
- 32 : c
;
89 if (opts
->name_check
== 's' && len
&& c
!= '.') {
95 while (c
!= '.' && len
--)
98 while (walk
- res
< 8)
100 while (len
> 0 && walk
- res
< MSDOS_NAME
) {
103 if (opts
->name_check
!= 'r' && strchr(bad_chars
, c
))
105 if (opts
->name_check
== 's' &&
106 strchr(bad_if_strict(opts
), c
))
108 if (c
< ' ' || c
== ':' || c
== '\\')
111 if (opts
->name_check
== 's')
115 if (c
>= 'A' && c
<= 'Z' && opts
->name_check
== 's')
118 if (!opts
->nocase
&& c
>= 'a' && c
<= 'z')
125 if (opts
->name_check
== 's' && len
)
128 while (walk
- res
< MSDOS_NAME
)
131 /* GEMDOS is less stupid and has no reserved names */
132 for (reserved
= reserved_names
; *reserved
; reserved
++)
133 if (!strncmp(res
, *reserved
, 8))
138 /***** Locates a directory entry. Uses unformatted name. */
139 static int msdos_find(struct inode
*dir
, const unsigned char *name
, int len
,
140 struct fat_slot_info
*sinfo
)
142 struct msdos_sb_info
*sbi
= MSDOS_SB(dir
->i_sb
);
143 unsigned char msdos_name
[MSDOS_NAME
];
146 err
= msdos_format_name(name
, len
, msdos_name
, &sbi
->options
);
150 err
= fat_scan(dir
, msdos_name
, sinfo
);
151 if (!err
&& sbi
->options
.dotsOK
) {
152 if (name
[0] == '.') {
153 if (!(sinfo
->de
->attr
& ATTR_HIDDEN
))
156 if (sinfo
->de
->attr
& ATTR_HIDDEN
)
166 * Compute the hash for the msdos name corresponding to the dentry.
167 * Note: if the name is invalid, we leave the hash code unchanged so
168 * that the existing dentry can be used. The msdos fs routines will
169 * return ENOENT or EINVAL as appropriate.
171 static int msdos_hash(struct dentry
*dentry
, struct qstr
*qstr
)
173 struct fat_mount_options
*options
= &MSDOS_SB(dentry
->d_sb
)->options
;
174 unsigned char msdos_name
[MSDOS_NAME
];
177 error
= msdos_format_name(qstr
->name
, qstr
->len
, msdos_name
, options
);
179 qstr
->hash
= full_name_hash(msdos_name
, MSDOS_NAME
);
184 * Compare two msdos names. If either of the names are invalid,
185 * we fall back to doing the standard name comparison.
187 static int msdos_cmp(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
)
189 struct fat_mount_options
*options
= &MSDOS_SB(dentry
->d_sb
)->options
;
190 unsigned char a_msdos_name
[MSDOS_NAME
], b_msdos_name
[MSDOS_NAME
];
193 error
= msdos_format_name(a
->name
, a
->len
, a_msdos_name
, options
);
196 error
= msdos_format_name(b
->name
, b
->len
, b_msdos_name
, options
);
199 error
= memcmp(a_msdos_name
, b_msdos_name
, MSDOS_NAME
);
205 if (a
->len
== b
->len
)
206 error
= memcmp(a
->name
, b
->name
, a
->len
);
210 static struct dentry_operations msdos_dentry_operations
= {
211 .d_hash
= msdos_hash
,
212 .d_compare
= msdos_cmp
,
216 * AV. Wrappers for FAT sb operations. Is it wise?
219 /***** Get inode using directory and name */
220 static struct dentry
*msdos_lookup(struct inode
*dir
, struct dentry
*dentry
,
221 struct nameidata
*nd
)
223 struct super_block
*sb
= dir
->i_sb
;
224 struct fat_slot_info sinfo
;
225 struct inode
*inode
= NULL
;
228 dentry
->d_op
= &msdos_dentry_operations
;
231 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
, &sinfo
);
236 inode
= fat_build_inode(sb
, sinfo
.de
, sinfo
.i_pos
);
239 res
= PTR_ERR(inode
);
244 dentry
= d_splice_alias(inode
, dentry
);
246 dentry
->d_op
= &msdos_dentry_operations
;
254 /***** Creates a directory entry (name is already formatted). */
255 static int msdos_add_entry(struct inode
*dir
, const unsigned char *name
,
256 int is_dir
, int is_hid
, int cluster
,
257 struct timespec
*ts
, struct fat_slot_info
*sinfo
)
259 struct msdos_dir_entry de
;
263 memcpy(de
.name
, name
, MSDOS_NAME
);
264 de
.attr
= is_dir
? ATTR_DIR
: ATTR_ARCH
;
266 de
.attr
|= ATTR_HIDDEN
;
268 fat_date_unix2dos(ts
->tv_sec
, &time
, &date
);
269 de
.cdate
= de
.adate
= 0;
274 de
.start
= cpu_to_le16(cluster
);
275 de
.starthi
= cpu_to_le16(cluster
>> 16);
278 err
= fat_add_entries(dir
, &de
, 1, sinfo
);
282 dir
->i_ctime
= dir
->i_mtime
= *ts
;
284 (void)fat_sync_inode(dir
);
286 mark_inode_dirty(dir
);
291 /***** Create a file */
292 static int msdos_create(struct inode
*dir
, struct dentry
*dentry
, int mode
,
293 struct nameidata
*nd
)
295 struct super_block
*sb
= dir
->i_sb
;
297 struct fat_slot_info sinfo
;
299 unsigned char msdos_name
[MSDOS_NAME
];
304 err
= msdos_format_name(dentry
->d_name
.name
, dentry
->d_name
.len
,
305 msdos_name
, &MSDOS_SB(sb
)->options
);
308 is_hid
= (dentry
->d_name
.name
[0] == '.') && (msdos_name
[0] != '.');
309 /* Have to do it due to foo vs. .foo conflicts */
310 if (!fat_scan(dir
, msdos_name
, &sinfo
)) {
316 ts
= CURRENT_TIME_SEC
;
317 err
= msdos_add_entry(dir
, msdos_name
, 0, is_hid
, 0, &ts
, &sinfo
);
320 inode
= fat_build_inode(sb
, sinfo
.de
, sinfo
.i_pos
);
323 err
= PTR_ERR(inode
);
326 inode
->i_mtime
= inode
->i_atime
= inode
->i_ctime
= ts
;
327 /* timestamp is already written, so mark_inode_dirty() is unneeded. */
329 d_instantiate(dentry
, inode
);
335 /***** Remove a directory */
336 static int msdos_rmdir(struct inode
*dir
, struct dentry
*dentry
)
338 struct inode
*inode
= dentry
->d_inode
;
339 struct fat_slot_info sinfo
;
344 * Check whether the directory is not in use, then check
345 * whether it is empty.
347 err
= fat_dir_empty(inode
);
350 err
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
, &sinfo
);
354 err
= fat_remove_entries(dir
, &sinfo
); /* and releases bh */
360 inode
->i_ctime
= CURRENT_TIME_SEC
;
368 /***** Make a directory */
369 static int msdos_mkdir(struct inode
*dir
, struct dentry
*dentry
, int mode
)
371 struct super_block
*sb
= dir
->i_sb
;
372 struct fat_slot_info sinfo
;
374 unsigned char msdos_name
[MSDOS_NAME
];
376 int err
, is_hid
, cluster
;
380 err
= msdos_format_name(dentry
->d_name
.name
, dentry
->d_name
.len
,
381 msdos_name
, &MSDOS_SB(sb
)->options
);
384 is_hid
= (dentry
->d_name
.name
[0] == '.') && (msdos_name
[0] != '.');
385 /* foo vs .foo situation */
386 if (!fat_scan(dir
, msdos_name
, &sinfo
)) {
392 ts
= CURRENT_TIME_SEC
;
393 cluster
= fat_alloc_new_dir(dir
, &ts
);
398 err
= msdos_add_entry(dir
, msdos_name
, 1, is_hid
, cluster
, &ts
, &sinfo
);
403 inode
= fat_build_inode(sb
, sinfo
.de
, sinfo
.i_pos
);
406 err
= PTR_ERR(inode
);
407 /* the directory was completed, just return a error */
411 inode
->i_mtime
= inode
->i_atime
= inode
->i_ctime
= ts
;
412 /* timestamp is already written, so mark_inode_dirty() is unneeded. */
414 d_instantiate(dentry
, inode
);
420 fat_free_clusters(dir
, cluster
);
426 /***** Unlink a file */
427 static int msdos_unlink(struct inode
*dir
, struct dentry
*dentry
)
429 struct inode
*inode
= dentry
->d_inode
;
430 struct fat_slot_info sinfo
;
434 err
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
, &sinfo
);
438 err
= fat_remove_entries(dir
, &sinfo
); /* and releases bh */
442 inode
->i_ctime
= CURRENT_TIME_SEC
;
450 static int do_msdos_rename(struct inode
*old_dir
, unsigned char *old_name
,
451 struct dentry
*old_dentry
,
452 struct inode
*new_dir
, unsigned char *new_name
,
453 struct dentry
*new_dentry
, int is_hid
)
455 struct buffer_head
*dotdot_bh
;
456 struct msdos_dir_entry
*dotdot_de
;
458 struct inode
*old_inode
, *new_inode
;
459 struct fat_slot_info old_sinfo
, sinfo
;
461 int err
, old_attrs
, is_dir
, update_dotdot
, corrupt
= 0;
463 old_sinfo
.bh
= sinfo
.bh
= dotdot_bh
= NULL
;
464 old_inode
= old_dentry
->d_inode
;
465 new_inode
= new_dentry
->d_inode
;
467 err
= fat_scan(old_dir
, old_name
, &old_sinfo
);
473 is_dir
= S_ISDIR(old_inode
->i_mode
);
474 update_dotdot
= (is_dir
&& old_dir
!= new_dir
);
476 if (fat_get_dotdot_entry(old_inode
, &dotdot_bh
, &dotdot_de
,
477 &dotdot_i_pos
) < 0) {
483 old_attrs
= MSDOS_I(old_inode
)->i_attrs
;
484 err
= fat_scan(new_dir
, new_name
, &sinfo
);
487 /* "foo" -> ".foo" case. just change the ATTR_HIDDEN */
488 if (sinfo
.de
!= old_sinfo
.de
) {
493 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
495 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
496 if (IS_DIRSYNC(old_dir
)) {
497 err
= fat_sync_inode(old_inode
);
499 MSDOS_I(old_inode
)->i_attrs
= old_attrs
;
503 mark_inode_dirty(old_inode
);
505 old_dir
->i_version
++;
506 old_dir
->i_ctime
= old_dir
->i_mtime
= CURRENT_TIME_SEC
;
507 if (IS_DIRSYNC(old_dir
))
508 (void)fat_sync_inode(old_dir
);
510 mark_inode_dirty(old_dir
);
515 ts
= CURRENT_TIME_SEC
;
519 if (MSDOS_I(new_inode
)->i_pos
!= sinfo
.i_pos
) {
520 /* WTF??? Cry and fail. */
521 printk(KERN_WARNING
"msdos_rename: fs corrupted\n");
526 err
= fat_dir_empty(new_inode
);
530 fat_detach(new_inode
);
532 err
= msdos_add_entry(new_dir
, new_name
, is_dir
, is_hid
, 0,
537 new_dir
->i_version
++;
539 fat_detach(old_inode
);
540 fat_attach(old_inode
, sinfo
.i_pos
);
542 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
544 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
545 if (IS_DIRSYNC(new_dir
)) {
546 err
= fat_sync_inode(old_inode
);
550 mark_inode_dirty(old_inode
);
553 int start
= MSDOS_I(new_dir
)->i_logstart
;
554 dotdot_de
->start
= cpu_to_le16(start
);
555 dotdot_de
->starthi
= cpu_to_le16(start
>> 16);
556 mark_buffer_dirty(dotdot_bh
);
557 if (IS_DIRSYNC(new_dir
)) {
558 err
= sync_dirty_buffer(dotdot_bh
);
567 err
= fat_remove_entries(old_dir
, &old_sinfo
); /* and releases bh */
571 old_dir
->i_version
++;
572 old_dir
->i_ctime
= old_dir
->i_mtime
= ts
;
573 if (IS_DIRSYNC(old_dir
))
574 (void)fat_sync_inode(old_dir
);
576 mark_inode_dirty(old_dir
);
580 new_inode
->i_nlink
-= 2;
582 new_inode
->i_nlink
--;
583 new_inode
->i_ctime
= ts
;
588 brelse(old_sinfo
.bh
);
592 /* data cluster is shared, serious corruption */
596 int start
= MSDOS_I(old_dir
)->i_logstart
;
597 dotdot_de
->start
= cpu_to_le16(start
);
598 dotdot_de
->starthi
= cpu_to_le16(start
>> 16);
599 mark_buffer_dirty(dotdot_bh
);
600 corrupt
|= sync_dirty_buffer(dotdot_bh
);
603 fat_detach(old_inode
);
604 fat_attach(old_inode
, old_sinfo
.i_pos
);
605 MSDOS_I(old_inode
)->i_attrs
= old_attrs
;
607 fat_attach(new_inode
, sinfo
.i_pos
);
609 corrupt
|= fat_sync_inode(new_inode
);
612 * If new entry was not sharing the data cluster, it
613 * shouldn't be serious corruption.
615 int err2
= fat_remove_entries(new_dir
, &sinfo
);
621 fat_fs_panic(new_dir
->i_sb
,
622 "%s: Filesystem corrupted (i_pos %lld)",
623 __FUNCTION__
, sinfo
.i_pos
);
628 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
629 static int msdos_rename(struct inode
*old_dir
, struct dentry
*old_dentry
,
630 struct inode
*new_dir
, struct dentry
*new_dentry
)
632 unsigned char old_msdos_name
[MSDOS_NAME
], new_msdos_name
[MSDOS_NAME
];
637 err
= msdos_format_name(old_dentry
->d_name
.name
,
638 old_dentry
->d_name
.len
, old_msdos_name
,
639 &MSDOS_SB(old_dir
->i_sb
)->options
);
642 err
= msdos_format_name(new_dentry
->d_name
.name
,
643 new_dentry
->d_name
.len
, new_msdos_name
,
644 &MSDOS_SB(new_dir
->i_sb
)->options
);
649 (new_dentry
->d_name
.name
[0] == '.') && (new_msdos_name
[0] != '.');
651 err
= do_msdos_rename(old_dir
, old_msdos_name
, old_dentry
,
652 new_dir
, new_msdos_name
, new_dentry
, is_hid
);
658 static struct inode_operations msdos_dir_inode_operations
= {
659 .create
= msdos_create
,
660 .lookup
= msdos_lookup
,
661 .unlink
= msdos_unlink
,
662 .mkdir
= msdos_mkdir
,
663 .rmdir
= msdos_rmdir
,
664 .rename
= msdos_rename
,
665 .setattr
= fat_notify_change
,
668 static int msdos_fill_super(struct super_block
*sb
, void *data
, int silent
)
672 res
= fat_fill_super(sb
, data
, silent
, &msdos_dir_inode_operations
, 0);
676 sb
->s_flags
|= MS_NOATIME
;
677 sb
->s_root
->d_op
= &msdos_dentry_operations
;
681 static struct super_block
*msdos_get_sb(struct file_system_type
*fs_type
,
682 int flags
, const char *dev_name
,
685 return get_sb_bdev(fs_type
, flags
, dev_name
, data
, msdos_fill_super
);
688 static struct file_system_type msdos_fs_type
= {
689 .owner
= THIS_MODULE
,
691 .get_sb
= msdos_get_sb
,
692 .kill_sb
= kill_block_super
,
693 .fs_flags
= FS_REQUIRES_DEV
,
696 static int __init
init_msdos_fs(void)
698 return register_filesystem(&msdos_fs_type
);
701 static void __exit
exit_msdos_fs(void)
703 unregister_filesystem(&msdos_fs_type
);
706 MODULE_LICENSE("GPL");
707 MODULE_AUTHOR("Werner Almesberger");
708 MODULE_DESCRIPTION("MS-DOS filesystem support");
710 module_init(init_msdos_fs
)
711 module_exit(exit_msdos_fs
)