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
10 #define __NO_VERSION__
11 #include <linux/module.h>
13 #include <linux/sched.h>
14 #include <linux/msdos_fs.h>
15 #include <linux/errno.h>
16 #include <linux/string.h>
18 #include <asm/uaccess.h>
20 #include "../fat/msbuffer.h"
25 /* MS-DOS "device special files" */
27 static const char *reserved_names
[] = {
28 "CON ","PRN ","NUL ","AUX ",
29 "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
30 "COM1 ","COM2 ","COM3 ","COM4 ",
34 /* Characters that are undesirable in an MS-DOS file name */
36 static char bad_chars
[] = "*?<>|\"";
37 static char bad_if_strict_pc
[] = "+=,; ";
38 static char bad_if_strict_atari
[] = " "; /* GEMDOS is less restrictive */
39 #define bad_if_strict(opts) ((opts)->atari ? bad_if_strict_atari : bad_if_strict_pc)
42 void msdos_put_super(struct super_block
*sb
)
47 /***** Formats an MS-DOS file name. Rejects invalid names. */
48 static int msdos_format_name(const char *name
,int len
,
49 char *res
,struct fat_mount_options
*opts
)
50 /* conv is relaxed/normal/strict, name is proposed name,
51 * len is the length of the proposed name, res is the result name,
52 * dotsOK is if hidden files get dots.
56 const char **reserved
;
60 if (name
[0] == '.') { /* dotfile because . and .. already done */
62 /* Get rid of dot - test for it elsewhere */
65 else if (!opts
->atari
) return -EINVAL
;
67 /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
71 for (walk
= res
; len
&& walk
-res
< 8; walk
++) {
74 if (opts
->conversion
!= 'r' && strchr(bad_chars
,c
))
76 if (opts
->conversion
== 's' && strchr(bad_if_strict(opts
),c
))
78 if (c
>= 'A' && c
<= 'Z' && opts
->conversion
== 's')
80 if (c
< ' ' || c
== ':' || c
== '\\') return -EINVAL
;
81 /* 0xE5 is legal as a first character, but we must substitute 0x05 */
82 /* because 0xE5 marks deleted files. Yes, DOS really does this. */
83 /* It seems that Microsoft hacked DOS to support non-US characters */
84 /* after the 0xE5 character was already in use to mark deleted files. */
85 if((res
==walk
) && (c
==0xE5)) c
=0x05;
88 *walk
= (c
>= 'a' && c
<= 'z') ? c
-32 : c
;
90 if (space
) return -EINVAL
;
91 if (opts
->conversion
== 's' && len
&& c
!= '.') {
94 if (c
!= '.') return -EINVAL
;
96 while (c
!= '.' && len
--) c
= *name
++;
98 while (walk
-res
< 8) *walk
++ = ' ';
99 while (len
> 0 && walk
-res
< MSDOS_NAME
) {
102 if (opts
->conversion
!= 'r' && strchr(bad_chars
,c
))
104 if (opts
->conversion
== 's' &&
105 strchr(bad_if_strict(opts
),c
))
107 if (c
< ' ' || c
== ':' || c
== '\\')
110 if (opts
->conversion
== 's')
114 if (c
>= 'A' && c
<= 'Z' && opts
->conversion
== 's')
117 *walk
++ = c
>= 'a' && c
<= 'z' ? c
-32 : c
;
119 if (space
) return -EINVAL
;
120 if (opts
->conversion
== 's' && len
) return -EINVAL
;
122 while (walk
-res
< MSDOS_NAME
) *walk
++ = ' ';
124 /* GEMDOS is less stupid and has no reserved names */
125 for (reserved
= reserved_names
; *reserved
; reserved
++)
126 if (!strncmp(res
,*reserved
,8)) return -EINVAL
;
130 /***** Locates a directory entry. Uses unformatted name. */
131 static int msdos_find(struct inode
*dir
,const char *name
,int len
,
132 struct buffer_head
**bh
,struct msdos_dir_entry
**de
,int *ino
)
136 char msdos_name
[MSDOS_NAME
];
138 dotsOK
= MSDOS_SB(dir
->i_sb
)->options
.dotsOK
;
139 res
= msdos_format_name(name
,len
, msdos_name
,&MSDOS_SB(dir
->i_sb
)->options
);
142 res
= fat_scan(dir
,msdos_name
,bh
,de
,ino
);
143 if (!res
&& dotsOK
) {
145 if (!((*de
)->attr
& ATTR_HIDDEN
))
148 if ((*de
)->attr
& ATTR_HIDDEN
)
157 * Compute the hash for the msdos name corresponding to the dentry.
158 * Note: if the name is invalid, we leave the hash code unchanged so
159 * that the existing dentry can be used. The msdos fs routines will
160 * return ENOENT or EINVAL as appropriate.
162 static int msdos_hash(struct dentry
*dentry
, struct qstr
*qstr
)
164 struct fat_mount_options
*options
= & (MSDOS_SB(dentry
->d_sb
)->options
);
166 char msdos_name
[MSDOS_NAME
];
168 error
= msdos_format_name(qstr
->name
, qstr
->len
, msdos_name
, options
);
170 qstr
->hash
= full_name_hash(msdos_name
, MSDOS_NAME
);
175 * Compare two msdos names. If either of the names are invalid,
176 * we fall back to doing the standard name comparison.
178 static int msdos_cmp(struct dentry
*dentry
, struct qstr
*a
, struct qstr
*b
)
180 struct fat_mount_options
*options
= & (MSDOS_SB(dentry
->d_sb
)->options
);
182 char a_msdos_name
[MSDOS_NAME
], b_msdos_name
[MSDOS_NAME
];
184 error
= msdos_format_name(a
->name
, a
->len
, a_msdos_name
, options
);
187 error
= msdos_format_name(b
->name
, b
->len
, b_msdos_name
, options
);
190 error
= memcmp(a_msdos_name
, b_msdos_name
, MSDOS_NAME
);
196 if (a
->len
== b
->len
)
197 error
= memcmp(a
->name
, b
->name
, a
->len
);
202 static struct dentry_operations msdos_dentry_operations
= {
203 NULL
, /* d_revalidate */
212 * AV. Wrappers for FAT sb operations. Is it wise?
215 /***** Get inode using directory and name */
216 struct dentry
*msdos_lookup(struct inode
*dir
,struct dentry
*dentry
)
218 struct super_block
*sb
= dir
->i_sb
;
219 struct inode
*inode
= NULL
;
220 struct msdos_dir_entry
*de
;
221 struct buffer_head
*bh
= NULL
;
224 PRINTK (("msdos_lookup\n"));
226 dentry
->d_op
= &msdos_dentry_operations
;
228 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
, &bh
,
235 inode
= fat_build_inode(sb
, de
, ino
, &res
);
239 d_add(dentry
, inode
);
247 /***** Creates a directory entry (name is already formatted). */
248 static int msdos_add_entry(struct inode
*dir
, const char *name
,
249 struct buffer_head
**bh
,
250 struct msdos_dir_entry
**de
,
252 int is_dir
, int is_hid
)
254 struct super_block
*sb
= dir
->i_sb
;
257 if ((res
= fat_add_entries(dir
, 1, bh
, de
, ino
))<0)
260 * XXX all times should be set by caller upon successful completion.
262 dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
263 mark_inode_dirty(dir
);
264 memcpy((*de
)->name
,name
,MSDOS_NAME
);
265 (*de
)->attr
= is_dir
? ATTR_DIR
: ATTR_ARCH
;
267 (*de
)->attr
|= ATTR_HIDDEN
;
270 fat_date_unix2dos(dir
->i_mtime
,&(*de
)->time
,&(*de
)->date
);
272 fat_mark_buffer_dirty(sb
, *bh
, 1);
277 * AV. Huh??? It's exported. Oughtta check usage.
280 /***** Create a file */
281 int msdos_create(struct inode
*dir
,struct dentry
*dentry
,int mode
)
283 struct super_block
*sb
= dir
->i_sb
;
284 struct buffer_head
*bh
;
285 struct msdos_dir_entry
*de
;
288 char msdos_name
[MSDOS_NAME
];
290 res
= msdos_format_name(dentry
->d_name
.name
,dentry
->d_name
.len
,
291 msdos_name
, &MSDOS_SB(sb
)->options
);
294 is_hid
= (dentry
->d_name
.name
[0]=='.') && (msdos_name
[0]!='.');
295 /* Have to do it due to foo vs. .foo conflicts */
296 if (fat_scan(dir
,msdos_name
,&bh
,&de
,&ino
) >= 0) {
301 res
= msdos_add_entry(dir
, msdos_name
, &bh
, &de
, &ino
, 0, is_hid
);
304 inode
= fat_build_inode(dir
->i_sb
, de
, ino
, &res
);
308 inode
->i_mtime
= inode
->i_atime
= inode
->i_ctime
= CURRENT_TIME
;
309 mark_inode_dirty(inode
);
310 d_instantiate(dentry
, inode
);
314 /***** Remove a directory */
315 int msdos_rmdir(struct inode
*dir
, struct dentry
*dentry
)
317 struct super_block
*sb
= dir
->i_sb
;
318 struct inode
*inode
= dentry
->d_inode
;
320 struct buffer_head
*bh
;
321 struct msdos_dir_entry
*de
;
324 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
,
329 * Check whether the directory is not in use, then check
330 * whether it is empty.
333 if (!list_empty(&dentry
->d_hash
))
335 res
= fat_dir_empty(inode
);
339 de
->name
[0] = DELETED_FLAG
;
340 fat_mark_buffer_dirty(sb
, bh
, 1);
343 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
345 mark_inode_dirty(inode
);
346 mark_inode_dirty(dir
);
355 /***** Make a directory */
356 int msdos_mkdir(struct inode
*dir
,struct dentry
*dentry
,int mode
)
358 struct super_block
*sb
= dir
->i_sb
;
359 struct buffer_head
*bh
;
360 struct msdos_dir_entry
*de
;
363 char msdos_name
[MSDOS_NAME
];
366 res
= msdos_format_name(dentry
->d_name
.name
,dentry
->d_name
.len
,
367 msdos_name
, &MSDOS_SB(sb
)->options
);
370 is_hid
= (dentry
->d_name
.name
[0]=='.') && (msdos_name
[0]!='.');
371 /* foo vs .foo situation */
372 if (fat_scan(dir
,msdos_name
,&bh
,&de
,&ino
) >= 0)
375 res
= msdos_add_entry(dir
, msdos_name
, &bh
, &de
, &ino
, 1, is_hid
);
378 inode
= fat_build_inode(dir
->i_sb
, de
, ino
, &res
);
386 inode
->i_nlink
= 2; /* no need to mark them dirty */
388 res
= fat_new_dir(inode
, dir
, 0);
393 d_instantiate(dentry
, inode
);
400 printk("msdos_mkdir: error=%d, attempting cleanup\n", res
);
402 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
404 mark_inode_dirty(inode
);
405 mark_inode_dirty(dir
);
406 de
->name
[0] = DELETED_FLAG
;
407 fat_mark_buffer_dirty(sb
, bh
, 1);
419 /***** Unlink a file */
420 int msdos_unlink( struct inode
*dir
, struct dentry
*dentry
)
422 struct super_block
*sb
= dir
->i_sb
;
423 struct inode
*inode
= dentry
->d_inode
;
425 struct buffer_head
*bh
;
426 struct msdos_dir_entry
*de
;
429 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
,
434 de
->name
[0] = DELETED_FLAG
;
435 fat_mark_buffer_dirty(sb
, bh
, 1);
439 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
440 mark_inode_dirty(inode
);
441 mark_inode_dirty(dir
);
442 d_delete(dentry
); /* This also frees the inode */
448 static int do_msdos_rename(struct inode
*old_dir
, char *old_name
,
449 struct dentry
*old_dentry
,
450 struct inode
*new_dir
,char *new_name
, struct dentry
*new_dentry
,
451 struct buffer_head
*old_bh
,
452 struct msdos_dir_entry
*old_de
, int old_ino
, int is_hid
)
454 struct super_block
*sb
= old_dir
->i_sb
;
455 struct buffer_head
*new_bh
=NULL
,*dotdot_bh
=NULL
;
456 struct msdos_dir_entry
*new_de
,*dotdot_de
;
457 struct inode
*old_inode
,*new_inode
;
458 int new_ino
,dotdot_ino
;
462 old_inode
= old_dentry
->d_inode
;
463 new_inode
= new_dentry
->d_inode
;
464 is_dir
= S_ISDIR(old_inode
->i_mode
);
466 if (fat_scan(new_dir
,new_name
,&new_bh
,&new_de
,&new_ino
)>=0 &&!new_inode
)
467 goto degenerate_case
;
470 error
= fat_dir_empty(new_inode
);
474 error
= fat_scan(old_inode
, MSDOS_DOTDOT
, &dotdot_bh
,
475 &dotdot_de
, &dotdot_ino
);
478 "MSDOS: %s/%s, get dotdot failed, ret=%d\n",
479 old_dentry
->d_parent
->d_name
.name
,
480 old_dentry
->d_name
.name
, error
);
485 error
= msdos_add_entry(new_dir
, new_name
, &new_bh
, &new_de
,
486 &new_ino
, is_dir
, is_hid
);
490 new_dir
->i_version
= ++event
;
495 fat_detach(new_inode
);
496 old_de
->name
[0] = DELETED_FLAG
;
497 fat_mark_buffer_dirty(sb
, old_bh
, 1);
498 fat_detach(old_inode
);
499 fat_attach(old_inode
, new_ino
);
501 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
503 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
504 mark_inode_dirty(old_inode
);
505 old_dir
->i_version
= ++event
;
506 old_dir
->i_ctime
= old_dir
->i_mtime
= CURRENT_TIME
;
507 mark_inode_dirty(old_dir
);
509 new_inode
->i_nlink
--;
510 new_inode
->i_ctime
= CURRENT_TIME
;
511 mark_inode_dirty(new_inode
);
514 dotdot_de
->start
= CT_LE_W(MSDOS_I(new_dir
)->i_logstart
);
515 dotdot_de
->starthi
= CT_LE_W((MSDOS_I(new_dir
)->i_logstart
) >> 16);
516 fat_mark_buffer_dirty(sb
, dotdot_bh
, 1);
518 mark_inode_dirty(old_dir
);
520 new_inode
->i_nlink
--;
521 mark_inode_dirty(new_inode
);
524 mark_inode_dirty(new_dir
);
529 fat_brelse(sb
, new_bh
);
530 fat_brelse(sb
, dotdot_bh
);
538 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
540 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
541 mark_inode_dirty(old_inode
);
542 old_dir
->i_version
= ++event
;
543 old_dir
->i_ctime
= old_dir
->i_mtime
= CURRENT_TIME
;
544 mark_inode_dirty(old_dir
);
548 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
549 int msdos_rename(struct inode
*old_dir
,struct dentry
*old_dentry
,
550 struct inode
*new_dir
,struct dentry
*new_dentry
)
552 struct super_block
*sb
= old_dir
->i_sb
;
553 struct buffer_head
*old_bh
;
554 struct msdos_dir_entry
*old_de
;
556 int is_hid
,old_hid
; /* if new file and old file are hidden */
557 char old_msdos_name
[MSDOS_NAME
], new_msdos_name
[MSDOS_NAME
];
559 error
= msdos_format_name(old_dentry
->d_name
.name
,
560 old_dentry
->d_name
.len
,old_msdos_name
,
561 &MSDOS_SB(old_dir
->i_sb
)->options
);
564 error
= msdos_format_name(new_dentry
->d_name
.name
,
565 new_dentry
->d_name
.len
,new_msdos_name
,
566 &MSDOS_SB(new_dir
->i_sb
)->options
);
570 is_hid
= (new_dentry
->d_name
.name
[0]=='.') && (new_msdos_name
[0]!='.');
571 old_hid
= (old_dentry
->d_name
.name
[0]=='.') && (old_msdos_name
[0]!='.');
572 error
= fat_scan(old_dir
, old_msdos_name
, &old_bh
, &old_de
, &old_ino
);
576 error
= do_msdos_rename(old_dir
, old_msdos_name
, old_dentry
,
577 new_dir
, new_msdos_name
, new_dentry
,
578 old_bh
, old_de
, (ino_t
)old_ino
, is_hid
);
579 fat_brelse(sb
, old_bh
);
586 /* The public inode operations for the msdos fs */
587 struct inode_operations msdos_dir_inode_operations
= {
588 &fat_dir_operations
, /* default directory file-ops */
589 msdos_create
, /* create */
590 msdos_lookup
, /* lookup */
592 msdos_unlink
, /* unlink */
594 msdos_mkdir
, /* mkdir */
595 msdos_rmdir
, /* rmdir */
597 msdos_rename
, /* rename */
599 NULL
, /* follow_link */
600 NULL
, /* get_block */
602 NULL
, /* writepage */
603 NULL
, /* flushpage */
605 NULL
, /* permission */
607 NULL
, /* revalidate */
610 static void msdos_put_super_callback(struct super_block
*sb
)
615 struct super_block
*msdos_read_super(struct super_block
*sb
,void *data
, int silent
)
617 struct super_block
*res
;
621 MSDOS_SB(sb
)->options
.isvfat
= 0;
622 res
= fat_read_super(sb
, data
, silent
, &msdos_dir_inode_operations
);
625 MSDOS_SB(sb
)->put_super_callback
=msdos_put_super_callback
;
626 sb
->s_root
->d_op
= &msdos_dentry_operations
;
638 int init_module(void)
640 return init_msdos_fs();
644 void cleanup_module(void)
646 unregister_filesystem(&msdos_fs_type
);