Import 2.1.81
[davej-history.git] / fs / msdos / namei.c
blob87c62e4c9cd356ec0797f62b48bdf24ea9f99ed4
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 */
8 #include <linux/config.h>
10 #define __NO_VERSION__
11 #include <linux/module.h>
13 #include <linux/sched.h>
14 #include <linux/msdos_fs.h>
15 #include <linux/kernel.h>
16 #include <linux/errno.h>
17 #include <linux/string.h>
18 #include <linux/stat.h>
20 #include <asm/uaccess.h>
22 #include "../fat/msbuffer.h"
24 #define MSDOS_DEBUG 0
25 #define PRINTK(x)
27 /* MS-DOS "device special files" */
29 static const char *reserved_names[] = {
30 #ifndef CONFIG_ATARI /* GEMDOS is less stupid */
31 "CON ","PRN ","NUL ","AUX ",
32 "LPT1 ","LPT2 ","LPT3 ","LPT4 ",
33 "COM1 ","COM2 ","COM3 ","COM4 ",
34 #endif
35 NULL };
38 /* Characters that are undesirable in an MS-DOS file name */
40 static char bad_chars[] = "*?<>|\"";
41 #ifdef CONFIG_ATARI
42 /* GEMDOS is less restrictive */
43 static char bad_if_strict[] = " ";
44 #else
45 static char bad_if_strict[] = "+=,; ";
46 #endif
48 void msdos_put_super(struct super_block *sb)
50 fat_put_super(sb);
51 MOD_DEC_USE_COUNT;
54 struct super_operations msdos_sops = {
55 msdos_read_inode,
56 fat_write_inode,
57 fat_put_inode,
58 fat_delete_inode,
59 fat_notify_change,
60 msdos_put_super,
61 NULL, /* added in 0.96c */
62 fat_statfs,
63 NULL
66 /***** Formats an MS-DOS file name. Rejects invalid names. */
67 static int msdos_format_name(char conv,const char *name,int len,
68 char *res,int dot_dirs,char dotsOK)
69 /* conv is relaxed/normal/strict, name is proposed name,
70 * len is the length of the proposed name, res is the result name,
71 * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
74 char *walk;
75 const char **reserved;
76 unsigned char c;
77 int space;
79 if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
80 if (!dot_dirs) return -EEXIST;
81 memset(res+1,' ',10);
82 while (len--) *res++ = '.';
83 return 0;
85 if (name[0] == '.') { /* dotfile because . and .. already done */
86 if (!dotsOK) return -EINVAL;
87 /* Get rid of dot - test for it elsewhere */
88 name++; len--;
90 #ifndef CONFIG_ATARI
91 space = 1; /* disallow names that _really_ start with a dot */
92 #else
93 space = 0; /* GEMDOS does not care */
94 #endif
95 c = 0;
96 for (walk = res; len && walk-res < 8; walk++) {
97 c = *name++;
98 len--;
99 if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
100 if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
101 if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
102 if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
103 /* 0xE5 is legal as a first character, but we must substitute 0x05 */
104 /* because 0xE5 marks deleted files. Yes, DOS really does this. */
105 /* It seems that Microsoft hacked DOS to support non-US characters */
106 /* after the 0xE5 character was already in use to mark deleted files. */
107 if((res==walk) && (c==0xE5)) c=0x05;
108 if (c == '.') break;
109 space = (c == ' ');
110 *walk = (c >= 'a' && c <= 'z') ? c-32 : c;
112 if (space) return -EINVAL;
113 if (conv == 's' && len && c != '.') {
114 c = *name++;
115 len--;
116 if (c != '.') return -EINVAL;
118 while (c != '.' && len--) c = *name++;
119 if (c == '.') {
120 while (walk-res < 8) *walk++ = ' ';
121 while (len > 0 && walk-res < MSDOS_NAME) {
122 c = *name++;
123 len--;
124 if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
125 if (conv == 's' && strchr(bad_if_strict,c))
126 return -EINVAL;
127 if (c < ' ' || c == ':' || c == '\\')
128 return -EINVAL;
129 if (c == '.') {
130 if (conv == 's')
131 return -EINVAL;
132 break;
134 if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
135 space = c == ' ';
136 *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
138 if (space) return -EINVAL;
139 if (conv == 's' && len) return -EINVAL;
141 while (walk-res < MSDOS_NAME) *walk++ = ' ';
142 for (reserved = reserved_names; *reserved; reserved++)
143 if (!strncmp(res,*reserved,8)) return -EINVAL;
144 return 0;
148 /***** Locates a directory entry. Uses unformatted name. */
149 static int msdos_find(struct inode *dir,const char *name,int len,
150 struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
152 char msdos_name[MSDOS_NAME];
153 int res;
154 char dotsOK;
155 char scantype;
157 dotsOK = MSDOS_SB(dir->i_sb)->options.dotsOK;
158 res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
159 name,len, msdos_name,1,dotsOK);
160 if (res < 0)
161 return -ENOENT;
162 if((name[0]=='.') && dotsOK){
163 switch(len){
164 case 0: panic("Empty name in msdos_find!");
165 case 1: scantype = SCAN_ANY; break;
166 case 2: scantype = ((name[1]=='.')?SCAN_ANY:SCAN_HID); break;
167 default: scantype = SCAN_HID;
169 } else {
170 scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
172 return fat_scan(dir,msdos_name,bh,de,ino,scantype);
176 * Compute the hash for the msdos name corresponding to the dentry.
177 * Note: if the name is invalid, we leave the hash code unchanged so
178 * that the existing dentry can be used. The msdos fs routines will
179 * return ENOENT or EINVAL as appropriate.
181 static int msdos_hash(struct dentry *dentry, struct qstr *qstr)
183 struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
184 int error;
185 char msdos_name[MSDOS_NAME];
187 error = msdos_format_name(options->name_check, qstr->name, qstr->len,
188 msdos_name, 1, options->dotsOK);
189 if (!error)
190 qstr->hash = full_name_hash(msdos_name, MSDOS_NAME);
191 return 0;
195 * Compare two msdos names. If either of the names are invalid,
196 * we fall back to doing the standard name comparison.
198 static int msdos_cmp(struct dentry *dentry, struct qstr *a, struct qstr *b)
200 struct fat_mount_options *options = & (MSDOS_SB(dentry->d_sb)->options);
201 int error;
202 char a_msdos_name[MSDOS_NAME], b_msdos_name[MSDOS_NAME];
204 error = msdos_format_name(options->name_check, a->name, a->len,
205 a_msdos_name, 1, options->dotsOK);
206 if (error)
207 goto old_compare;
208 error = msdos_format_name(options->name_check, b->name, b->len,
209 b_msdos_name, 1, options->dotsOK);
210 if (error)
211 goto old_compare;
212 error = memcmp(a_msdos_name, b_msdos_name, MSDOS_NAME);
213 out:
214 return error;
216 old_compare:
217 error = 1;
218 if (a->len == b->len)
219 error = memcmp(a->name, b->name, a->len);
220 goto out;
224 static struct dentry_operations msdos_dentry_operations = {
225 NULL, /* d_revalidate */
226 msdos_hash,
227 msdos_cmp,
228 NULL /* d_delete */
231 struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
233 struct super_block *res;
235 MOD_INC_USE_COUNT;
237 MSDOS_SB(sb)->options.isvfat = 0;
238 sb->s_op = &msdos_sops;
239 res = fat_read_super(sb, data, silent);
240 if (res == NULL)
241 goto out_fail;
242 sb->s_root->d_op = &msdos_dentry_operations;
243 return res;
245 out_fail:
246 sb->s_dev = 0;
247 MOD_DEC_USE_COUNT;
248 return NULL;
252 /***** Get inode using directory and name */
253 int msdos_lookup(struct inode *dir,struct dentry *dentry)
255 struct super_block *sb = dir->i_sb;
256 int ino,res;
257 struct msdos_dir_entry *de;
258 struct buffer_head *bh;
259 struct inode *inode;
261 PRINTK (("msdos_lookup\n"));
263 dentry->d_op = &msdos_dentry_operations;
265 if(!dir) { /* N.B. This test is bogus -- should never happen */
266 d_add(dentry, NULL);
267 return 0;
270 if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,&bh,&de,&ino)) < 0) {
271 if(res == -ENOENT) {
272 d_add(dentry, NULL);
273 res = 0;
275 return res;
277 PRINTK (("msdos_lookup 4\n"));
278 if (bh)
279 fat_brelse(sb, bh);
280 PRINTK (("msdos_lookup 4.5\n"));
281 if (!(inode = iget(dir->i_sb,ino)))
282 return -EACCES;
283 PRINTK (("msdos_lookup 5\n"));
284 if (!inode->i_sb ||
285 (inode->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
286 /* crossed a mount point into a non-msdos fs */
287 d_add(dentry, inode);
288 return 0;
290 if (MSDOS_I(inode)->i_busy) { /* mkdir in progress */
291 iput(inode);
292 d_add(dentry, NULL); /* N.B. Do we really want a negative? */
293 return 0;
295 PRINTK (("msdos_lookup 6\n"));
296 d_add(dentry, inode);
297 PRINTK (("msdos_lookup 7\n"));
298 return 0;
302 /***** Creates a directory entry (name is already formatted). */
303 static int msdos_create_entry(struct inode *dir, const char *name,
304 int is_dir, int is_hid, struct inode **result)
306 struct super_block *sb = dir->i_sb;
307 struct buffer_head *bh;
308 struct msdos_dir_entry *de;
309 int res,ino;
311 if(!dir)
312 return -ENOENT;
314 *result = NULL;
315 if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
316 if (res != -ENOENT) return res;
317 if ((dir->i_ino == MSDOS_ROOT_INO) &&
318 (MSDOS_SB(sb)->fat_bits != 32))
319 return -ENOSPC;
320 if ((res = fat_add_cluster(dir)) < 0) return res;
321 if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
324 * XXX all times should be set by caller upon successful completion.
326 dir->i_ctime = dir->i_mtime = CURRENT_TIME;
327 mark_inode_dirty(dir);
328 memcpy(de->name,name,MSDOS_NAME);
329 de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
330 de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
331 de->start = 0;
332 de->starthi = 0;
333 fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
334 de->size = 0;
335 fat_mark_buffer_dirty(sb, bh, 1);
336 if ((*result = iget(dir->i_sb,ino)) != NULL)
337 msdos_read_inode(*result);
338 fat_brelse(sb, bh);
339 if (!*result) return -EIO;
340 (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
341 CURRENT_TIME;
342 mark_inode_dirty(*result);
343 return 0;
346 /***** Create a file or directory */
347 int msdos_create(struct inode *dir,struct dentry *dentry,int mode)
349 struct super_block *sb = dir->i_sb;
350 struct buffer_head *bh;
351 struct msdos_dir_entry *de;
352 struct inode *inode;
353 char msdos_name[MSDOS_NAME];
354 int ino,res,is_hid;
356 if (!dir) return -ENOENT;
357 if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
358 dentry->d_name.name,dentry->d_name.len,
359 msdos_name,0,
360 MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
361 return res;
362 is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
363 fat_lock_creation();
364 /* Scan for existing file twice, so that creating a file fails
365 * with -EINVAL if the other (dotfile/nondotfile) exists.
366 * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
368 if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
369 fat_unlock_creation();
370 fat_brelse(sb, bh);
371 return is_hid ? -EEXIST : -EINVAL;
373 if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
374 fat_unlock_creation();
375 fat_brelse(sb, bh);
376 return is_hid ? -EINVAL : -EEXIST;
378 res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,
379 &inode);
380 fat_unlock_creation();
381 if (!res)
382 d_instantiate(dentry, inode);
383 return res;
387 #ifdef DEBUG
389 static void dump_fat(struct super_block *sb,int start)
391 printk("[");
392 while (start) {
393 printk("%d ",start);
394 start = fat_access(sb,start,-1);
395 if (!start) {
396 printk("ERROR");
397 break;
399 if (start == -1) break;
401 printk("]\n");
404 #endif
406 /***** See if directory is empty */
407 static int msdos_empty(struct inode *dir)
409 loff_t pos;
410 struct buffer_head *bh;
411 struct msdos_dir_entry *de;
412 int result = 0;
414 if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
415 pos = 0;
416 bh = NULL;
417 while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
418 /* Ignore vfat longname entries */
419 if (de->attr == ATTR_EXT)
420 continue;
421 if (!IS_FREE(de->name) &&
422 strncmp(de->name,MSDOS_DOT , MSDOS_NAME) &&
423 strncmp(de->name,MSDOS_DOTDOT, MSDOS_NAME)) {
424 result = -ENOTEMPTY;
425 break;
428 if (bh)
429 fat_brelse(dir->i_sb, bh);
431 return result;
434 /***** Remove a directory */
435 int msdos_rmdir(struct inode *dir, struct dentry *dentry)
437 struct inode *inode = dentry->d_inode;
438 struct super_block *sb = dir->i_sb;
439 int res,ino;
440 struct buffer_head *bh;
441 struct msdos_dir_entry *de;
443 bh = NULL;
444 res = msdos_find(dir, dentry->d_name.name, dentry->d_name.len,
445 &bh, &de, &ino);
446 if (res < 0)
447 goto rmdir_done;
448 res = -ENOTDIR;
449 if (!S_ISDIR(inode->i_mode))
450 goto rmdir_done;
451 if (dir->i_dev != inode->i_dev || dir == inode)
452 printk("msdos_rmdir: impossible condition\n");
454 * Prune any child dentries, then verify that
455 * the directory is empty and not in use.
457 shrink_dcache_parent(dentry);
458 res = msdos_empty(inode);
459 if (res)
460 goto rmdir_done;
461 res = -EBUSY;
462 if (dentry->d_count > 1) {
463 #ifdef MSDOS_DEBUG
464 printk("msdos_rmdir: %s/%s busy, d_count=%d\n",
465 dentry->d_parent->d_name.name, dentry->d_name.name, dentry->d_count);
466 #endif
467 goto rmdir_done;
470 inode->i_nlink = 0;
471 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
472 dir->i_nlink--;
473 mark_inode_dirty(inode);
474 mark_inode_dirty(dir);
476 * Do the d_delete before any blocking operations.
477 * We must make a negative dentry, as the FAT code
478 * apparently relies on the inode being iput().
480 d_delete(dentry);
481 de->name[0] = DELETED_FLAG;
482 fat_mark_buffer_dirty(sb, bh, 1);
483 res = 0;
484 rmdir_done:
485 fat_brelse(sb, bh);
486 return res;
489 /***** Make a directory */
490 int msdos_mkdir(struct inode *dir,struct dentry *dentry,int mode)
492 struct super_block *sb = dir->i_sb;
493 struct buffer_head *bh;
494 struct msdos_dir_entry *de;
495 struct inode *inode,*dot;
496 char msdos_name[MSDOS_NAME];
497 int ino,res,is_hid;
499 if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
500 dentry->d_name.name,dentry->d_name.len,
501 msdos_name,0,
502 MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0)
503 return res;
504 is_hid = (dentry->d_name.name[0]=='.') && (msdos_name[0]!='.');
505 fat_lock_creation();
506 if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
507 fat_unlock_creation();
508 /* N.B. does this need to be released on the other path? */
509 fat_brelse(sb, bh);
510 return -EEXIST;
512 res = msdos_create_entry(dir,msdos_name,1,is_hid, &inode);
513 if (res < 0)
514 goto out_unlock;
515 dir->i_nlink++;
516 inode->i_nlink = 2; /* no need to mark them dirty */
517 MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
518 if ((res = fat_add_cluster(inode)) < 0)
519 goto mkdir_error;
520 if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
521 goto mkdir_error;
522 dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
523 MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
524 MSDOS_I(dot)->i_logstart = MSDOS_I(inode)->i_logstart;
525 dot->i_nlink = inode->i_nlink;
526 mark_inode_dirty(dot);
527 iput(dot);
528 if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
529 goto mkdir_error;
530 fat_unlock_creation();
531 dot->i_size = dir->i_size;
532 MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
533 MSDOS_I(dot)->i_logstart = MSDOS_I(dir)->i_logstart;
534 dot->i_nlink = dir->i_nlink;
535 mark_inode_dirty(dot);
536 MSDOS_I(inode)->i_busy = 0;
537 iput(dot);
538 d_instantiate(dentry, inode);
539 return 0;
540 mkdir_error:
541 if (msdos_rmdir(dir,dentry) < 0)
542 fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
543 out_unlock:
544 fat_unlock_creation();
545 return res;
548 /***** Unlink a file */
549 static int msdos_unlinkx(
550 struct inode *dir,
551 struct dentry *dentry,
552 int nospc) /* Flag special file ? */
554 struct super_block *sb = dir->i_sb;
555 struct inode *inode = dentry->d_inode;
556 int res,ino;
557 struct buffer_head *bh;
558 struct msdos_dir_entry *de;
560 bh = NULL;
561 if ((res = msdos_find(dir,dentry->d_name.name,dentry->d_name.len,
562 &bh,&de,&ino)) < 0)
563 goto unlink_done;
564 res = -EPERM;
565 if (!S_ISREG(inode->i_mode) && nospc)
566 goto unlink_done;
567 if (IS_IMMUTABLE(inode))
568 goto unlink_done;
569 inode->i_nlink = 0;
570 inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
571 MSDOS_I(inode)->i_busy = 1;
572 mark_inode_dirty(inode);
573 mark_inode_dirty(dir);
574 de->name[0] = DELETED_FLAG;
575 fat_mark_buffer_dirty(sb, bh, 1);
576 d_delete(dentry); /* This also frees the inode */
577 res = 0;
578 unlink_done:
579 fat_brelse(sb, bh);
580 return res;
583 /***** Unlink, as called for msdosfs */
584 int msdos_unlink(struct inode *dir,struct dentry *dentry)
586 return msdos_unlinkx (dir,dentry,1);
589 /***** Unlink, as called for umsdosfs */
590 int msdos_unlink_umsdos(struct inode *dir,struct dentry *dentry)
592 return msdos_unlinkx (dir,dentry,0);
595 /***** Rename within a directory */
596 static int rename_same_dir(struct inode *old_dir,char *old_name,
597 struct dentry *old_dentry,
598 struct inode *new_dir,char *new_name,struct dentry *new_dentry,
599 struct buffer_head *old_bh,
600 struct msdos_dir_entry *old_de,int old_ino,int is_hid)
602 struct super_block *sb = old_dir->i_sb;
603 struct buffer_head *new_bh;
604 struct msdos_dir_entry *new_de;
605 struct inode *new_inode,*old_inode;
606 int new_ino,exists,error;
608 if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
609 exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
610 if (*(unsigned char *) old_de->name == DELETED_FLAG) {
611 if (exists)
612 fat_brelse(sb, new_bh);
613 return -ENOENT;
615 if (exists) {
616 new_inode = new_dentry->d_inode;
617 error = S_ISDIR(new_inode->i_mode)
618 ? (old_de->attr & ATTR_DIR)
619 ? msdos_empty(new_inode)
620 : -EPERM
621 : (old_de->attr & ATTR_DIR)
622 ? -EPERM
623 : 0;
624 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
625 if (error) {
626 fat_brelse(sb, new_bh);
627 return error;
629 if (S_ISDIR(new_inode->i_mode)) {
630 new_dir->i_nlink--;
631 mark_inode_dirty(new_dir);
633 new_inode->i_nlink = 0;
634 MSDOS_I(new_inode)->i_busy = 1;
635 mark_inode_dirty(new_inode);
636 new_de->name[0] = DELETED_FLAG;
637 fat_mark_buffer_dirty(sb, new_bh, 1);
638 fat_brelse(sb, new_bh);
640 memcpy(old_de->name,new_name,MSDOS_NAME);
641 /* Update the dcache */
642 d_move(old_dentry, new_dentry);
643 set_hid:
644 old_de->attr = is_hid
645 ? (old_de->attr | ATTR_HIDDEN)
646 : (old_de->attr &~ ATTR_HIDDEN);
647 fat_mark_buffer_dirty(sb, old_bh, 1);
648 /* update binary info for conversion, i_attrs */
649 old_inode = old_dentry->d_inode;
650 MSDOS_I(old_inode)->i_attrs = is_hid
651 ? (MSDOS_I(old_inode)->i_attrs | ATTR_HIDDEN)
652 : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
653 return 0;
656 /***** Rename across directories - a nonphysical move */
657 static int rename_diff_dir(struct inode *old_dir,char *old_name,
658 struct dentry *old_dentry,
659 struct inode *new_dir,char *new_name,struct dentry *new_dentry,
660 struct buffer_head *old_bh,
661 struct msdos_dir_entry *old_de,int old_ino,int is_hid)
663 struct super_block *sb = old_dir->i_sb;
664 struct buffer_head *new_bh,*free_bh,*dotdot_bh;
665 struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
666 struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode;
667 struct dentry *walk;
668 int new_ino,free_ino,dotdot_ino;
669 int error,exists;
671 if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
672 if (old_ino == new_dir->i_ino) return -EINVAL;
673 walk = new_dentry;
674 /* prevent moving directory below itself */
675 for (;;) {
676 if (walk == old_dentry) return -EINVAL;
677 if (walk == walk->d_parent) break;
678 walk = walk->d_parent;
680 /* find free spot */
681 while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
682 SCAN_ANY)) < 0) {
683 if (error != -ENOENT) return error;
684 error = fat_add_cluster(new_dir);
685 if (error) return error;
687 exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
688 old_inode = old_dentry->d_inode;
689 if (*(unsigned char *) old_de->name == DELETED_FLAG) {
690 fat_brelse(sb, free_bh);
691 if (exists)
692 fat_brelse(sb, new_bh);
693 return -ENOENT;
695 new_inode = NULL; /* to make GCC happy */
696 if (exists) { /* Trash the old file! */
697 new_inode = new_dentry->d_inode;
698 error = S_ISDIR(new_inode->i_mode)
699 ? (old_de->attr & ATTR_DIR)
700 ? msdos_empty(new_inode)
701 : -EPERM
702 : (old_de->attr & ATTR_DIR)
703 ? -EPERM
704 : 0;
705 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
706 if (error) {
707 fat_brelse(sb, new_bh);
708 return error;
710 new_inode->i_nlink = 0;
711 MSDOS_I(new_inode)->i_busy = 1;
712 mark_inode_dirty(new_inode);
713 new_de->name[0] = DELETED_FLAG;
714 fat_mark_buffer_dirty(sb, new_bh, 1);
716 memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
717 memcpy(free_de->name,new_name,MSDOS_NAME);
718 free_de->attr = is_hid
719 ? (free_de->attr|ATTR_HIDDEN)
720 : (free_de->attr&~ATTR_HIDDEN);
721 if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
722 free_de->name[0] = DELETED_FLAG;
724 * Don't mark free_bh as dirty. Both states
725 * are supposed to be equivalent.
727 fat_brelse(sb, free_bh);
728 if (exists)
729 fat_brelse(sb, new_bh);
730 return -EIO;
732 if (exists && S_ISDIR(new_inode->i_mode)) {
733 new_dir->i_nlink--;
734 mark_inode_dirty(new_dir);
736 msdos_read_inode(free_inode);
738 free_inode->i_mode = old_inode->i_mode;
739 free_inode->i_size = old_inode->i_size;
740 free_inode->i_blocks = old_inode->i_blocks;
741 free_inode->i_mtime = old_inode->i_mtime;
742 free_inode->i_atime = old_inode->i_atime;
743 free_inode->i_ctime = old_inode->i_ctime;
744 MSDOS_I(free_inode)->i_ctime_ms = MSDOS_I(old_inode)->i_ctime_ms;
746 MSDOS_I(free_inode)->i_start = MSDOS_I(old_inode)->i_start;
747 MSDOS_I(free_inode)->i_logstart = MSDOS_I(old_inode)->i_logstart;
748 MSDOS_I(free_inode)->i_attrs = MSDOS_I(old_inode)->i_attrs;
750 /* Detach d_alias from old inode and attach to new inode */
751 list_del(&old_dentry->d_alias);
752 d_instantiate(old_dentry, free_inode);
753 iput(old_inode);
755 fat_cache_inval_inode(old_inode);
756 mark_inode_dirty(old_inode);
757 old_de->name[0] = DELETED_FLAG;
758 fat_mark_buffer_dirty(sb, old_bh, 1);
759 fat_mark_buffer_dirty(sb, free_bh, 1);
761 if (exists) {
762 /* free_inode is put after putting new_inode and old_inode */
763 fat_brelse(sb, new_bh);
765 if (S_ISDIR(old_inode->i_mode)) {
766 if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
767 &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
768 if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
769 fat_brelse(sb, dotdot_bh);
770 error = -EIO;
771 goto rename_done;
773 MSDOS_I(dotdot_inode)->i_start = MSDOS_I(new_dir)->i_start;
774 MSDOS_I(dotdot_inode)->i_logstart = MSDOS_I(new_dir)->i_logstart;
775 dotdot_de->start = CT_LE_W(MSDOS_I(new_dir)->i_logstart);
776 dotdot_de->starthi = CT_LE_W((MSDOS_I(new_dir)->i_logstart) >> 16);
777 mark_inode_dirty(dotdot_inode);
778 fat_mark_buffer_dirty(sb, dotdot_bh, 1);
779 old_dir->i_nlink--;
780 new_dir->i_nlink++;
781 /* no need to mark them dirty */
782 dotdot_inode->i_nlink = new_dir->i_nlink;
783 iput(dotdot_inode);
784 fat_brelse(sb, dotdot_bh);
787 /* Update the dcache */
788 d_move(old_dentry, new_dentry);
789 error = 0;
790 rename_done:
791 fat_brelse(sb, free_bh);
792 return error;
795 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
796 int msdos_rename(struct inode *old_dir,struct dentry *old_dentry,
797 struct inode *new_dir,struct dentry *new_dentry)
799 struct super_block *sb = old_dir->i_sb;
800 char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
801 struct buffer_head *old_bh;
802 struct msdos_dir_entry *old_de;
803 int old_ino,error;
804 int is_hid,old_hid; /* if new file and old file are hidden */
806 if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
807 old_dentry->d_name.name,
808 old_dentry->d_name.len,old_msdos_name,1,
809 MSDOS_SB(old_dir->i_sb)->options.dotsOK))
810 < 0) goto rename_done;
811 if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
812 new_dentry->d_name.name,
813 new_dentry->d_name.len,new_msdos_name,0,
814 MSDOS_SB(new_dir->i_sb)->options.dotsOK))
815 < 0) goto rename_done;
816 is_hid = (new_dentry->d_name.name[0]=='.') && (new_msdos_name[0]!='.');
817 old_hid = (old_dentry->d_name.name[0]=='.') && (old_msdos_name[0]!='.');
818 if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
819 &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
820 fat_lock_creation();
821 if (old_dir == new_dir)
822 error = rename_same_dir(old_dir,old_msdos_name,old_dentry,
823 new_dir,new_msdos_name,new_dentry,
824 old_bh,old_de,old_ino,is_hid);
825 else error = rename_diff_dir(old_dir,old_msdos_name,old_dentry,
826 new_dir,new_msdos_name,new_dentry,
827 old_bh,old_de,old_ino,is_hid);
828 fat_unlock_creation();
829 fat_brelse(sb, old_bh);
830 rename_done:
831 return error;
835 /* The public inode operations for the msdos fs */
836 struct inode_operations msdos_dir_inode_operations = {
837 &fat_dir_operations, /* default directory file-ops */
838 msdos_create, /* create */
839 msdos_lookup, /* lookup */
840 NULL, /* link */
841 msdos_unlink, /* unlink */
842 NULL, /* symlink */
843 msdos_mkdir, /* mkdir */
844 msdos_rmdir, /* rmdir */
845 NULL, /* mknod */
846 msdos_rename, /* rename */
847 NULL, /* readlink */
848 NULL, /* follow_link */
849 NULL, /* readpage */
850 NULL, /* writepage */
851 fat_bmap, /* bmap */
852 NULL, /* truncate */
853 NULL, /* permission */
854 NULL, /* smap */
855 NULL, /* updatepage */
856 NULL, /* revalidate */
860 void msdos_read_inode(struct inode *inode)
862 fat_read_inode(inode, &msdos_dir_inode_operations);
867 #ifdef MODULE
868 int init_module(void)
870 return init_msdos_fs();
874 void cleanup_module(void)
876 unregister_filesystem(&msdos_fs_type);
879 #endif