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
= {
204 d_compare
: msdos_cmp
,
208 * AV. Wrappers for FAT sb operations. Is it wise?
211 /***** Get inode using directory and name */
212 struct dentry
*msdos_lookup(struct inode
*dir
,struct dentry
*dentry
)
214 struct super_block
*sb
= dir
->i_sb
;
215 struct inode
*inode
= NULL
;
216 struct msdos_dir_entry
*de
;
217 struct buffer_head
*bh
= NULL
;
220 PRINTK (("msdos_lookup\n"));
222 dentry
->d_op
= &msdos_dentry_operations
;
224 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
, &bh
,
231 inode
= fat_build_inode(sb
, de
, ino
, &res
);
235 d_add(dentry
, inode
);
243 /***** Creates a directory entry (name is already formatted). */
244 static int msdos_add_entry(struct inode
*dir
, const char *name
,
245 struct buffer_head
**bh
,
246 struct msdos_dir_entry
**de
,
248 int is_dir
, int is_hid
)
250 struct super_block
*sb
= dir
->i_sb
;
253 if ((res
= fat_add_entries(dir
, 1, bh
, de
, ino
))<0)
256 * XXX all times should be set by caller upon successful completion.
258 dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
259 mark_inode_dirty(dir
);
260 memcpy((*de
)->name
,name
,MSDOS_NAME
);
261 (*de
)->attr
= is_dir
? ATTR_DIR
: ATTR_ARCH
;
263 (*de
)->attr
|= ATTR_HIDDEN
;
266 fat_date_unix2dos(dir
->i_mtime
,&(*de
)->time
,&(*de
)->date
);
268 fat_mark_buffer_dirty(sb
, *bh
, 1);
273 * AV. Huh??? It's exported. Oughtta check usage.
276 /***** Create a file */
277 int msdos_create(struct inode
*dir
,struct dentry
*dentry
,int mode
)
279 struct super_block
*sb
= dir
->i_sb
;
280 struct buffer_head
*bh
;
281 struct msdos_dir_entry
*de
;
284 char msdos_name
[MSDOS_NAME
];
286 res
= msdos_format_name(dentry
->d_name
.name
,dentry
->d_name
.len
,
287 msdos_name
, &MSDOS_SB(sb
)->options
);
290 is_hid
= (dentry
->d_name
.name
[0]=='.') && (msdos_name
[0]!='.');
291 /* Have to do it due to foo vs. .foo conflicts */
292 if (fat_scan(dir
,msdos_name
,&bh
,&de
,&ino
) >= 0) {
297 res
= msdos_add_entry(dir
, msdos_name
, &bh
, &de
, &ino
, 0, is_hid
);
300 inode
= fat_build_inode(dir
->i_sb
, de
, ino
, &res
);
304 inode
->i_mtime
= inode
->i_atime
= inode
->i_ctime
= CURRENT_TIME
;
305 mark_inode_dirty(inode
);
306 d_instantiate(dentry
, inode
);
310 /***** Remove a directory */
311 int msdos_rmdir(struct inode
*dir
, struct dentry
*dentry
)
313 struct super_block
*sb
= dir
->i_sb
;
314 struct inode
*inode
= dentry
->d_inode
;
316 struct buffer_head
*bh
;
317 struct msdos_dir_entry
*de
;
320 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
,
325 * Check whether the directory is not in use, then check
326 * whether it is empty.
328 res
= fat_dir_empty(inode
);
332 de
->name
[0] = DELETED_FLAG
;
333 fat_mark_buffer_dirty(sb
, bh
, 1);
336 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
338 mark_inode_dirty(inode
);
339 mark_inode_dirty(dir
);
347 /***** Make a directory */
348 int msdos_mkdir(struct inode
*dir
,struct dentry
*dentry
,int mode
)
350 struct super_block
*sb
= dir
->i_sb
;
351 struct buffer_head
*bh
;
352 struct msdos_dir_entry
*de
;
355 char msdos_name
[MSDOS_NAME
];
358 res
= msdos_format_name(dentry
->d_name
.name
,dentry
->d_name
.len
,
359 msdos_name
, &MSDOS_SB(sb
)->options
);
362 is_hid
= (dentry
->d_name
.name
[0]=='.') && (msdos_name
[0]!='.');
363 /* foo vs .foo situation */
364 if (fat_scan(dir
,msdos_name
,&bh
,&de
,&ino
) >= 0)
367 res
= msdos_add_entry(dir
, msdos_name
, &bh
, &de
, &ino
, 1, is_hid
);
370 inode
= fat_build_inode(dir
->i_sb
, de
, ino
, &res
);
378 inode
->i_nlink
= 2; /* no need to mark them dirty */
380 res
= fat_new_dir(inode
, dir
, 0);
385 d_instantiate(dentry
, inode
);
392 printk("msdos_mkdir: error=%d, attempting cleanup\n", res
);
394 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
396 mark_inode_dirty(inode
);
397 mark_inode_dirty(dir
);
398 de
->name
[0] = DELETED_FLAG
;
399 fat_mark_buffer_dirty(sb
, bh
, 1);
411 /***** Unlink a file */
412 int msdos_unlink( struct inode
*dir
, struct dentry
*dentry
)
414 struct super_block
*sb
= dir
->i_sb
;
415 struct inode
*inode
= dentry
->d_inode
;
417 struct buffer_head
*bh
;
418 struct msdos_dir_entry
*de
;
421 res
= msdos_find(dir
, dentry
->d_name
.name
, dentry
->d_name
.len
,
426 de
->name
[0] = DELETED_FLAG
;
427 fat_mark_buffer_dirty(sb
, bh
, 1);
431 inode
->i_ctime
= dir
->i_ctime
= dir
->i_mtime
= CURRENT_TIME
;
432 mark_inode_dirty(inode
);
433 mark_inode_dirty(dir
);
439 static int do_msdos_rename(struct inode
*old_dir
, char *old_name
,
440 struct dentry
*old_dentry
,
441 struct inode
*new_dir
,char *new_name
, struct dentry
*new_dentry
,
442 struct buffer_head
*old_bh
,
443 struct msdos_dir_entry
*old_de
, int old_ino
, int is_hid
)
445 struct super_block
*sb
= old_dir
->i_sb
;
446 struct buffer_head
*new_bh
=NULL
,*dotdot_bh
=NULL
;
447 struct msdos_dir_entry
*new_de
,*dotdot_de
;
448 struct inode
*old_inode
,*new_inode
;
449 int new_ino
,dotdot_ino
;
453 old_inode
= old_dentry
->d_inode
;
454 new_inode
= new_dentry
->d_inode
;
455 is_dir
= S_ISDIR(old_inode
->i_mode
);
457 if (fat_scan(new_dir
,new_name
,&new_bh
,&new_de
,&new_ino
)>=0 &&!new_inode
)
458 goto degenerate_case
;
461 error
= fat_dir_empty(new_inode
);
465 error
= fat_scan(old_inode
, MSDOS_DOTDOT
, &dotdot_bh
,
466 &dotdot_de
, &dotdot_ino
);
469 "MSDOS: %s/%s, get dotdot failed, ret=%d\n",
470 old_dentry
->d_parent
->d_name
.name
,
471 old_dentry
->d_name
.name
, error
);
476 error
= msdos_add_entry(new_dir
, new_name
, &new_bh
, &new_de
,
477 &new_ino
, is_dir
, is_hid
);
481 new_dir
->i_version
= ++event
;
486 fat_detach(new_inode
);
487 old_de
->name
[0] = DELETED_FLAG
;
488 fat_mark_buffer_dirty(sb
, old_bh
, 1);
489 fat_detach(old_inode
);
490 fat_attach(old_inode
, new_ino
);
492 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
494 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
495 mark_inode_dirty(old_inode
);
496 old_dir
->i_version
= ++event
;
497 old_dir
->i_ctime
= old_dir
->i_mtime
= CURRENT_TIME
;
498 mark_inode_dirty(old_dir
);
500 new_inode
->i_nlink
--;
501 new_inode
->i_ctime
= CURRENT_TIME
;
502 mark_inode_dirty(new_inode
);
505 dotdot_de
->start
= CT_LE_W(MSDOS_I(new_dir
)->i_logstart
);
506 dotdot_de
->starthi
= CT_LE_W((MSDOS_I(new_dir
)->i_logstart
) >> 16);
507 fat_mark_buffer_dirty(sb
, dotdot_bh
, 1);
509 mark_inode_dirty(old_dir
);
511 new_inode
->i_nlink
--;
512 mark_inode_dirty(new_inode
);
515 mark_inode_dirty(new_dir
);
520 fat_brelse(sb
, new_bh
);
521 fat_brelse(sb
, dotdot_bh
);
529 MSDOS_I(old_inode
)->i_attrs
|= ATTR_HIDDEN
;
531 MSDOS_I(old_inode
)->i_attrs
&= ~ATTR_HIDDEN
;
532 mark_inode_dirty(old_inode
);
533 old_dir
->i_version
= ++event
;
534 old_dir
->i_ctime
= old_dir
->i_mtime
= CURRENT_TIME
;
535 mark_inode_dirty(old_dir
);
539 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
540 int msdos_rename(struct inode
*old_dir
,struct dentry
*old_dentry
,
541 struct inode
*new_dir
,struct dentry
*new_dentry
)
543 struct super_block
*sb
= old_dir
->i_sb
;
544 struct buffer_head
*old_bh
;
545 struct msdos_dir_entry
*old_de
;
547 int is_hid
,old_hid
; /* if new file and old file are hidden */
548 char old_msdos_name
[MSDOS_NAME
], new_msdos_name
[MSDOS_NAME
];
550 error
= msdos_format_name(old_dentry
->d_name
.name
,
551 old_dentry
->d_name
.len
,old_msdos_name
,
552 &MSDOS_SB(old_dir
->i_sb
)->options
);
555 error
= msdos_format_name(new_dentry
->d_name
.name
,
556 new_dentry
->d_name
.len
,new_msdos_name
,
557 &MSDOS_SB(new_dir
->i_sb
)->options
);
561 is_hid
= (new_dentry
->d_name
.name
[0]=='.') && (new_msdos_name
[0]!='.');
562 old_hid
= (old_dentry
->d_name
.name
[0]=='.') && (old_msdos_name
[0]!='.');
563 error
= fat_scan(old_dir
, old_msdos_name
, &old_bh
, &old_de
, &old_ino
);
567 error
= do_msdos_rename(old_dir
, old_msdos_name
, old_dentry
,
568 new_dir
, new_msdos_name
, new_dentry
,
569 old_bh
, old_de
, (ino_t
)old_ino
, is_hid
);
570 fat_brelse(sb
, old_bh
);
577 /* The public inode operations for the msdos fs */
578 struct inode_operations msdos_dir_inode_operations
= {
579 create
: msdos_create
,
580 lookup
: msdos_lookup
,
581 unlink
: msdos_unlink
,
584 rename
: msdos_rename
,
585 setattr
: fat_notify_change
,
588 struct super_block
*msdos_read_super(struct super_block
*sb
,void *data
, int silent
)
590 struct super_block
*res
;
592 MSDOS_SB(sb
)->options
.isvfat
= 0;
593 res
= fat_read_super(sb
, data
, silent
, &msdos_dir_inode_operations
);
595 sb
->s_root
->d_op
= &msdos_dentry_operations
;