root/fs/msdos/namei.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. msdos_put_super
  2. msdos_read_super
  3. msdos_format_name
  4. msdos_find
  5. msdos_lookup
  6. msdos_create_entry
  7. msdos_create
  8. dump_fat
  9. msdos_empty
  10. msdos_rmdir
  11. msdos_mkdir
  12. msdos_unlinkx
  13. msdos_unlink
  14. msdos_unlink_umsdos
  15. rename_same_dir
  16. rename_diff_dir
  17. msdos_rename
  18. msdos_read_inode
  19. init_module
  20. cleanup_module

   1 /*
   2  *  linux/fs/msdos/namei.c
   3  *
   4  *  Written 1992,1993 by Werner Almesberger
   5  *  Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu>
   6  */
   7 
   8 #define __NO_VERSION__
   9 #include <linux/module.h>
  10 
  11 #include <linux/sched.h>
  12 #include <linux/msdos_fs.h>
  13 #include <linux/kernel.h>
  14 #include <linux/errno.h>
  15 #include <linux/string.h>
  16 #include <linux/stat.h>
  17 
  18 #include <asm/segment.h>
  19 
  20 #include "../fat/msbuffer.h"
  21 
  22 #define PRINTK(x)
  23 
  24 
  25 /* MS-DOS "device special files" */
  26 
  27 static const char *reserved_names[] = {
  28     "CON     ","PRN     ","NUL     ","AUX     ",
  29     "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
  30     "COM1    ","COM2    ","COM3    ","COM4    ",
  31     NULL };
  32 
  33 
  34 /* Characters that are undesirable in an MS-DOS file name */
  35   
  36 static char bad_chars[] = "*?<>|\"";
  37 static char bad_if_strict[] = "+=,; ";
  38 
  39 
  40 void msdos_put_super(struct super_block *sb)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42         fat_put_super(sb);
  43         MOD_DEC_USE_COUNT;
  44 }
  45 
  46 struct super_operations msdos_sops = { 
  47         msdos_read_inode,
  48         fat_notify_change,
  49         fat_write_inode,
  50         fat_put_inode,
  51         msdos_put_super,
  52         NULL, /* added in 0.96c */
  53         fat_statfs,
  54         NULL
  55 };
  56 
  57 struct super_block *msdos_read_super(struct super_block *sb,void *data, int silent)
     /* [previous][next][first][last][top][bottom][index][help] */
  58 {
  59         struct super_block *res;
  60 
  61         MOD_INC_USE_COUNT;
  62 
  63         sb->s_op = &msdos_sops;
  64         res =  fat_read_super(sb, data, silent);
  65         if (res == NULL)
  66                 MOD_DEC_USE_COUNT;
  67 
  68         return res;
  69 }
  70 
  71 
  72 
  73 
  74 
  75 /***** Formats an MS-DOS file name. Rejects invalid names. */
  76 static int msdos_format_name(char conv,const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
  77         char *res,int dot_dirs,char dotsOK)
  78         /* conv is relaxed/normal/strict, name is proposed name,
  79          * len is the length of the proposed name, res is the result name,
  80          * dot_dirs is . and .. are OK, dotsOK is if hidden files get dots.
  81          */
  82 {
  83         char *walk;
  84         const char **reserved;
  85         unsigned char c;
  86         int space;
  87 
  88         if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.'))) {
  89                 if (!dot_dirs) return -EEXIST;
  90                 memset(res+1,' ',10);
  91                 while (len--) *res++ = '.';
  92                 return 0;
  93         }
  94         if (name[0] == '.') {  /* dotfile because . and .. already done */
  95                 if (!dotsOK) return -EINVAL;
  96                 /* Get rid of dot - test for it elsewhere */
  97                 name++; len--;
  98         }
  99         space = 1; /* disallow names that _really_ start with a dot */
 100         c = 0;
 101         for (walk = res; len && walk-res < 8; walk++) {
 102                 c = *name++;
 103                 len--;
 104                 if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
 105                 if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
 106                 if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
 107                 if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
 108 /*  0xE5 is legal as a first character, but we must substitute 0x05     */
 109 /*  because 0xE5 marks deleted files.  Yes, DOS really does this.       */
 110 /*  It seems that Microsoft hacked DOS to support non-US characters     */
 111 /*  after the 0xE5 character was already in use to mark deleted files.  */
 112                 if((res==walk) && (c==0xE5)) c=0x05;
 113                 if (c == '.') break;
 114                 space = (c == ' ');
 115                 *walk = (c >= 'a' && c <= 'z') ? c-32 : c;
 116         }
 117         if (space) return -EINVAL;
 118         if (conv == 's' && len && c != '.') {
 119                 c = *name++;
 120                 len--;
 121                 if (c != '.') return -EINVAL;
 122         }
 123         while (c != '.' && len--) c = *name++;
 124         if (c == '.') {
 125                 while (walk-res < 8) *walk++ = ' ';
 126                 while (len > 0 && walk-res < MSDOS_NAME) {
 127                         c = *name++;
 128                         len--;
 129                         if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
 130                         if (conv == 's' && strchr(bad_if_strict,c))
 131                                 return -EINVAL;
 132                         if (c < ' ' || c == ':' || c == '\\' || c == '.')
 133                                 return -EINVAL;
 134                         if (c >= 'A' && c <= 'Z' && conv == 's') return -EINVAL;
 135                         space = c == ' ';
 136                         *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
 137                 }
 138                 if (space) return -EINVAL;
 139                 if (conv == 's' && len) return -EINVAL;
 140         }
 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;
 145 }
 146 
 147 
 148 /***** Locates a directory entry.  Uses unformatted name. */
 149 static int msdos_find(struct inode *dir,const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 150     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
 151 {
 152         char msdos_name[MSDOS_NAME];
 153         int res;
 154         char dotsOK;
 155         char scantype;
 156 
 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;
 168             }
 169         } else {
 170             scantype = (dotsOK ? SCAN_NOTHID : SCAN_ANY);
 171         }
 172         return fat_scan(dir,msdos_name,bh,de,ino,scantype);
 173 }
 174 
 175 /***** Get inode using directory and name */
 176 int msdos_lookup(struct inode *dir,const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 177     struct inode **result)
 178 {
 179         struct super_block *sb = dir->i_sb;
 180         int ino,res;
 181         struct msdos_dir_entry *de;
 182         struct buffer_head *bh;
 183         struct inode *next;
 184         
 185         PRINTK (("msdos_lookup\n"));
 186 
 187         *result = NULL;
 188         if (!dir) return -ENOENT;
 189         if (!S_ISDIR(dir->i_mode)) {
 190                 iput(dir);
 191                 return -ENOENT;
 192         }
 193         PRINTK (("msdos_lookup 2\n"));
 194         if (len == 1 && name[0] == '.') {
 195                 *result = dir;
 196                 return 0;
 197         }
 198         if (len == 2 && name[0] == '.' && name[1] == '.') {
 199                 ino = fat_parent_ino(dir,0);
 200                 iput(dir);
 201                 if (ino < 0) return ino;
 202                 if (!(*result = iget(dir->i_sb,ino))) return -EACCES;
 203                 return 0;
 204         }
 205 #if 0
 206         if (dcache_lookup(dir, name, len, (unsigned long *) &ino)) {
 207                 iput(dir);
 208                 if (!(*result = iget(dir->i_sb, ino)))
 209                         return -EACCES;
 210                 return 0;
 211         }
 212 #endif
 213         PRINTK (("msdos_lookup 3\n"));
 214         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
 215                 iput(dir);
 216                 return res;
 217         }
 218         PRINTK (("msdos_lookup 4\n"));
 219         if (bh) brelse(bh);
 220         PRINTK (("msdos_lookup 4.5\n"));
 221 /* printk("lookup: ino=%d\n",ino); */
 222         if (!(*result = iget(dir->i_sb,ino))) {
 223                 iput(dir);
 224                 return -EACCES;
 225         }
 226         PRINTK (("msdos_lookup 5\n"));
 227         if (!(*result)->i_sb ||
 228             ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
 229                 /* crossed a mount point into a non-msdos fs */
 230                 iput(dir);
 231                 return 0;
 232         }
 233         if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
 234                 iput(*result);
 235                 iput(dir);
 236                 return -ENOENT;
 237         }
 238         PRINTK (("msdos_lookup 6\n"));
 239         while (MSDOS_I(*result)->i_old) {
 240                 next = MSDOS_I(*result)->i_old;
 241                 iput(*result);
 242                 if (!(*result = iget(next->i_sb,next->i_ino))) {
 243                         fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
 244                         iput(dir);
 245                         return -ENOENT;
 246                 }
 247         }
 248         PRINTK (("msdos_lookup 7\n"));
 249         iput(dir);
 250         PRINTK (("msdos_lookup 8\n"));
 251         return 0;
 252 }
 253 
 254 
 255 /***** Creates a directory entry (name is already formatted). */
 256 static int msdos_create_entry(struct inode *dir, const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 257     int is_dir, int is_hid, struct inode **result)
 258 {
 259         struct super_block *sb = dir->i_sb;
 260         struct buffer_head *bh;
 261         struct msdos_dir_entry *de;
 262         int res,ino;
 263 
 264         if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
 265                 if (res != -ENOENT) return res;
 266                 if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
 267                 if ((res = fat_add_cluster(dir)) < 0) return res;
 268                 if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
 269         }
 270         /*
 271          * XXX all times should be set by caller upon successful completion.
 272          */
 273         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 274         dir->i_dirt = 1;
 275         memcpy(de->name,name,MSDOS_NAME);
 276         memset(de->unused, 0, sizeof(de->unused));
 277         de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 278         de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
 279         de->start = 0;
 280         fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
 281         de->size = 0;
 282         mark_buffer_dirty(bh, 1);
 283         if ((*result = iget(dir->i_sb,ino)) != NULL)
 284                 msdos_read_inode(*result);
 285         brelse(bh);
 286         if (!*result) return -EIO;
 287         (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
 288             CURRENT_TIME;
 289         (*result)->i_dirt = 1;
 290         dcache_add(dir, name, len, ino);
 291         return 0;
 292 }
 293 
 294 /***** Create a file or directory */
 295 int msdos_create(struct inode *dir,const char *name,int len,int mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 296         struct inode **result)
 297 {
 298         struct super_block *sb = dir->i_sb;
 299         struct buffer_head *bh;
 300         struct msdos_dir_entry *de;
 301         char msdos_name[MSDOS_NAME];
 302         int ino,res,is_hid;
 303 
 304         if (!dir) return -ENOENT;
 305         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
 306                                      name,len,msdos_name,0,
 307                                      MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
 308                 iput(dir);
 309                 return res;
 310         }
 311         is_hid = (name[0]=='.') && (msdos_name[0]!='.');
 312         fat_lock_creation();
 313         /* Scan for existing file twice, so that creating a file fails
 314          * with -EINVAL if the other (dotfile/nondotfile) exists.
 315          * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
 316          */
 317         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
 318                 fat_unlock_creation();
 319                 brelse(bh);
 320                 iput(dir);
 321                 return is_hid ? -EEXIST : -EINVAL;
 322         }
 323         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
 324                 fat_unlock_creation();
 325                 brelse(bh);
 326                 iput(dir);
 327                 return is_hid ? -EINVAL : -EEXIST;
 328         }
 329         res = msdos_create_entry(dir,msdos_name,len,S_ISDIR(mode),is_hid,
 330                                  result);
 331         fat_unlock_creation();
 332         iput(dir);
 333         return res;
 334 }
 335 
 336 
 337 #ifdef DEBUG
 338 
 339 static void dump_fat(struct super_block *sb,int start)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341         printk("[");
 342         while (start) {
 343                 printk("%d ",start);
 344                 start = fat_access(sb,start,-1);
 345                 if (!start) {
 346                         printk("ERROR");
 347                         break;
 348                 }
 349                 if (start == -1) break;
 350         }
 351         printk("]\n");
 352 }
 353 
 354 #endif
 355 
 356 /***** See if directory is empty */
 357 static int msdos_empty(struct inode *dir)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 {
 359         struct super_block *sb = dir->i_sb;
 360         loff_t pos;
 361         struct buffer_head *bh;
 362         struct msdos_dir_entry *de;
 363 
 364         if (dir->i_count > 1)
 365                 return -EBUSY;
 366         if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 367                 pos = 0;
 368                 bh = NULL;
 369                 while (fat_get_entry(dir,&pos,&bh,&de) > -1)
 370                         if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
 371                             MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
 372                             MSDOS_NAME)) {
 373                                 brelse(bh);
 374                                 return -ENOTEMPTY;
 375                         }
 376                 if (bh)
 377                         brelse(bh);
 378         }
 379         return 0;
 380 }
 381 
 382 /***** Remove a directory */
 383 int msdos_rmdir(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 384 {
 385         struct super_block *sb = dir->i_sb;
 386         int res,ino;
 387         struct buffer_head *bh;
 388         struct msdos_dir_entry *de;
 389         struct inode *inode;
 390 
 391         bh = NULL;
 392         inode = NULL;
 393         res = -EPERM;
 394         if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
 395                 goto rmdir_done;
 396         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
 397         res = -ENOENT;
 398         if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
 399         res = -ENOTDIR;
 400         if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
 401         res = -EBUSY;
 402         if (dir->i_dev != inode->i_dev || dir == inode)
 403           goto rmdir_done;
 404         res = msdos_empty(inode);
 405         if (res)
 406                 goto rmdir_done;
 407         inode->i_nlink = 0;
 408         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 409         dir->i_nlink--;
 410         inode->i_dirt = dir->i_dirt = 1;
 411         de->name[0] = DELETED_FLAG;
 412         mark_buffer_dirty(bh, 1);
 413         res = 0;
 414 rmdir_done:
 415         brelse(bh);
 416         iput(dir);
 417         iput(inode);
 418         return res;
 419 }
 420 
 421 /***** Make a directory */
 422 int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424         struct super_block *sb = dir->i_sb;
 425         struct buffer_head *bh;
 426         struct msdos_dir_entry *de;
 427         struct inode *inode,*dot;
 428         char msdos_name[MSDOS_NAME];
 429         int ino,res,is_hid;
 430 
 431         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->options.name_check,
 432                                      name,len,msdos_name,0,
 433                                      MSDOS_SB(dir->i_sb)->options.dotsOK)) < 0) {
 434                 iput(dir);
 435                 return res;
 436         }
 437         is_hid = (name[0]=='.') && (msdos_name[0]!='.');
 438         fat_lock_creation();
 439         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
 440                 fat_unlock_creation();
 441                 brelse(bh);
 442                 iput(dir);
 443                 return -EEXIST;
 444         }
 445         if ((res = msdos_create_entry(dir,msdos_name,len,1,is_hid,
 446                                       &inode)) < 0) {
 447                 fat_unlock_creation();
 448                 iput(dir);
 449                 return res;
 450         }
 451         dir->i_nlink++;
 452         inode->i_nlink = 2; /* no need to mark them dirty */
 453         MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
 454         if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
 455         if ((res = msdos_create_entry(inode,MSDOS_DOT,1,1,0,&dot)) < 0)
 456                 goto mkdir_error;
 457         dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
 458         MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
 459         dot->i_nlink = inode->i_nlink;
 460         dot->i_dirt = 1;
 461         iput(dot);
 462         if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,2,1,0,&dot)) < 0)
 463                 goto mkdir_error;
 464         fat_unlock_creation();
 465         dot->i_size = dir->i_size;
 466         MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
 467         dot->i_nlink = dir->i_nlink;
 468         dot->i_dirt = 1;
 469         MSDOS_I(inode)->i_busy = 0;
 470         iput(dot);
 471         iput(inode);
 472         iput(dir);
 473         return 0;
 474 mkdir_error:
 475         iput(inode);
 476         if (msdos_rmdir(dir,name,len) < 0)
 477                 fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
 478         fat_unlock_creation();
 479         return res;
 480 }
 481 
 482 /***** Unlink a file */
 483 static int msdos_unlinkx(
     /* [previous][next][first][last][top][bottom][index][help] */
 484         struct inode *dir,
 485         const char *name,
 486         int len,
 487         int nospc)      /* Flag special file ? */
 488 {
 489         struct super_block *sb = dir->i_sb;
 490         int res,ino;
 491         struct buffer_head *bh;
 492         struct msdos_dir_entry *de;
 493         struct inode *inode;
 494 
 495         bh = NULL;
 496         inode = NULL;
 497         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
 498                 goto unlink_done;
 499         if (!(inode = iget(dir->i_sb,ino))) {
 500                 res = -ENOENT;
 501                 goto unlink_done;
 502         }
 503         if (!S_ISREG(inode->i_mode) && nospc){
 504                 res = -EPERM;
 505                 goto unlink_done;
 506         }
 507         if (IS_IMMUTABLE(inode)){
 508                 res = -EPERM;
 509                 goto unlink_done;
 510         }
 511         inode->i_nlink = 0;
 512         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 513         MSDOS_I(inode)->i_busy = 1;
 514         inode->i_dirt = dir->i_dirt = 1;
 515         de->name[0] = DELETED_FLAG;
 516         mark_buffer_dirty(bh, 1);
 517 unlink_done:
 518         brelse(bh);
 519         iput(inode);
 520         iput(dir);
 521         return res;
 522 }
 523 
 524 /***** Unlink, as called for msdosfs */
 525 int msdos_unlink(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 526 {
 527         return msdos_unlinkx (dir,name,len,1);
 528 }
 529 
 530 /***** Unlink, as called for umsdosfs */
 531 int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 532 {
 533         return msdos_unlinkx (dir,name,len,0);
 534 }
 535 
 536 /***** Rename within a directory */
 537 static int rename_same_dir(struct inode *old_dir,char *old_name,int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 538     struct inode *new_dir,char *new_name,int new_len,
 539     struct buffer_head *old_bh,
 540     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 541 {
 542         struct super_block *sb = old_dir->i_sb;
 543         struct buffer_head *new_bh;
 544         struct msdos_dir_entry *new_de;
 545         struct inode *new_inode,*old_inode;
 546         int new_ino,exists,error;
 547 
 548         if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
 549         exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 550         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 551                 if (exists) brelse(new_bh);
 552                 return -ENOENT;
 553         }
 554         if (exists) {
 555                 if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
 556                         brelse(new_bh);
 557                         return -EIO;
 558                 }
 559                 error = S_ISDIR(new_inode->i_mode)
 560                         ? (old_de->attr & ATTR_DIR)
 561                                 ? msdos_empty(new_inode)
 562                                 : -EPERM
 563                         : (old_de->attr & ATTR_DIR)
 564                                 ? -EPERM
 565                                 : 0;
 566                 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 567                 if (error) {
 568                         iput(new_inode);
 569                         brelse(new_bh);
 570                         return error;
 571                 }
 572                 if (S_ISDIR(new_inode->i_mode)) {
 573                         new_dir->i_nlink--;
 574                         new_dir->i_dirt = 1;
 575                 }
 576                 new_inode->i_nlink = 0;
 577                 MSDOS_I(new_inode)->i_busy = 1;
 578                 new_inode->i_dirt = 1;
 579                 new_de->name[0] = DELETED_FLAG;
 580                 mark_buffer_dirty(new_bh, 1);
 581                 dcache_add(new_dir, new_name, new_len, new_ino);
 582                 iput(new_inode);
 583                 brelse(new_bh);
 584         }
 585         memcpy(old_de->name,new_name,MSDOS_NAME);
 586 set_hid:
 587         old_de->attr = is_hid
 588                 ? (old_de->attr | ATTR_HIDDEN)
 589                 : (old_de->attr &~ ATTR_HIDDEN);
 590         mark_buffer_dirty(old_bh, 1);
 591         /* update binary info for conversion, i_attrs */
 592         if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
 593                 MSDOS_I(old_inode)->i_attrs = is_hid
 594                         ? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
 595                         : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
 596                 iput(old_inode);
 597         }
 598         return 0;
 599 }
 600 
 601 /***** Rename across directories - a nonphysical move */
 602 static int rename_diff_dir(struct inode *old_dir,char *old_name,int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 603     struct inode *new_dir,char *new_name,int new_len,
 604     struct buffer_head *old_bh,
 605     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 606 {
 607         struct super_block *sb = old_dir->i_sb;
 608         struct buffer_head *new_bh,*free_bh,*dotdot_bh;
 609         struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
 610         struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
 611         int new_ino,free_ino,dotdot_ino;
 612         int error,exists,ino;
 613 
 614         if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
 615         if (old_ino == new_dir->i_ino) return -EINVAL;
 616         if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
 617         /* prevent moving directory below itself */
 618         while (walk->i_ino != MSDOS_ROOT_INO) {
 619                 ino = fat_parent_ino(walk,1);
 620                 iput(walk);
 621                 if (ino < 0) return ino;
 622                 if (ino == old_ino) return -EINVAL;
 623                 if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
 624         }
 625         iput(walk);
 626         /* find free spot */
 627         while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
 628             SCAN_ANY)) < 0) {
 629                 if (error != -ENOENT) return error;
 630                 error = fat_add_cluster(new_dir);
 631                 if (error) return error;
 632         }
 633         exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 634         if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
 635                 brelse(free_bh);
 636                 if (exists) brelse(new_bh);
 637                 return -EIO;
 638         }
 639         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 640                 iput(old_inode);
 641                 brelse(free_bh);
 642                 if (exists) brelse(new_bh);
 643                 return -ENOENT;
 644         }
 645         new_inode = NULL; /* to make GCC happy */
 646         if (exists) {  /* Trash the old file! */
 647                 if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
 648                         iput(old_inode);
 649                         brelse(new_bh);
 650                         return -EIO;
 651                 }
 652                 error = S_ISDIR(new_inode->i_mode)
 653                         ? (old_de->attr & ATTR_DIR)
 654                                 ? msdos_empty(new_inode)
 655                                 : -EPERM
 656                         : (old_de->attr & ATTR_DIR)
 657                                 ? -EPERM
 658                                 : 0;
 659                 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 660                 if (error) {
 661                         iput(new_inode);
 662                         iput(old_inode);
 663                         brelse(new_bh);
 664                         return error;
 665                 }
 666                 new_inode->i_nlink = 0;
 667                 MSDOS_I(new_inode)->i_busy = 1;
 668                 new_inode->i_dirt = 1;
 669                 new_de->name[0] = DELETED_FLAG;
 670                 mark_buffer_dirty(new_bh, 1);
 671         }
 672         memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
 673         memcpy(free_de->name,new_name,MSDOS_NAME);
 674         free_de->attr = is_hid
 675                 ? (free_de->attr|ATTR_HIDDEN)
 676                 : (free_de->attr&~ATTR_HIDDEN);
 677         if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
 678                 free_de->name[0] = DELETED_FLAG;
 679 /*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
 680                 brelse(free_bh);
 681                 if (exists) {
 682                         iput(new_inode);
 683                         brelse(new_bh);
 684                 }
 685                 return -EIO;
 686         }
 687         if (exists && S_ISDIR(new_inode->i_mode)) {
 688                 new_dir->i_nlink--;
 689                 new_dir->i_dirt = 1;
 690         }
 691         msdos_read_inode(free_inode);
 692         MSDOS_I(old_inode)->i_busy = 1;
 693         MSDOS_I(old_inode)->i_linked = free_inode;
 694         MSDOS_I(free_inode)->i_oldlink = old_inode;
 695         fat_cache_inval_inode(old_inode);
 696         old_inode->i_dirt = 1;
 697         old_de->name[0] = DELETED_FLAG;
 698         mark_buffer_dirty(old_bh, 1);
 699         mark_buffer_dirty(free_bh, 1);
 700         if (exists) {
 701                 MSDOS_I(new_inode)->i_depend = free_inode;
 702                 MSDOS_I(free_inode)->i_old = new_inode;
 703                 /* Two references now exist to free_inode so increase count */
 704                 free_inode->i_count++;
 705                 /* free_inode is put after putting new_inode and old_inode */
 706                 iput(new_inode);
 707                 dcache_add(new_dir, new_name, new_len, new_ino);
 708                 brelse(new_bh);
 709         }
 710         if (S_ISDIR(old_inode->i_mode)) {
 711                 if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
 712                     &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
 713                 if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
 714                         brelse(dotdot_bh);
 715                         error = -EIO;
 716                         goto rename_done;
 717                 }
 718                 dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
 719                     MSDOS_I(new_dir)->i_start;
 720                 dotdot_inode->i_dirt = 1;
 721                 mark_buffer_dirty(dotdot_bh, 1);
 722                 old_dir->i_nlink--;
 723                 new_dir->i_nlink++;
 724                 /* no need to mark them dirty */
 725                 dotdot_inode->i_nlink = new_dir->i_nlink;
 726                 iput(dotdot_inode);
 727                 brelse(dotdot_bh);
 728         }
 729         error = 0;
 730 rename_done:
 731         brelse(free_bh);
 732         iput(old_inode);
 733         return error;
 734 }
 735 
 736 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
 737 int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 738         struct inode *new_dir,const char *new_name,int new_len)
 739 {
 740         struct super_block *sb = old_dir->i_sb;
 741         char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
 742         struct buffer_head *old_bh;
 743         struct msdos_dir_entry *old_de;
 744         int old_ino,error;
 745         int is_hid,old_hid; /* if new file and old file are hidden */
 746 
 747         if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->options.name_check,
 748                                        old_name,old_len,old_msdos_name,1,
 749                                        MSDOS_SB(old_dir->i_sb)->options.dotsOK))
 750             < 0) goto rename_done;
 751         if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->options.name_check,
 752                                        new_name,new_len,new_msdos_name,0,
 753                                        MSDOS_SB(new_dir->i_sb)->options.dotsOK))
 754             < 0) goto rename_done;
 755         is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
 756         old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
 757         if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
 758             &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
 759         fat_lock_creation();
 760         if (old_dir == new_dir)
 761                 error = rename_same_dir(old_dir,old_msdos_name,old_len,new_dir,
 762                     new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
 763         else error = rename_diff_dir(old_dir,old_msdos_name,old_len,new_dir,
 764                     new_msdos_name,new_len,old_bh,old_de,old_ino,is_hid);
 765         fat_unlock_creation();
 766         brelse(old_bh);
 767 rename_done:
 768         iput(old_dir);
 769         iput(new_dir);
 770         return error;
 771 }
 772 
 773 
 774 /* The public inode operations for the msdos fs */
 775 struct inode_operations msdos_dir_inode_operations = {
 776         &fat_dir_operations,    /* default directory file-ops */
 777         msdos_create,           /* create */
 778         msdos_lookup,           /* lookup */
 779         NULL,                   /* link */
 780         msdos_unlink,           /* unlink */
 781         NULL,                   /* symlink */
 782         msdos_mkdir,            /* mkdir */
 783         msdos_rmdir,            /* rmdir */
 784         NULL,                   /* mknod */
 785         msdos_rename,           /* rename */
 786         NULL,                   /* readlink */
 787         NULL,                   /* follow_link */
 788         NULL,                   /* readpage */
 789         NULL,                   /* writepage */
 790         fat_bmap,               /* bmap */
 791         NULL,                   /* truncate */
 792         NULL                    /* permission */
 793 };
 794 
 795 
 796 void msdos_read_inode(struct inode *inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 797 {
 798         fat_read_inode(inode, &msdos_dir_inode_operations);
 799 }
 800 
 801 
 802 
 803 #ifdef MODULE
 804 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 805 {
 806         return init_msdos_fs();
 807 }
 808 
 809 
 810 void cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 811 {
 812         unregister_filesystem(&msdos_fs_type);
 813 }
 814 
 815 #endif
 816 

/* [previous][next][first][last][top][bottom][index][help] */