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>
16 /* MS-DOS "device special files" */
17 static const unsigned char *reserved_names[] = {
18 "CON ","PRN ","NUL ","AUX ",
19 "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
20 "COM1 ","COM2 ","COM3 ","COM4 ",
24 /* Characters that are undesirable in an MS-DOS file name */
25 static unsigned char bad_chars[] = "*?<>|\"";
26 static unsigned char bad_if_strict_pc[] = "+=,; ";
27 static unsigned char bad_if_strict_atari[] = " "; /* GEMDOS is less restrictive */
28 #define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
30 /***** Formats an MS-DOS file name. Rejects invalid names. */
31 static int msdos_format_name(const unsigned char *name, int len,
32 unsigned char *res, struct fat_mount_options *opts)
33 /* name is the proposed name, len is its length, res is
34 * the resulting name, opts->name_check is either (r)elaxed,
35 * (n)ormal or (s)trict, opts->dotsOK allows dots at the
36 * beginning of name (for hidden files)
40 const unsigned char **reserved;
44 if (name[0] == '.') { /* dotfile because . and .. already done */
46 /* Get rid of dot - test for it elsewhere */
49 else if (!opts->atari) return -EINVAL;
51 /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
55 for (walk = res; len && walk-res < 8; walk++) {
58 if (opts->name_check != 'r' && strchr(bad_chars,c))
60 if (opts->name_check == 's' && strchr(bad_if_strict(opts),c))
62 if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
64 if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
65 /* 0xE5 is legal as a first character, but we must substitute 0x05 */
66 /* because 0xE5 marks deleted files. Yes, DOS really does this. */
67 /* It seems that Microsoft hacked DOS to support non-US characters */
68 /* after the 0xE5 character was already in use to mark deleted files. */
69 if((res==walk) && (c==0xE5)) c=0x05;
72 *walk = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
74 if (space) return -EINVAL;
75 if (opts->name_check == 's' && len && c != '.') {
78 if (c != '.') return -EINVAL;
80 while (c != '.' && len--) c = *name++;
82 while (walk-res < 8) *walk++ = ' ';
83 while (len > 0 && walk-res < MSDOS_NAME) {
86 if (opts->name_check != 'r' && strchr(bad_chars,c))
88 if (opts->name_check == 's' &&
89 strchr(bad_if_strict(opts),c))
91 if (c < ' ' || c == ':' || c == '\\')
94 if (opts->name_check == 's')
98 if (c >= 'A' && c <= 'Z' && opts->name_check == 's')
101 *walk++ = (!opts->nocase && c >= 'a' && c <= 'z') ? c-32 : c;
103 if (space) return -EINVAL;
104 if (opts->name_check == 's' && len) return -EINVAL;
106 while (walk-res < MSDOS_NAME) *walk++ = ' ';
108 /* GEMDOS is less stupid and has no reserved names */
109 for (reserved = reserved_names; *reserved; reserved++)
110 if (!strncmp(res,*reserved,8)) return -EINVAL;
114 /***** Locates a directory entry. Uses unformatted name. */
115 static int msdos_find(struct inode *dir, const unsigned char *name, int len,
116 struct buffer_head **bh, struct msdos_dir_entry **de,
119 unsigned char msdos_name[MSDOS_NAME];
123 dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
124 res = msdos_format_name(name,len, msdos_name,&MSDOS_SB(dir->i_sb)->options);
127 res = fat_scan(dir, msdos_name, bh, de, i_pos);
128 if (!res && dotsOK) {
130 if (!((*de)->attr & ATTR_HIDDEN))
133 if ((*de)->attr & ATTR_HIDDEN)
141 * Compute the hash for the msdos name corresponding to the dentry.
142 * Note: if the name is invalid, we leave the hash code unchanged so
143 * that the existing dentry can be used. The msdos fs routines will
144 * return ENOENT or EINVAL as appropriate.
146 static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
148 struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
149 unsigned char msdos_name[MSDOS_NAME];
152 error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
154 qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
159 * Compare two msdos names. If either of the names are invalid,
160 * we fall back to doing the standard name comparison.
162 static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
164 struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
165 unsigned char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
168 error = msdos_format_name(a->name, a->len, a_msdos_name, options);
171 error = msdos_format_name(b->name, b->len, b_msdos_name, options);
174 error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
180 if (a->len == b->len)
181 error = memcmp(a->name, b->name, a->len);
186 static struct dentry_operations msdos_dentry_operations = {
187 .d_hash = msdos_hash,
188 .d_compare = msdos_cmp,
192 * AV. Wrappers for FAT sb operations. Is it wise?
195 /***** Get inode using directory and name */
196 static struct dentry *msdos_lookup(struct inode *dir, struct dentry *dentry,
197 struct nameidata *nd)
199 struct super_block *sb = dir->i_sb;
200 struct inode *inode = NULL;
201 struct msdos_dir_entry *de;
202 struct buffer_head *bh = NULL;
206 dentry->d_op = &msdos_dentry_operations;
209 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len, &bh,
215 inode = fat_build_inode(sb, de, i_pos, &res);
220 dentry = d_splice_alias(inode, dentry);
222 dentry->d_op = &msdos_dentry_operations;
231 /***** Creates a directory entry (name is already formatted). */
232 static int msdos_add_entry(struct inode *dir, const unsigned char *name,
233 struct buffer_head **bh,
234 struct msdos_dir_entry **de,
235 loff_t *i_pos, int is_dir, int is_hid)
239 res = fat_add_entries(dir, 1, bh, de, i_pos);
244 * XXX all times should be set by caller upon successful completion.
246 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
247 mark_inode_dirty(dir);
249 memcpy((*de)->name, name, MSDOS_NAME);
250 (*de)->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
252 (*de)->attr |= ATTR_HIDDEN;
255 fat_date_unix2dos(dir->i_mtime.tv_sec, &(*de)->time, &(*de)->date);
257 mark_buffer_dirty(*bh);
261 /***** Create a file */
262 static int msdos_create(struct inode *dir, struct dentry *dentry, int mode,
263 struct nameidata *nd)
265 struct super_block *sb = dir->i_sb;
266 struct buffer_head *bh;
267 struct msdos_dir_entry *de;
271 unsigned char msdos_name[MSDOS_NAME];
274 res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
275 msdos_name, &MSDOS_SB(sb)->options);
280 is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
281 /* Have to do it due to foo vs. .foo conflicts */
282 if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0) {
288 res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 0, is_hid);
293 inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
299 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
300 mark_inode_dirty(inode);
301 d_instantiate(dentry, inode);
306 /***** Remove a directory */
307 static int msdos_rmdir(struct inode *dir, struct dentry *dentry)
309 struct inode *inode = dentry->d_inode;
312 struct buffer_head *bh;
313 struct msdos_dir_entry *de;
317 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
322 * Check whether the directory is not in use, then check
323 * whether it is empty.
325 res = fat_dir_empty(inode);
329 de->name[0] = DELETED_FLAG;
330 mark_buffer_dirty(bh);
333 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
335 mark_inode_dirty(inode);
336 mark_inode_dirty(dir);
345 /***** Make a directory */
346 static int msdos_mkdir(struct inode *dir, struct dentry *dentry, int mode)
348 struct super_block *sb = dir->i_sb;
349 struct buffer_head *bh;
350 struct msdos_dir_entry *de;
353 unsigned char msdos_name[MSDOS_NAME];
357 res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
358 msdos_name, &MSDOS_SB(sb)->options);
363 is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
364 /* foo vs .foo situation */
365 if (fat_scan(dir, msdos_name, &bh, &de, &i_pos) >= 0)
368 res = msdos_add_entry(dir, msdos_name, &bh, &de, &i_pos, 1, is_hid);
371 inode = fat_build_inode(dir->i_sb, de, i_pos, &res);
379 inode->i_nlink = 2; /* no need to mark them dirty */
381 res = fat_new_dir(inode, dir, 0);
386 d_instantiate(dentry, inode);
395 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
397 mark_inode_dirty(inode);
398 mark_inode_dirty(dir);
399 de->name[0] = DELETED_FLAG;
400 mark_buffer_dirty(bh);
412 /***** Unlink a file */
413 static int msdos_unlink(struct inode *dir, struct dentry *dentry)
415 struct inode *inode = dentry->d_inode;
418 struct buffer_head *bh;
419 struct msdos_dir_entry *de;
423 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
428 de->name[0] = DELETED_FLAG;
429 mark_buffer_dirty(bh);
433 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
434 mark_inode_dirty(inode);
435 mark_inode_dirty(dir);
442 static int do_msdos_rename(struct inode *old_dir, unsigned char *old_name,
443 struct dentry *old_dentry,
444 struct inode *new_dir, unsigned char *new_name, struct dentry *new_dentry,
445 struct buffer_head *old_bh,
446 struct msdos_dir_entry *old_de, loff_t old_i_pos, int is_hid)
448 struct buffer_head *new_bh=NULL,*dotdot_bh=NULL;
449 struct msdos_dir_entry *new_de,*dotdot_de;
450 struct inode *old_inode,*new_inode;
451 loff_t new_i_pos, dotdot_i_pos;
455 old_inode = old_dentry->d_inode;
456 new_inode = new_dentry->d_inode;
457 is_dir = S_ISDIR(old_inode->i_mode);
459 if (fat_scan(new_dir, new_name, &new_bh, &new_de, &new_i_pos) >= 0
461 goto degenerate_case;
464 error = fat_dir_empty(new_inode);
468 if (fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
469 &dotdot_de, &dotdot_i_pos) < 0) {
475 error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
476 &new_i_pos, is_dir, is_hid);
480 new_dir->i_version++;
485 fat_detach(new_inode);
486 old_de->name[0] = DELETED_FLAG;
487 mark_buffer_dirty(old_bh);
488 fat_detach(old_inode);
489 fat_attach(old_inode, new_i_pos);
491 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
493 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
494 mark_inode_dirty(old_inode);
495 old_dir->i_version++;
496 old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
497 mark_inode_dirty(old_dir);
499 new_inode->i_nlink--;
500 new_inode->i_ctime = CURRENT_TIME;
501 mark_inode_dirty(new_inode);
504 dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
505 dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
506 mark_buffer_dirty(dotdot_bh);
508 mark_inode_dirty(old_dir);
510 new_inode->i_nlink--;
511 mark_inode_dirty(new_inode);
514 mark_inode_dirty(new_dir);
528 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
530 MSDOS_I(old_inode)->i_attrs &= ~ATTR_HIDDEN;
531 mark_inode_dirty(old_inode);
532 old_dir->i_version++;
533 old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
534 mark_inode_dirty(old_dir);
538 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
539 static int msdos_rename(struct inode *old_dir, struct dentry *old_dentry,
540 struct inode *new_dir, struct dentry *new_dentry)
542 struct buffer_head *old_bh;
543 struct msdos_dir_entry *old_de;
545 int error, is_hid, old_hid; /* if new file and old file are hidden */
546 unsigned char old_msdos_name[MSDOS_NAME], new_msdos_name[MSDOS_NAME];
549 error = msdos_format_name(old_dentry->d_name.name,
550 old_dentry->d_name.len,old_msdos_name,
551 &MSDOS_SB(old_dir->i_sb)->options);
554 error = msdos_format_name(new_dentry->d_name.name,
555 new_dentry->d_name.len,new_msdos_name,
556 &MSDOS_SB(new_dir->i_sb)->options);
560 is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
561 old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
562 error = fat_scan(old_dir, old_msdos_name, &old_bh, &old_de, &old_i_pos);
566 error = do_msdos_rename(old_dir, old_msdos_name, old_dentry,
567 new_dir, new_msdos_name, new_dentry,
568 old_bh, old_de, old_i_pos, is_hid);
576 static struct inode_operations msdos_dir_inode_operations = {
577 .create = msdos_create,
578 .lookup = msdos_lookup,
579 .unlink = msdos_unlink,
580 .mkdir = msdos_mkdir,
581 .rmdir = msdos_rmdir,
582 .rename = msdos_rename,
583 .setattr = fat_notify_change,
586 static int msdos_fill_super(struct super_block *sb,void *data, int silent)
590 res = fat_fill_super(sb, data, silent, &msdos_dir_inode_operations, 0);
594 sb->s_root->d_op = &msdos_dentry_operations;
598 static struct super_block *msdos_get_sb(struct file_system_type *fs_type,
599 int flags, const char *dev_name, void *data)
601 return get_sb_bdev(fs_type, flags, dev_name, data, msdos_fill_super);
604 static struct file_system_type msdos_fs_type = {
605 .owner = THIS_MODULE,
607 .get_sb = msdos_get_sb,
608 .kill_sb = kill_block_super,
609 .fs_flags = FS_REQUIRES_DEV,
612 static int __init init_msdos_fs(void)
614 return register_filesystem(&msdos_fs_type);
617 static void __exit exit_msdos_fs(void)
619 unregister_filesystem(&msdos_fs_type);
622 MODULE_LICENSE("GPL");
623 MODULE_AUTHOR("Werner Almesberger");
624 MODULE_DESCRIPTION("MS-DOS filesystem support");
626 module_init(init_msdos_fs)
627 module_exit(exit_msdos_fs)