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)->dotsOK;
 158         res = msdos_format_name(MSDOS_SB(dir->i_sb)->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         PRINTK (("msdos_lookup 3\n"));
 206         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
 207                 iput(dir);
 208                 return res;
 209         }
 210         PRINTK (("msdos_lookup 4\n"));
 211         if (bh) brelse(bh);
 212         PRINTK (("msdos_lookup 4.5\n"));
 213 /* printk("lookup: ino=%d\n",ino); */
 214         if (!(*result = iget(dir->i_sb,ino))) {
 215                 iput(dir);
 216                 return -EACCES;
 217         }
 218         PRINTK (("msdos_lookup 5\n"));
 219         if (!(*result)->i_sb ||
 220             ((*result)->i_sb->s_magic != MSDOS_SUPER_MAGIC)) {
 221                 /* crossed a mount point into a non-msdos fs */
 222                 iput(dir);
 223                 return 0;
 224         }
 225         if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
 226                 iput(*result);
 227                 iput(dir);
 228                 return -ENOENT;
 229         }
 230         PRINTK (("msdos_lookup 6\n"));
 231         while (MSDOS_I(*result)->i_old) {
 232                 next = MSDOS_I(*result)->i_old;
 233                 iput(*result);
 234                 if (!(*result = iget(next->i_sb,next->i_ino))) {
 235                         fat_fs_panic(dir->i_sb,"msdos_lookup: Can't happen");
 236                         iput(dir);
 237                         return -ENOENT;
 238                 }
 239         }
 240         PRINTK (("msdos_lookup 7\n"));
 241         iput(dir);
 242         PRINTK (("msdos_lookup 8\n"));
 243         return 0;
 244 }
 245 
 246 
 247 /***** Creates a directory entry (name is already formatted). */
 248 static int msdos_create_entry(struct inode *dir, const char *name,
     /* [previous][next][first][last][top][bottom][index][help] */
 249     int is_dir, int is_hid, struct inode **result)
 250 {
 251         struct super_block *sb = dir->i_sb;
 252         struct buffer_head *bh;
 253         struct msdos_dir_entry *de;
 254         int res,ino;
 255 
 256         if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) {
 257                 if (res != -ENOENT) return res;
 258                 if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
 259                 if ((res = fat_add_cluster(dir)) < 0) return res;
 260                 if ((res = fat_scan(dir,NULL,&bh,&de,&ino,SCAN_ANY)) < 0) return res;
 261         }
 262         /*
 263          * XXX all times should be set by caller upon successful completion.
 264          */
 265         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 266         dir->i_dirt = 1;
 267         memcpy(de->name,name,MSDOS_NAME);
 268         memset(de->unused, 0, sizeof(de->unused));
 269         de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 270         de->attr = is_hid ? (de->attr|ATTR_HIDDEN) : (de->attr&~ATTR_HIDDEN);
 271         de->start = 0;
 272         fat_date_unix2dos(dir->i_mtime,&de->time,&de->date);
 273         de->size = 0;
 274         mark_buffer_dirty(bh, 1);
 275         if ((*result = iget(dir->i_sb,ino)) != NULL)
 276                 msdos_read_inode(*result);
 277         brelse(bh);
 278         if (!*result) return -EIO;
 279         (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
 280             CURRENT_TIME;
 281         (*result)->i_dirt = 1;
 282         return 0;
 283 }
 284 
 285 /***** Create a file or directory */
 286 int msdos_create(struct inode *dir,const char *name,int len,int mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 287         struct inode **result)
 288 {
 289         struct super_block *sb = dir->i_sb;
 290         struct buffer_head *bh;
 291         struct msdos_dir_entry *de;
 292         char msdos_name[MSDOS_NAME];
 293         int ino,res,is_hid;
 294 
 295         if (!dir) return -ENOENT;
 296         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
 297             msdos_name,0,MSDOS_SB(dir->i_sb)->dotsOK)) < 0) {
 298                 iput(dir);
 299                 return res;
 300         }
 301         is_hid = (name[0]=='.') && (msdos_name[0]!='.');
 302         fat_lock_creation();
 303         /* Scan for existing file twice, so that creating a file fails
 304          * with -EINVAL if the other (dotfile/nondotfile) exists.
 305          * Else SCAN_ANY would do. Maybe use EACCES, EBUSY, ENOSPC, ENFILE?
 306          */
 307         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_HID) >= 0) {
 308                 fat_unlock_creation();
 309                 brelse(bh);
 310                 iput(dir);
 311                 return is_hid ? -EEXIST : -EINVAL;
 312         }
 313         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_NOTHID) >= 0) {
 314                 fat_unlock_creation();
 315                 brelse(bh);
 316                 iput(dir);
 317                 return is_hid ? -EINVAL : -EEXIST;
 318         }
 319         res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),is_hid,result);
 320         fat_unlock_creation();
 321         iput(dir);
 322         return res;
 323 }
 324 
 325 
 326 #ifdef DEBUG
 327 
 328 static void dump_fat(struct super_block *sb,int start)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330         printk("[");
 331         while (start) {
 332                 printk("%d ",start);
 333                 start = fat_access(sb,start,-1);
 334                 if (!start) {
 335                         printk("ERROR");
 336                         break;
 337                 }
 338                 if (start == -1) break;
 339         }
 340         printk("]\n");
 341 }
 342 
 343 #endif
 344 
 345 /***** See if directory is empty */
 346 static int msdos_empty(struct inode *dir)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348         struct super_block *sb = dir->i_sb;
 349         loff_t pos;
 350         struct buffer_head *bh;
 351         struct msdos_dir_entry *de;
 352 
 353         if (dir->i_count > 1)
 354                 return -EBUSY;
 355         if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
 356                 pos = 0;
 357                 bh = NULL;
 358                 while (fat_get_entry(dir,&pos,&bh,&de) > -1)
 359                         if (!IS_FREE(de->name) && strncmp(de->name,MSDOS_DOT,
 360                             MSDOS_NAME) && strncmp(de->name,MSDOS_DOTDOT,
 361                             MSDOS_NAME)) {
 362                                 brelse(bh);
 363                                 return -ENOTEMPTY;
 364                         }
 365                 if (bh)
 366                         brelse(bh);
 367         }
 368         return 0;
 369 }
 370 
 371 /***** Remove a directory */
 372 int msdos_rmdir(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374         struct super_block *sb = dir->i_sb;
 375         int res,ino;
 376         struct buffer_head *bh;
 377         struct msdos_dir_entry *de;
 378         struct inode *inode;
 379 
 380         bh = NULL;
 381         inode = NULL;
 382         res = -EPERM;
 383         if (name[0] == '.' && (len == 1 || (len == 2 && name[1] == '.')))
 384                 goto rmdir_done;
 385         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
 386         res = -ENOENT;
 387         if (!(inode = iget(dir->i_sb,ino))) goto rmdir_done;
 388         res = -ENOTDIR;
 389         if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
 390         res = -EBUSY;
 391         if (dir->i_dev != inode->i_dev || dir == inode)
 392           goto rmdir_done;
 393         res = msdos_empty(inode);
 394         if (res)
 395                 goto rmdir_done;
 396         inode->i_nlink = 0;
 397         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 398         dir->i_nlink--;
 399         inode->i_dirt = dir->i_dirt = 1;
 400         de->name[0] = DELETED_FLAG;
 401         mark_buffer_dirty(bh, 1);
 402         res = 0;
 403 rmdir_done:
 404         brelse(bh);
 405         iput(dir);
 406         iput(inode);
 407         return res;
 408 }
 409 
 410 /***** Make a directory */
 411 int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 412 {
 413         struct super_block *sb = dir->i_sb;
 414         struct buffer_head *bh;
 415         struct msdos_dir_entry *de;
 416         struct inode *inode,*dot;
 417         char msdos_name[MSDOS_NAME];
 418         int ino,res,is_hid;
 419 
 420         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
 421             msdos_name,0,MSDOS_SB(dir->i_sb)->dotsOK)) < 0) {
 422                 iput(dir);
 423                 return res;
 424         }
 425         is_hid = (name[0]=='.') && (msdos_name[0]!='.');
 426         fat_lock_creation();
 427         if (fat_scan(dir,msdos_name,&bh,&de,&ino,SCAN_ANY) >= 0) {
 428                 fat_unlock_creation();
 429                 brelse(bh);
 430                 iput(dir);
 431                 return -EEXIST;
 432         }
 433         if ((res = msdos_create_entry(dir,msdos_name,1,is_hid,&inode)) < 0) {
 434                 fat_unlock_creation();
 435                 iput(dir);
 436                 return res;
 437         }
 438         dir->i_nlink++;
 439         inode->i_nlink = 2; /* no need to mark them dirty */
 440         MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
 441         if ((res = fat_add_cluster(inode)) < 0) goto mkdir_error;
 442         if ((res = msdos_create_entry(inode,MSDOS_DOT,1,0,&dot)) < 0)
 443                 goto mkdir_error;
 444         dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
 445         MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
 446         dot->i_nlink = inode->i_nlink;
 447         dot->i_dirt = 1;
 448         iput(dot);
 449         if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,0,&dot)) < 0)
 450                 goto mkdir_error;
 451         fat_unlock_creation();
 452         dot->i_size = dir->i_size;
 453         MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
 454         dot->i_nlink = dir->i_nlink;
 455         dot->i_dirt = 1;
 456         MSDOS_I(inode)->i_busy = 0;
 457         iput(dot);
 458         iput(inode);
 459         iput(dir);
 460         return 0;
 461 mkdir_error:
 462         iput(inode);
 463         if (msdos_rmdir(dir,name,len) < 0)
 464                 fat_fs_panic(dir->i_sb,"rmdir in mkdir failed");
 465         fat_unlock_creation();
 466         return res;
 467 }
 468 
 469 /***** Unlink a file */
 470 static int msdos_unlinkx(
     /* [previous][next][first][last][top][bottom][index][help] */
 471         struct inode *dir,
 472         const char *name,
 473         int len,
 474         int nospc)      /* Flag special file ? */
 475 {
 476         struct super_block *sb = dir->i_sb;
 477         int res,ino;
 478         struct buffer_head *bh;
 479         struct msdos_dir_entry *de;
 480         struct inode *inode;
 481 
 482         bh = NULL;
 483         inode = NULL;
 484         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
 485                 goto unlink_done;
 486         if (!(inode = iget(dir->i_sb,ino))) {
 487                 res = -ENOENT;
 488                 goto unlink_done;
 489         }
 490         if (!S_ISREG(inode->i_mode) && nospc){
 491                 res = -EPERM;
 492                 goto unlink_done;
 493         }
 494         if (IS_IMMUTABLE(inode)){
 495                 res = -EPERM;
 496                 goto unlink_done;
 497         }
 498         inode->i_nlink = 0;
 499         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 500         MSDOS_I(inode)->i_busy = 1;
 501         inode->i_dirt = dir->i_dirt = 1;
 502         de->name[0] = DELETED_FLAG;
 503         mark_buffer_dirty(bh, 1);
 504 unlink_done:
 505         brelse(bh);
 506         iput(inode);
 507         iput(dir);
 508         return res;
 509 }
 510 
 511 /***** Unlink, as called for msdosfs */
 512 int msdos_unlink(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 513 {
 514         return msdos_unlinkx (dir,name,len,1);
 515 }
 516 
 517 /***** Unlink, as called for umsdosfs */
 518 int msdos_unlink_umsdos(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 519 {
 520         return msdos_unlinkx (dir,name,len,0);
 521 }
 522 
 523 /***** Rename within a directory */
 524 static int rename_same_dir(struct inode *old_dir,char *old_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 525     struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
 526     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 527 {
 528         struct super_block *sb = old_dir->i_sb;
 529         struct buffer_head *new_bh;
 530         struct msdos_dir_entry *new_de;
 531         struct inode *new_inode,*old_inode;
 532         int new_ino,exists,error;
 533 
 534         if (!strncmp(old_name,new_name,MSDOS_NAME)) goto set_hid;
 535         exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 536         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 537                 if (exists) brelse(new_bh);
 538                 return -ENOENT;
 539         }
 540         if (exists) {
 541                 if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
 542                         brelse(new_bh);
 543                         return -EIO;
 544                 }
 545                 error = S_ISDIR(new_inode->i_mode)
 546                         ? (old_de->attr & ATTR_DIR)
 547                                 ? msdos_empty(new_inode)
 548                                 : -EPERM
 549                         : (old_de->attr & ATTR_DIR)
 550                                 ? -EPERM
 551                                 : 0;
 552                 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 553                 if (error) {
 554                         iput(new_inode);
 555                         brelse(new_bh);
 556                         return error;
 557                 }
 558                 if (S_ISDIR(new_inode->i_mode)) {
 559                         new_dir->i_nlink--;
 560                         new_dir->i_dirt = 1;
 561                 }
 562                 new_inode->i_nlink = 0;
 563                 MSDOS_I(new_inode)->i_busy = 1;
 564                 new_inode->i_dirt = 1;
 565                 new_de->name[0] = DELETED_FLAG;
 566                 mark_buffer_dirty(new_bh, 1);
 567                 iput(new_inode);
 568                 brelse(new_bh);
 569         }
 570         memcpy(old_de->name,new_name,MSDOS_NAME);
 571 set_hid:
 572         old_de->attr = is_hid
 573                 ? (old_de->attr | ATTR_HIDDEN)
 574                 : (old_de->attr &~ ATTR_HIDDEN);
 575         mark_buffer_dirty(old_bh, 1);
 576         /* update binary info for conversion, i_attrs */
 577         if ((old_inode = iget(old_dir->i_sb,old_ino)) != NULL) {
 578                 msdos_read_inode(old_inode);
 579                 MSDOS_I(old_inode)->i_attrs = is_hid
 580                         ? (MSDOS_I(old_inode)->i_attrs |  ATTR_HIDDEN)
 581                         : (MSDOS_I(old_inode)->i_attrs &~ ATTR_HIDDEN);
 582                 iput(old_inode);
 583         }
 584         return 0;
 585 }
 586 
 587 /***** Rename across directories - a nonphysical move */
 588 static int rename_diff_dir(struct inode *old_dir,char *old_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 589     struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
 590     struct msdos_dir_entry *old_de,int old_ino,int is_hid)
 591 {
 592         struct super_block *sb = old_dir->i_sb;
 593         struct buffer_head *new_bh,*free_bh,*dotdot_bh;
 594         struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
 595         struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
 596         int new_ino,free_ino,dotdot_ino;
 597         int error,exists,ino;
 598 
 599         if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
 600         if (old_ino == new_dir->i_ino) return -EINVAL;
 601         if (!(walk = iget(new_dir->i_sb,new_dir->i_ino))) return -EIO;
 602         /* prevent moving directory below itself */
 603         while (walk->i_ino != MSDOS_ROOT_INO) {
 604                 ino = fat_parent_ino(walk,1);
 605                 iput(walk);
 606                 if (ino < 0) return ino;
 607                 if (ino == old_ino) return -EINVAL;
 608                 if (!(walk = iget(new_dir->i_sb,ino))) return -EIO;
 609         }
 610         iput(walk);
 611         /* find free spot */
 612         while ((error = fat_scan(new_dir,NULL,&free_bh,&free_de,&free_ino,
 613             SCAN_ANY)) < 0) {
 614                 if (error != -ENOENT) return error;
 615                 error = fat_add_cluster(new_dir);
 616                 if (error) return error;
 617         }
 618         exists = fat_scan(new_dir,new_name,&new_bh,&new_de,&new_ino,SCAN_ANY) >= 0;
 619         if (!(old_inode = iget(old_dir->i_sb,old_ino))) {
 620                 brelse(free_bh);
 621                 if (exists) brelse(new_bh);
 622                 return -EIO;
 623         }
 624         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 625                 iput(old_inode);
 626                 brelse(free_bh);
 627                 if (exists) brelse(new_bh);
 628                 return -ENOENT;
 629         }
 630         new_inode = NULL; /* to make GCC happy */
 631         if (exists) {  /* Trash the old file! */
 632                 if (!(new_inode = iget(new_dir->i_sb,new_ino))) {
 633                         iput(old_inode);
 634                         brelse(new_bh);
 635                         return -EIO;
 636                 }
 637                 error = S_ISDIR(new_inode->i_mode)
 638                         ? (old_de->attr & ATTR_DIR)
 639                                 ? msdos_empty(new_inode)
 640                                 : -EPERM
 641                         : (old_de->attr & ATTR_DIR)
 642                                 ? -EPERM
 643                                 : 0;
 644                 if (!error && (old_de->attr & ATTR_SYS)) error = -EPERM;
 645                 if (error) {
 646                         iput(new_inode);
 647                         iput(old_inode);
 648                         brelse(new_bh);
 649                         return error;
 650                 }
 651                 new_inode->i_nlink = 0;
 652                 MSDOS_I(new_inode)->i_busy = 1;
 653                 new_inode->i_dirt = 1;
 654                 new_de->name[0] = DELETED_FLAG;
 655                 mark_buffer_dirty(new_bh, 1);
 656         }
 657         memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
 658         memcpy(free_de->name,new_name,MSDOS_NAME);
 659         free_de->attr = is_hid
 660                 ? (free_de->attr|ATTR_HIDDEN)
 661                 : (free_de->attr&~ATTR_HIDDEN);
 662         if (!(free_inode = iget(new_dir->i_sb,free_ino))) {
 663                 free_de->name[0] = DELETED_FLAG;
 664 /*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
 665                 brelse(free_bh);
 666                 if (exists) {
 667                         iput(new_inode);
 668                         brelse(new_bh);
 669                 }
 670                 return -EIO;
 671         }
 672         if (exists && S_ISDIR(new_inode->i_mode)) {
 673                 new_dir->i_nlink--;
 674                 new_dir->i_dirt = 1;
 675         }
 676         msdos_read_inode(free_inode);
 677         MSDOS_I(old_inode)->i_busy = 1;
 678         fat_cache_inval_inode(old_inode);
 679         old_inode->i_dirt = 1;
 680         old_de->name[0] = DELETED_FLAG;
 681         mark_buffer_dirty(old_bh, 1);
 682         mark_buffer_dirty(free_bh, 1);
 683         if (!exists) iput(free_inode);
 684         else {
 685                 MSDOS_I(new_inode)->i_depend = free_inode;
 686                 MSDOS_I(free_inode)->i_old = new_inode;
 687                 /* free_inode is put when putting new_inode */
 688                 iput(new_inode);
 689                 brelse(new_bh);
 690         }
 691         if (S_ISDIR(old_inode->i_mode)) {
 692                 if ((error = fat_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
 693                     &dotdot_de,&dotdot_ino,SCAN_ANY)) < 0) goto rename_done;
 694                 if (!(dotdot_inode = iget(old_inode->i_sb,dotdot_ino))) {
 695                         brelse(dotdot_bh);
 696                         error = -EIO;
 697                         goto rename_done;
 698                 }
 699                 dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
 700                     MSDOS_I(new_dir)->i_start;
 701                 dotdot_inode->i_dirt = 1;
 702                 mark_buffer_dirty(dotdot_bh, 1);
 703                 old_dir->i_nlink--;
 704                 new_dir->i_nlink++;
 705                 /* no need to mark them dirty */
 706                 dotdot_inode->i_nlink = new_dir->i_nlink;
 707                 iput(dotdot_inode);
 708                 brelse(dotdot_bh);
 709         }
 710         error = 0;
 711 rename_done:
 712         brelse(free_bh);
 713         iput(old_inode);
 714         return error;
 715 }
 716 
 717 /***** Rename, a wrapper for rename_same_dir & rename_diff_dir */
 718 int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 719         struct inode *new_dir,const char *new_name,int new_len)
 720 {
 721         struct super_block *sb = old_dir->i_sb;
 722         char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
 723         struct buffer_head *old_bh;
 724         struct msdos_dir_entry *old_de;
 725         int old_ino,error;
 726         int is_hid,old_hid; /* if new file and old file are hidden */
 727 
 728         if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->name_check,
 729             old_name,old_len,old_msdos_name,1,MSDOS_SB(old_dir->i_sb)->dotsOK))
 730             < 0) goto rename_done;
 731         if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->name_check,
 732             new_name,new_len,new_msdos_name,0,MSDOS_SB(new_dir->i_sb)->dotsOK))
 733             < 0) goto rename_done;
 734         is_hid = (new_name[0]=='.') && (new_msdos_name[0]!='.');
 735         old_hid = (old_name[0]=='.') && (old_msdos_name[0]!='.');
 736         if ((error = fat_scan(old_dir,old_msdos_name,&old_bh,&old_de,
 737             &old_ino,old_hid?SCAN_HID:SCAN_NOTHID)) < 0) goto rename_done;
 738         fat_lock_creation();
 739         if (old_dir == new_dir)
 740                 error = rename_same_dir(old_dir,old_msdos_name,new_dir,
 741                     new_msdos_name,old_bh,old_de,old_ino,is_hid);
 742         else error = rename_diff_dir(old_dir,old_msdos_name,new_dir,
 743                     new_msdos_name,old_bh,old_de,old_ino,is_hid);
 744         fat_unlock_creation();
 745         brelse(old_bh);
 746 rename_done:
 747         iput(old_dir);
 748         iput(new_dir);
 749         return error;
 750 }
 751 
 752 
 753 /* The public inode operations for the msdos fs */
 754 struct inode_operations msdos_dir_inode_operations = {
 755         &fat_dir_operations,    /* default directory file-ops */
 756         msdos_create,           /* create */
 757         msdos_lookup,           /* lookup */
 758         NULL,                   /* link */
 759         msdos_unlink,           /* unlink */
 760         NULL,                   /* symlink */
 761         msdos_mkdir,            /* mkdir */
 762         msdos_rmdir,            /* rmdir */
 763         NULL,                   /* mknod */
 764         msdos_rename,           /* rename */
 765         NULL,                   /* readlink */
 766         NULL,                   /* follow_link */
 767         NULL,                   /* readpage */
 768         NULL,                   /* writepage */
 769         fat_bmap,               /* bmap */
 770         NULL,                   /* truncate */
 771         NULL                    /* permission */
 772 };
 773 
 774 
 775 void msdos_read_inode(struct inode *inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 776 {
 777         fat_read_inode(inode, &msdos_dir_inode_operations);
 778 }
 779 
 780 
 781 
 782 #ifdef MODULE
 783 int init_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 784 {
 785         return init_msdos_fs();
 786 }
 787 
 788 
 789 void cleanup_module(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 790 {
 791         unregister_filesystem(&msdos_fs_type);
 792 }
 793 
 794 #endif
 795 

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