Merge with 2.3.99-pre9.
[linux-2.6/linux-mips.git] / fs / msdos / namei.c
blobbcb40df90525008ede469cd4402fba5d5d8b4adc
1 /*
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
7 */
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"
22 #define MSDOS_DEBUG 0
23 #define PRINTK(x)
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 ",
31 NULL };
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)
41 /* Must die */
42 void msdos_put_super(struct super_block *sb)
44 fat_put_super(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.
55 char *walk;
56 const char **reserved;
57 unsigned char c;
58 int space;
60 if (name[0] == '.') { /* dotfile because . and .. already done */
61 if (opts->dotsOK) {
62 /* Get rid of dot - test for it elsewhere */
63 name++; len--;
65 else if (!opts->atari) return -EINVAL;
67 /* disallow names that _really_ start with a dot for MS-DOS, GEMDOS does
68 * not care */
69 space = !opts->atari;
70 c = 0;
71 for (walk = res; len && walk-res < 8; walk++) {
72 c = *name++;
73 len--;
74 if (opts->conversion != 'r' && strchr(bad_chars,c))
75 return -EINVAL;
76 if (opts->conversion == 's' && strchr(bad_if_strict(opts),c))
77 return -EINVAL;
78 if (c >= 'A' && c <= 'Z' && opts->conversion == 's')
79 return -EINVAL;
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;
86 if (c == '.') break;
87 space = (c == ' ');
88 *walk = (c >= 'a' && c <= 'z') ? c-32 : c;
90 if (space) return -EINVAL;
91 if (opts->conversion == 's' && len && c != '.') {
92 c = *name++;
93 len--;
94 if (c != '.') return -EINVAL;
96 while (c != '.' && len--) c = *name++;
97 if (c == '.') {
98 while (walk-res < 8) *walk++ = ' ';
99 while (len > 0 && walk-res < MSDOS_NAME) {
100 c = *name++;
101 len--;
102 if (opts->conversion != 'r' && strchr(bad_chars,c))
103 return -EINVAL;
104 if (opts->conversion == 's' &&
105 strchr(bad_if_strict(opts),c))
106 return -EINVAL;
107 if (c < ' ' || c == ':' || c == '\\')
108 return -EINVAL;
109 if (c == '.') {
110 if (opts->conversion == 's')
111 return -EINVAL;
112 break;
114 if (c >= 'A' && c <= 'Z' && opts->conversion == 's')
115 return -EINVAL;
116 space = c == ' ';
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++ = ' ';
123 if (!opts->atari)
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;
127 return 0;
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)
134 int res;
135 char dotsOK;
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);
140 if (res < 0)
141 return -ENOENT;
142 res = fat_scan(dir,msdos_name,bh,de,ino);
143 if (!res && dotsOK) {
144 if (name[0]=='.') {
145 if (!((*de)->attr & ATTR_HIDDEN))
146 res = -ENOENT;
147 } else {
148 if ((*de)->attr & ATTR_HIDDEN)
149 res = -ENOENT;
152 return res;
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);
165 int error;
166 char msdos_name[MSDOS_NAME];
168 error = msdos_format_name(qstr->name, qstr->len, msdos_name, options);
169 if (!error)
170 qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
171 return 0;
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);
181 int error;
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);
185 if (error)
186 goto old_compare;
187 error = msdos_format_name(b->name, b->len, b_msdos_name, options);
188 if (error)
189 goto old_compare;
190 error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
191 out:
192 return error;
194 old_compare:
195 error = 1;
196 if (a->len == b->len)
197 error = memcmp(a->name, b->name, a->len);
198 goto out;
202 static struct dentry_operations msdos_dentry_operations = {
203 d_hash: msdos_hash,
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;
218 int ino,res;
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,
225 &de, &ino);
227 if (res == -ENOENT)
228 goto add;
229 if (res < 0)
230 goto out;
231 inode = fat_build_inode(sb, de, ino, &res);
232 if (res)
233 goto out;
234 add:
235 d_add(dentry, inode);
236 res = 0;
237 out:
238 if (bh)
239 fat_brelse(sb, bh);
240 return ERR_PTR(res);
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,
247 int *ino,
248 int is_dir, int is_hid)
250 struct super_block *sb = dir->i_sb;
251 int res;
253 if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
254 return res;
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;
262 if (is_hid)
263 (*de)->attr |= ATTR_HIDDEN;
264 (*de)->start = 0;
265 (*de)->starthi = 0;
266 fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
267 (*de)->size = 0;
268 fat_mark_buffer_dirty(sb, *bh, 1);
269 return 0;
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;
282 struct inode *inode;
283 int ino,res,is_hid;
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);
288 if (res < 0)
289 return res;
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) {
293 fat_brelse(sb, bh);
294 return -EINVAL;
296 inode = NULL;
297 res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
298 if (res)
299 return res;
300 inode = fat_build_inode(dir->i_sb, de, ino, &res);
301 fat_brelse(sb, bh);
302 if (!inode)
303 return res;
304 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
305 mark_inode_dirty(inode);
306 d_instantiate(dentry, inode);
307 return 0;
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;
315 int res,ino;
316 struct buffer_head *bh;
317 struct msdos_dir_entry *de;
319 bh = NULL;
320 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
321 &bh, &de, &ino);
322 if (res < 0)
323 goto rmdir_done;
325 * Check whether the directory is not in use, then check
326 * whether it is empty.
328 res = fat_dir_empty(inode);
329 if (res)
330 goto rmdir_done;
332 de->name[0] = DELETED_FLAG;
333 fat_mark_buffer_dirty(sb, bh, 1);
334 fat_detach(inode);
335 inode->i_nlink = 0;
336 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
337 dir->i_nlink--;
338 mark_inode_dirty(inode);
339 mark_inode_dirty(dir);
340 res = 0;
342 rmdir_done:
343 fat_brelse(sb, bh);
344 return res;
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;
353 struct inode *inode;
354 int res,is_hid;
355 char msdos_name[MSDOS_NAME];
356 int ino;
358 res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
359 msdos_name, &MSDOS_SB(sb)->options);
360 if (res < 0)
361 return res;
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)
365 goto out_exist;
367 res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
368 if (res)
369 goto out_unlock;
370 inode = fat_build_inode(dir->i_sb, de, ino, &res);
371 if (!inode) {
372 fat_brelse(sb, bh);
373 goto out_unlock;
375 res = 0;
377 dir->i_nlink++;
378 inode->i_nlink = 2; /* no need to mark them dirty */
380 res = fat_new_dir(inode, dir, 0);
381 if (res)
382 goto mkdir_error;
384 fat_brelse(sb, bh);
385 d_instantiate(dentry, inode);
386 res = 0;
388 out_unlock:
389 return res;
391 mkdir_error:
392 printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
393 inode->i_nlink = 0;
394 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
395 dir->i_nlink--;
396 mark_inode_dirty(inode);
397 mark_inode_dirty(dir);
398 de->name[0] = DELETED_FLAG;
399 fat_mark_buffer_dirty(sb, bh, 1);
400 fat_brelse(sb, bh);
401 fat_detach(inode);
402 iput(inode);
403 goto out_unlock;
405 out_exist:
406 fat_brelse(sb, bh);
407 res = -EINVAL;
408 goto out_unlock;
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;
416 int res,ino;
417 struct buffer_head *bh;
418 struct msdos_dir_entry *de;
420 bh = NULL;
421 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
422 &bh, &de, &ino);
423 if (res < 0)
424 goto unlink_done;
426 de->name[0] = DELETED_FLAG;
427 fat_mark_buffer_dirty(sb, bh, 1);
428 fat_detach(inode);
429 fat_brelse(sb, bh);
430 inode->i_nlink = 0;
431 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
432 mark_inode_dirty(inode);
433 mark_inode_dirty(dir);
434 res = 0;
435 unlink_done:
436 return res;
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;
450 int error;
451 int is_dir;
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;
459 if (is_dir) {
460 if (new_inode) {
461 error = fat_dir_empty(new_inode);
462 if (error)
463 goto out;
465 error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
466 &dotdot_de, &dotdot_ino);
467 if (error < 0) {
468 printk(KERN_WARNING
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);
472 goto out;
475 if (!new_bh) {
476 error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
477 &new_ino, is_dir, is_hid);
478 if (error)
479 goto out;
481 new_dir->i_version = ++event;
483 /* There we go */
485 if (new_inode)
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);
491 if (is_hid)
492 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
493 else
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);
499 if (new_inode) {
500 new_inode->i_nlink--;
501 new_inode->i_ctime = CURRENT_TIME;
502 mark_inode_dirty(new_inode);
504 if (dotdot_bh) {
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);
508 old_dir->i_nlink--;
509 mark_inode_dirty(old_dir);
510 if (new_inode) {
511 new_inode->i_nlink--;
512 mark_inode_dirty(new_inode);
513 } else {
514 new_dir->i_nlink++;
515 mark_inode_dirty(new_dir);
518 error = 0;
519 out:
520 fat_brelse(sb, new_bh);
521 fat_brelse(sb, dotdot_bh);
522 return error;
524 degenerate_case:
525 error = -EINVAL;
526 if (new_de!=old_de)
527 goto out;
528 if (is_hid)
529 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
530 else
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);
536 return 0;
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;
546 int old_ino, error;
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);
553 if (error < 0)
554 goto rename_done;
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);
558 if (error < 0)
559 goto rename_done;
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);
564 if (error < 0)
565 goto rename_done;
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);
572 rename_done:
573 return error;
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,
582 mkdir: msdos_mkdir,
583 rmdir: msdos_rmdir,
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);
594 if (res)
595 sb->s_root->d_op = &msdos_dentry_operations;
596 return res;