* better
[mascara-docs.git] / i386 / linux-2.3.21 / fs / msdos / namei.c
blobd69aba48d2e4d7e122fc3fc1665ff384723d3b74
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 NULL, /* d_revalidate */
204 msdos_hash,
205 msdos_cmp,
206 NULL, /* d_delete */
207 NULL,
208 NULL
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;
222 int ino,res;
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,
229 &de, &ino);
231 if (res == -ENOENT)
232 goto add;
233 if (res < 0)
234 goto out;
235 inode = fat_build_inode(sb, de, ino, &res);
236 if (res)
237 goto out;
238 add:
239 d_add(dentry, inode);
240 res = 0;
241 out:
242 if (bh)
243 fat_brelse(sb, bh);
244 return ERR_PTR(res);
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,
251 int *ino,
252 int is_dir, int is_hid)
254 struct super_block *sb = dir->i_sb;
255 int res;
257 if ((res = fat_add_entries(dir, 1, bh, de, ino))<0)
258 return res;
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;
266 if (is_hid)
267 (*de)->attr |= ATTR_HIDDEN;
268 (*de)->start = 0;
269 (*de)->starthi = 0;
270 fat_date_unix2dos(dir->i_mtime,&(*de)->time,&(*de)->date);
271 (*de)->size = 0;
272 fat_mark_buffer_dirty(sb, *bh, 1);
273 return 0;
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;
286 struct inode *inode;
287 int ino,res,is_hid;
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);
292 if (res < 0)
293 return res;
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) {
297 fat_brelse(sb, bh);
298 return -EINVAL;
300 inode = NULL;
301 res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 0, is_hid);
302 if (res)
303 return res;
304 inode = fat_build_inode(dir->i_sb, de, ino, &res);
305 fat_brelse(sb, bh);
306 if (!inode)
307 return res;
308 inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
309 mark_inode_dirty(inode);
310 d_instantiate(dentry, inode);
311 return 0;
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;
319 int res,ino;
320 struct buffer_head *bh;
321 struct msdos_dir_entry *de;
323 bh = NULL;
324 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
325 &bh, &de, &ino);
326 if (res < 0)
327 goto rmdir_done;
329 * Check whether the directory is not in use, then check
330 * whether it is empty.
332 res = -EBUSY;
333 if (!list_empty(&dentry->d_hash))
334 goto rmdir_done;
335 res = fat_dir_empty(inode);
336 if (res)
337 goto rmdir_done;
339 de->name[0] = DELETED_FLAG;
340 fat_mark_buffer_dirty(sb, bh, 1);
341 fat_detach(inode);
342 inode->i_nlink = 0;
343 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
344 dir->i_nlink--;
345 mark_inode_dirty(inode);
346 mark_inode_dirty(dir);
347 d_delete(dentry);
348 res = 0;
350 rmdir_done:
351 fat_brelse(sb, bh);
352 return res;
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;
361 struct inode *inode;
362 int res,is_hid;
363 char msdos_name[MSDOS_NAME];
364 int ino;
366 res = msdos_format_name(dentry->d_name.name,dentry->d_name.len,
367 msdos_name, &MSDOS_SB(sb)->options);
368 if (res < 0)
369 return res;
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)
373 goto out_exist;
375 res = msdos_add_entry(dir, msdos_name, &bh, &de, &ino, 1, is_hid);
376 if (res)
377 goto out_unlock;
378 inode = fat_build_inode(dir->i_sb, de, ino, &res);
379 if (!inode) {
380 fat_brelse(sb, bh);
381 goto out_unlock;
383 res = 0;
385 dir->i_nlink++;
386 inode->i_nlink = 2; /* no need to mark them dirty */
388 res = fat_new_dir(inode, dir, 0);
389 if (res)
390 goto mkdir_error;
392 fat_brelse(sb, bh);
393 d_instantiate(dentry, inode);
394 res = 0;
396 out_unlock:
397 return res;
399 mkdir_error:
400 printk("msdos_mkdir: error=%d, attempting cleanup\n", res);
401 inode->i_nlink = 0;
402 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
403 dir->i_nlink--;
404 mark_inode_dirty(inode);
405 mark_inode_dirty(dir);
406 de->name[0] = DELETED_FLAG;
407 fat_mark_buffer_dirty(sb, bh, 1);
408 fat_brelse(sb, bh);
409 fat_detach(inode);
410 iput(inode);
411 goto out_unlock;
413 out_exist:
414 fat_brelse(sb, bh);
415 res = -EINVAL;
416 goto out_unlock;
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;
424 int res,ino;
425 struct buffer_head *bh;
426 struct msdos_dir_entry *de;
428 bh = NULL;
429 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
430 &bh, &de, &ino);
431 if (res < 0)
432 goto unlink_done;
434 de->name[0] = DELETED_FLAG;
435 fat_mark_buffer_dirty(sb, bh, 1);
436 fat_detach(inode);
437 fat_brelse(sb, bh);
438 inode->i_nlink = 0;
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 */
443 res = 0;
444 unlink_done:
445 return res;
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;
459 int error;
460 int is_dir;
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;
468 if (is_dir) {
469 if (new_inode) {
470 error = fat_dir_empty(new_inode);
471 if (error)
472 goto out;
474 error = fat_scan(old_inode, MSDOS_DOTDOT, &dotdot_bh,
475 &dotdot_de, &dotdot_ino);
476 if (error < 0) {
477 printk(KERN_WARNING
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);
481 goto out;
484 if (!new_bh) {
485 error = msdos_add_entry(new_dir, new_name, &new_bh, &new_de,
486 &new_ino, is_dir, is_hid);
487 if (error)
488 goto out;
490 new_dir->i_version = ++event;
492 /* There we go */
494 if (new_inode)
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);
500 if (is_hid)
501 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
502 else
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);
508 if (new_inode) {
509 new_inode->i_nlink--;
510 new_inode->i_ctime = CURRENT_TIME;
511 mark_inode_dirty(new_inode);
513 if (dotdot_bh) {
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);
517 old_dir->i_nlink--;
518 mark_inode_dirty(old_dir);
519 if (new_inode) {
520 new_inode->i_nlink--;
521 mark_inode_dirty(new_inode);
522 } else {
523 new_dir->i_nlink++;
524 mark_inode_dirty(new_dir);
527 error = 0;
528 out:
529 fat_brelse(sb, new_bh);
530 fat_brelse(sb, dotdot_bh);
531 return error;
533 degenerate_case:
534 error = -EINVAL;
535 if (new_de!=old_de)
536 goto out;
537 if (is_hid)
538 MSDOS_I(old_inode)->i_attrs |= ATTR_HIDDEN;
539 else
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);
545 return 0;
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;
555 int old_ino, error;
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);
562 if (error < 0)
563 goto rename_done;
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);
567 if (error < 0)
568 goto rename_done;
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);
573 if (error < 0)
574 goto rename_done;
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);
581 rename_done:
582 return error;
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 */
591 NULL, /* link */
592 msdos_unlink, /* unlink */
593 NULL, /* symlink */
594 msdos_mkdir, /* mkdir */
595 msdos_rmdir, /* rmdir */
596 NULL, /* mknod */
597 msdos_rename, /* rename */
598 NULL, /* readlink */
599 NULL, /* follow_link */
600 NULL, /* get_block */
601 NULL, /* readpage */
602 NULL, /* writepage */
603 NULL, /* flushpage */
604 NULL, /* truncate */
605 NULL, /* permission */
606 NULL, /* smap */
607 NULL, /* revalidate */
610 static void msdos_put_super_callback(struct super_block *sb)
612 MOD_DEC_USE_COUNT;
615 struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
617 struct super_block *res;
619 MOD_INC_USE_COUNT;
621 MSDOS_SB(sb)->options.isvfat = 0;
622 res = fat_read_super(sb, data, silent, &msdos_dir_inode_operations);
623 if (res == NULL)
624 goto out_fail;
625 MSDOS_SB(sb)->put_super_callback=msdos_put_super_callback;
626 sb->s_root->d_op = &msdos_dentry_operations;
627 return res;
629 out_fail:
630 sb->s_dev = 0;
631 MOD_DEC_USE_COUNT;
632 return NULL;
637 #ifdef MODULE
638 int init_module(void)
640 return init_msdos_fs();
644 void cleanup_module(void)
646 unregister_filesystem(&msdos_fs_type);
649 #endif