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

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