root/fs/msdos/namei.c

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

DEFINITIONS

This source file includes following definitions.
  1. msdos_format_name
  2. msdos_find
  3. msdos_lookup
  4. msdos_create_entry
  5. msdos_create
  6. dump_fat
  7. msdos_mkdir
  8. msdos_empty
  9. msdos_rmdir
  10. msdos_unlinkx
  11. msdos_unlink
  12. msdos_unlink_umsdos
  13. rename_same_dir
  14. rename_diff_dir
  15. msdos_rename

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

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