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_rmdir
  9. msdos_unlink
  10. rename_same_dir
  11. rename_diff_dir
  12. msdos_rename

   1 /*
   2  *  linux/fs/msdos/namei.c
   3  *
   4  *  Written 1992 by Werner Almesberger
   5  */
   6 
   7 #include <asm/segment.h>
   8 
   9 #include <linux/sched.h>
  10 #include <linux/msdos_fs.h>
  11 #include <linux/kernel.h>
  12 #include <linux/errno.h>
  13 #include <linux/string.h>
  14 #include <linux/stat.h>
  15 
  16 /* MS-DOS "device special files" */
  17 
  18 static char *reserved_names[] = {
  19     "CON     ","PRN     ","NUL     ","AUX     ",
  20     "LPT1    ","LPT2    ","LPT3    ","LPT4    ",
  21     "COM1    ","COM2    ","COM3    ","COM4    ",
  22     NULL };
  23 
  24 
  25 /* Characters that are undesirable in an MS-DOS file name */
  26   
  27 static char bad_chars[] = "*?<>|\" ";
  28 static char bad_if_strict[] = "+=,;";
  29 
  30 
  31 /* Formats an MS-DOS file name. Rejects invalid names. */
  32 
  33 static int msdos_format_name(char conv,const char *name,int len,char *res)
     /* [previous][next][first][last][top][bottom][index][help] */
  34 {
  35         char *walk,**reserved;
  36         char c;
  37         int space;
  38 
  39         if (get_fs_byte(name) == DELETED_FLAG) return -EINVAL;
  40         if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 &&
  41             get_fs_byte(name+1) == '.'))) {
  42                 memset(res+1,' ',10);
  43                 while (len--) *res++ = '.';
  44                 return 0;
  45         }
  46         space = 0; /* to make GCC happy */
  47         c = 0;
  48         for (walk = res; len && walk-res < 8; walk++) {
  49                 c = get_fs_byte(name++);
  50                 len--;
  51                 if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
  52                 if (conv == 's' && strchr(bad_if_strict,c)) return -EINVAL;
  53                 if (c >= 'A' && c <= 'Z') {
  54                         if (conv == 's') return -EINVAL;
  55                         c += 32;
  56                 }
  57                 if (c < ' ' || c == ':' || c == '\\') return -EINVAL;
  58                 if (c == '.') break;
  59                 space = c == ' ';
  60                 *walk = c >= 'a' && c <= 'z' ? c-32 : c;
  61         }
  62         if (space) return -EINVAL;
  63         if (conv == 's' && len && c != '.') {
  64                 c = get_fs_byte(name++);
  65                 len--;
  66                 if (c != '.') return -EINVAL;
  67         }
  68         while (c != '.' && len--) c = get_fs_byte(name++);
  69         if (walk == res) return -EINVAL;
  70         if (c == '.') {
  71                 while (walk-res < 8) *walk++ = ' ';
  72                 while (len > 0 && walk-res < MSDOS_NAME) {
  73                         c = get_fs_byte(name++);
  74                         len--;
  75                         if (conv != 'r' && strchr(bad_chars,c)) return -EINVAL;
  76                         if (conv == 's' && strchr(bad_if_strict,c))
  77                                 return -EINVAL;
  78                         if (c < ' ' || c == ':' || c == '\\' || c == '.')
  79                                 return -EINVAL;
  80                         if (c >= 'A' && c <= 'Z') {
  81                                 if (conv == 's') return -EINVAL;
  82                                 c += 32;
  83                         }
  84                         space = c == ' ';
  85                         *walk++ = c >= 'a' && c <= 'z' ? c-32 : c;
  86                 }
  87                 if (space) return -EINVAL;
  88                 if (conv == 's' && len) return -EINVAL;
  89         }
  90         while (walk-res < MSDOS_NAME) *walk++ = ' ';
  91         for (reserved = reserved_names; *reserved; reserved++)
  92                 if (!strncmp(res,*reserved,8)) return -EINVAL;
  93         return 0;
  94 }
  95 
  96 
  97 /* Locates a directory entry. */
  98 
  99 static int msdos_find(struct inode *dir,const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 100     struct buffer_head **bh,struct msdos_dir_entry **de,int *ino)
 101 {
 102         char msdos_name[MSDOS_NAME];
 103         int res;
 104 
 105         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
 106             msdos_name)) < 0) return res;
 107         return msdos_scan(dir,msdos_name,bh,de,ino);
 108 }
 109 
 110 
 111 int msdos_lookup(struct inode *dir,const char *name,int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 112     struct inode **result)
 113 {
 114         int ino,res;
 115         struct msdos_dir_entry *de;
 116         struct buffer_head *bh;
 117         struct inode *next;
 118 
 119         *result = NULL;
 120         if (!dir) return -ENOENT;
 121         if (!S_ISDIR(dir->i_mode)) {
 122                 iput(dir);
 123                 return -ENOENT;
 124         }
 125         if (len == 1 && get_fs_byte(name) == '.') {
 126                 *result = dir;
 127                 return 0;
 128         }
 129         if (len == 2 && get_fs_byte(name) == '.' && get_fs_byte(name+1) == '.')
 130             {
 131                 ino = msdos_parent_ino(dir,0);
 132                 iput(dir);
 133                 if (ino < 0) return ino;
 134                 if (!(*result = iget(dir->i_dev,ino))) return -EACCES;
 135                 return 0;
 136         }
 137         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) {
 138                 iput(dir);
 139                 return res;
 140         }
 141         if (bh) brelse(bh);
 142 /* printk("lookup: ino=%d\r\n",ino); */
 143         if (!(*result = iget(dir->i_dev,ino))) {
 144                 iput(dir);
 145                 return -EACCES;
 146         }
 147         if (MSDOS_I(*result)->i_busy) { /* mkdir in progress */
 148                 iput(*result);
 149                 iput(dir);
 150                 return -ENOENT;
 151         }
 152         while (MSDOS_I(*result)->i_old) {
 153                 next = MSDOS_I(*result)->i_old;
 154                 iput(*result);
 155                 if (!(*result = iget(next->i_dev,next->i_ino)))
 156                         panic("msdos_lookup: Can't happen");
 157         }
 158         iput(dir);
 159         return 0;
 160 }
 161 
 162 
 163 /* Creates a directory entry (name is already formatted). */
 164 
 165 static int msdos_create_entry(struct inode *dir,char *name,int is_dir,
     /* [previous][next][first][last][top][bottom][index][help] */
 166     struct inode **result)
 167 {
 168         struct buffer_head *bh;
 169         struct msdos_dir_entry *de;
 170         int res,ino;
 171 
 172         if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) {
 173                 if (dir->i_ino == MSDOS_ROOT_INO) return -ENOSPC;
 174                 if ((res = msdos_add_cluster(dir)) < 0) return res;
 175                 if ((res = msdos_scan(dir,NULL,&bh,&de,&ino)) < 0) return res;
 176         }
 177         memcpy(de->name,name,MSDOS_NAME);
 178         de->attr = is_dir ? ATTR_DIR : ATTR_ARCH;
 179         de->start = 0;
 180         date_unix2dos(CURRENT_TIME,&de->time,&de->date);
 181         de->size = 0;
 182         bh->b_dirt = 1;
 183         if (*result = iget(dir->i_dev,ino)) msdos_read_inode(*result);
 184         brelse(bh);
 185         if (!*result) return -EIO;
 186         (*result)->i_mtime = (*result)->i_atime = (*result)->i_ctime =
 187             CURRENT_TIME;
 188         (*result)->i_dirt = 1;
 189         return 0;
 190 }
 191 
 192 
 193 int msdos_create(struct inode *dir,const char *name,int len,int mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 194         struct inode **result)
 195 {
 196         struct buffer_head *bh;
 197         struct msdos_dir_entry *de;
 198         char msdos_name[MSDOS_NAME];
 199         int ino,res;
 200 
 201         if (!dir) return -ENOENT;
 202         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
 203             msdos_name)) < 0) {
 204                 iput(dir);
 205                 return res;
 206         }
 207         lock_creation();
 208         if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
 209                 unlock_creation();
 210                 brelse(bh);
 211                 iput(dir);
 212                 return -EEXIST;
 213         }
 214         res = msdos_create_entry(dir,msdos_name,S_ISDIR(mode),result);
 215         unlock_creation();
 216         iput(dir);
 217         return res;
 218 }
 219 
 220 
 221 #ifdef DEBUG
 222 
 223 static void dump_fat(struct super_block *sb,int start)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225         printk("[");
 226         while (start) {
 227                 printk("%d ",start);
 228                 start = fat_access(sb,start,-1);
 229                 if (!start) {
 230                         printk("ERROR");
 231                         break;
 232                 }
 233                 if (start == -1) break;
 234         }
 235         printk("]\n");
 236 }
 237 
 238 #endif
 239 
 240 
 241 int msdos_mkdir(struct inode *dir,const char *name,int len,int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243         struct buffer_head *bh;
 244         struct msdos_dir_entry *de;
 245         struct inode *inode,*dot;
 246         char msdos_name[MSDOS_NAME];
 247         int ino,res;
 248 
 249         if ((res = msdos_format_name(MSDOS_SB(dir->i_sb)->name_check,name,len,
 250             msdos_name)) < 0) {
 251                 iput(dir);
 252                 return res;
 253         }
 254         lock_creation();
 255         if (msdos_scan(dir,msdos_name,&bh,&de,&ino) >= 0) {
 256                 unlock_creation();
 257                 brelse(bh);
 258                 iput(dir);
 259                 return -EEXIST;
 260         }
 261         if ((res = msdos_create_entry(dir,msdos_name,1,&inode)) < 0) {
 262                 unlock_creation();
 263                 iput(dir);
 264                 return res;
 265         }
 266         dir->i_nlink++;
 267         inode->i_nlink = 2; /* no need to mark them dirty */
 268         MSDOS_I(inode)->i_busy = 1; /* prevent lookups */
 269         if ((res = msdos_add_cluster(inode)) < 0) goto mkdir_error;
 270         if ((res = msdos_create_entry(inode,MSDOS_DOT,1,&dot)) < 0)
 271                 goto mkdir_error;
 272         dot->i_size = inode->i_size; /* doesn't grow in the 2nd create_entry */
 273         MSDOS_I(dot)->i_start = MSDOS_I(inode)->i_start;
 274         dot->i_nlink = inode->i_nlink;
 275         dot->i_dirt = 1;
 276         iput(dot);
 277         if ((res = msdos_create_entry(inode,MSDOS_DOTDOT,1,&dot)) < 0)
 278                 goto mkdir_error;
 279         unlock_creation();
 280         dot->i_size = dir->i_size;
 281         MSDOS_I(dot)->i_start = MSDOS_I(dir)->i_start;
 282         dot->i_nlink = dir->i_nlink;
 283         dot->i_dirt = 1;
 284         MSDOS_I(inode)->i_busy = 0;
 285         iput(dot);
 286         iput(inode);
 287         iput(dir);
 288         return 0;
 289 mkdir_error:
 290         iput(inode);
 291         if (msdos_rmdir(dir,name,len) < 0) panic("rmdir in mkdir failed");
 292         unlock_creation();
 293         return res;
 294 }
 295 
 296 
 297 int msdos_rmdir(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 298 {
 299         int res,ino,pos;
 300         struct buffer_head *bh,*dbh;
 301         struct msdos_dir_entry *de,*dde;
 302         struct inode *inode;
 303 
 304         bh = NULL;
 305         inode = NULL;
 306         res = -EINVAL;
 307         if (get_fs_byte(name) == '.' && (len == 1 || (len == 2 &&
 308             get_fs_byte(name+1) == '.'))) goto rmdir_done;
 309         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0) goto rmdir_done;
 310         res = -ENOENT;
 311         if (!(inode = iget(dir->i_dev,ino))) goto rmdir_done;
 312         res = -ENOTDIR;
 313         if (!S_ISDIR(inode->i_mode)) goto rmdir_done;
 314         res = -EBUSY;
 315         if (dir->i_dev != inode->i_dev || dir == inode) goto rmdir_done;
 316         if (inode->i_count > 1) goto rmdir_done;
 317         if (MSDOS_I(inode)->i_start) { /* may be zero in mkdir */
 318                 res = -ENOTEMPTY;
 319                 pos = 0;
 320                 dbh = NULL;
 321                 while (msdos_get_entry(inode,&pos,&dbh,&dde) > -1)
 322                         if (dde->name[0] && ((unsigned char *) dde->name)[0] !=
 323                             DELETED_FLAG && strncmp(dde->name,MSDOS_DOT,
 324                             MSDOS_NAME) && strncmp(dde->name,MSDOS_DOTDOT,
 325                             MSDOS_NAME)) goto rmdir_done;
 326                 if (dbh) brelse(dbh);
 327         }
 328         inode->i_nlink = 0;
 329         dir->i_mtime = CURRENT_TIME;
 330         dir->i_nlink--;
 331         inode->i_dirt = dir->i_dirt = 1;
 332         de->name[0] = DELETED_FLAG;
 333         bh->b_dirt = 1;
 334         res = 0;
 335 rmdir_done:
 336         brelse(bh);
 337         iput(dir);
 338         iput(inode);
 339         return res;
 340 }
 341 
 342 
 343 int msdos_unlink(struct inode *dir,const char *name,int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 344 {
 345         int res,ino;
 346         struct buffer_head *bh;
 347         struct msdos_dir_entry *de;
 348         struct inode *inode;
 349 
 350         bh = NULL;
 351         inode = NULL;
 352         if ((res = msdos_find(dir,name,len,&bh,&de,&ino)) < 0)
 353                 goto unlink_done;
 354         if (!(inode = iget(dir->i_dev,ino))) {
 355                 res = -ENOENT;
 356                 goto unlink_done;
 357         }
 358         if (!S_ISREG(inode->i_mode)) {
 359                 res = -EPERM;
 360                 goto unlink_done;
 361         }
 362         inode->i_nlink = 0;
 363         MSDOS_I(inode)->i_busy = 1;
 364         inode->i_dirt = 1;
 365         de->name[0] = DELETED_FLAG;
 366         bh->b_dirt = 1;
 367 unlink_done:
 368         brelse(bh);
 369         iput(inode);
 370         iput(dir);
 371         return res;
 372 }
 373 
 374 
 375 static int rename_same_dir(struct inode *old_dir,char *old_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 376     struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
 377     struct msdos_dir_entry *old_de,int old_ino)
 378 {
 379         struct buffer_head *new_bh;
 380         struct msdos_dir_entry *new_de;
 381         struct inode *new_inode,*old_inode;
 382         int new_ino;
 383         int exists;
 384 
 385         if (!strncmp(old_name,new_name,MSDOS_NAME)) return 0;
 386         exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino) >= 0;
 387         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 388                 if (exists) brelse(new_bh);
 389                 return -ENOENT;
 390         }
 391         if (exists) {
 392                 if (!(new_inode = iget(new_dir->i_dev,new_ino))) {
 393                         brelse(new_bh);
 394                         return -EIO;
 395                 }
 396                 if (S_ISDIR(new_inode->i_mode)) {
 397                         iput(new_inode);
 398                         brelse(new_bh);
 399                         return -EPERM;
 400                 }
 401                 new_inode->i_nlink = 0;
 402                 MSDOS_I(new_inode)->i_busy = 1;
 403                 new_inode->i_dirt = 1;
 404                 new_de->name[0] = DELETED_FLAG;
 405                 new_bh->b_dirt = 1;
 406                 iput(new_inode);
 407                 brelse(new_bh);
 408         }
 409         memcpy(old_de->name,new_name,MSDOS_NAME);
 410         old_bh->b_dirt = 1;
 411         if (MSDOS_SB(old_dir->i_sb)->conversion == 'a') /* update binary info */
 412                 if (old_inode = iget(old_dir->i_dev,old_ino)) {
 413                         msdos_read_inode(old_inode);
 414                         iput(old_inode);
 415                 }
 416         return 0;
 417 }
 418 
 419 
 420 static int rename_diff_dir(struct inode *old_dir,char *old_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 421     struct inode *new_dir,char *new_name,struct buffer_head *old_bh,
 422     struct msdos_dir_entry *old_de,int old_ino)
 423 {
 424         struct buffer_head *new_bh,*free_bh,*dotdot_bh;
 425         struct msdos_dir_entry *new_de,*free_de,*dotdot_de;
 426         struct inode *old_inode,*new_inode,*free_inode,*dotdot_inode,*walk;
 427         int new_ino,free_ino,dotdot_ino;
 428         int error,exists,ino;
 429 
 430         if (old_dir->i_dev != new_dir->i_dev) return -EINVAL;
 431         if (old_ino == new_dir->i_ino) return -EINVAL;
 432         if (!(walk = iget(new_dir->i_dev,new_dir->i_ino))) return -EIO;
 433         while (walk->i_ino != MSDOS_ROOT_INO) {
 434                 ino = msdos_parent_ino(walk,1);
 435                 iput(walk);
 436                 if (ino < 0) return ino;
 437                 if (ino == old_ino) return -EINVAL;
 438                 if (!(walk = iget(new_dir->i_dev,ino))) return -EIO;
 439         }
 440         iput(walk);
 441         if ((error = msdos_scan(new_dir,NULL,&free_bh,&free_de,&free_ino)) < 0)
 442             return error;
 443         exists = msdos_scan(new_dir,new_name,&new_bh,&new_de,&new_ino)
 444             >= 0;
 445         if (!(old_inode = iget(old_dir->i_dev,old_ino))) {
 446                 brelse(free_bh);
 447                 if (exists) brelse(new_bh);
 448                 return -EIO;
 449         }
 450         if (*(unsigned char *) old_de->name == DELETED_FLAG) {
 451                 iput(old_inode);
 452                 brelse(free_bh);
 453                 if (exists) brelse(new_bh);
 454                 return -ENOENT;
 455         }
 456         new_inode = NULL; /* to make GCC happy */
 457         if (exists) {
 458                 if (!(new_inode = iget(new_dir->i_dev,new_ino))) {
 459                         iput(old_inode);
 460                         brelse(new_bh);
 461                         return -EIO;
 462                 }
 463                 if (S_ISDIR(new_inode->i_mode)) {
 464                         iput(new_inode);
 465                         iput(old_inode);
 466                         brelse(new_bh);
 467                         return -EPERM;
 468                 }
 469                 new_inode->i_nlink = 0;
 470                 MSDOS_I(new_inode)->i_busy = 1;
 471                 new_inode->i_dirt = 1;
 472                 new_de->name[0] = DELETED_FLAG;
 473                 new_bh->b_dirt = 1;
 474         }
 475         memcpy(free_de,old_de,sizeof(struct msdos_dir_entry));
 476         memcpy(free_de->name,new_name,MSDOS_NAME);
 477         if (!(free_inode = iget(new_dir->i_dev,free_ino))) {
 478                 free_de->name[0] = DELETED_FLAG;
 479 /*  Don't mark free_bh as dirty. Both states are supposed to be equivalent. */
 480                 brelse(free_bh);
 481                 if (exists) {
 482                         iput(new_inode);
 483                         brelse(new_bh);
 484                 }
 485                 return -EIO;
 486         }
 487         msdos_read_inode(free_inode);
 488         MSDOS_I(old_inode)->i_busy = 1;
 489         cache_inval_inode(old_inode);
 490         old_inode->i_dirt = 1;
 491         old_de->name[0] = DELETED_FLAG;
 492         old_bh->b_dirt = 1;
 493         free_bh->b_dirt = 1;
 494         if (!exists) iput(free_inode);
 495         else {
 496                 MSDOS_I(new_inode)->i_depend = free_inode;
 497                 MSDOS_I(free_inode)->i_old = new_inode;
 498                 /* free_inode is put when putting new_inode */
 499                 iput(new_inode);
 500                 brelse(new_bh);
 501         }
 502         if (S_ISDIR(old_inode->i_mode)) {
 503                 if ((error = msdos_scan(old_inode,MSDOS_DOTDOT,&dotdot_bh,
 504                     &dotdot_de,&dotdot_ino)) < 0) goto rename_done;
 505                 if (!(dotdot_inode = iget(old_inode->i_dev,dotdot_ino))) {
 506                         brelse(dotdot_bh);
 507                         error = -EIO;
 508                         goto rename_done;
 509                 }
 510                 dotdot_de->start = MSDOS_I(dotdot_inode)->i_start =
 511                     MSDOS_I(new_dir)->i_start;
 512                 dotdot_inode->i_dirt = 1;
 513                 dotdot_bh->b_dirt = 1;
 514                 iput(dotdot_inode);
 515                 brelse(dotdot_bh);
 516                 old_dir->i_nlink--;
 517                 new_dir->i_nlink++;
 518                 /* no need to mark them dirty */
 519         }
 520         error = 0;
 521 rename_done:
 522         brelse(free_bh);
 523         iput(old_inode);
 524         return error;
 525 }
 526 
 527 
 528 int msdos_rename(struct inode *old_dir,const char *old_name,int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 529         struct inode *new_dir,const char *new_name,int new_len)
 530 {
 531         char old_msdos_name[MSDOS_NAME],new_msdos_name[MSDOS_NAME];
 532         struct buffer_head *old_bh;
 533         struct msdos_dir_entry *old_de;
 534         int old_ino,error;
 535 
 536         if ((error = msdos_format_name(MSDOS_SB(old_dir->i_sb)->name_check,
 537             old_name,old_len,old_msdos_name)) < 0) goto rename_done;
 538         if ((error = msdos_format_name(MSDOS_SB(new_dir->i_sb)->name_check,
 539             new_name,new_len,new_msdos_name)) < 0) goto rename_done;
 540         if ((error = msdos_scan(old_dir,old_msdos_name,&old_bh,&old_de,
 541             &old_ino)) < 0) goto rename_done;
 542         lock_creation();
 543         if (old_dir == new_dir)
 544                 error = rename_same_dir(old_dir,old_msdos_name,new_dir,
 545                     new_msdos_name,old_bh,old_de,old_ino);
 546         else error = rename_diff_dir(old_dir,old_msdos_name,new_dir,
 547                     new_msdos_name,old_bh,old_de,old_ino);
 548         unlock_creation();
 549         brelse(old_bh);
 550 rename_done:
 551         iput(old_dir);
 552         iput(new_dir);
 553         return error;
 554 }

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