root/fs/sysv/namei.c

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

DEFINITIONS

This source file includes following definitions.
  1. namecompare
  2. sysv_match
  3. sysv_find_entry
  4. sysv_lookup
  5. sysv_add_entry
  6. sysv_create
  7. sysv_mknod
  8. sysv_mkdir
  9. empty_dir
  10. sysv_rmdir
  11. sysv_unlink
  12. sysv_symlink
  13. sysv_link
  14. subdir
  15. do_sysv_rename
  16. sysv_rename

   1 /*
   2  *  linux/fs/sysv/namei.c
   3  *
   4  *  minix/namei.c
   5  *  Copyright (C) 1991, 1992  Linus Torvalds
   6  *
   7  *  coh/namei.c
   8  *  Copyright (C) 1993  Pascal Haible, Bruno Haible
   9  *
  10  *  sysv/namei.c
  11  *  Copyright (C) 1993  Bruno Haible
  12  */
  13 
  14 #include <linux/sched.h>
  15 #include <linux/kernel.h>
  16 #include <linux/fs.h>
  17 #include <linux/sysv_fs.h>
  18 #include <linux/string.h>
  19 #include <linux/stat.h>
  20 #include <linux/errno.h>
  21 
  22 /* compare strings: name[0..len-1] (not zero-terminated) and
  23  * buffer[0..] (filled with zeroes up to buffer[0..maxlen-1])
  24  */
  25 static inline int namecompare(int len, int maxlen,
     /* [previous][next][first][last][top][bottom][index][help] */
  26         const char * name, const char * buffer)
  27 {
  28         if (len >= maxlen || !buffer[len]) {
  29                 unsigned char same;
  30                 __asm__("repe ; cmpsb ; setz %0"
  31                         :"=q" (same)
  32                         :"S" ((long) name),"D" ((long) buffer),"c" (len)
  33                         :"cx","di","si");
  34                 return same;
  35         }
  36         /* if (len<maxlen && buffer[len]) then buffer is longer than name */
  37         return 0;
  38 }
  39 
  40 /*
  41  * ok, we cannot use strncmp, as the name is not in our data space. [Now it is!]
  42  * Thus we'll have to use sysv_match. No big problem. Match also makes
  43  * some sanity tests.
  44  *
  45  * NOTE! unlike strncmp, sysv_match returns 1 for success, 0 for failure.
  46  */
  47 static int sysv_match(int len, const char * name, struct sysv_dir_entry * de)
     /* [previous][next][first][last][top][bottom][index][help] */
  48 {
  49         if (!de->inode || len > SYSV_NAMELEN)
  50                 return 0;
  51         /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
  52         if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
  53                 return 1;
  54         return namecompare(len,SYSV_NAMELEN,name,de->name);
  55 }
  56 
  57 /*
  58  *      sysv_find_entry()
  59  *
  60  * finds an entry in the specified directory with the wanted name. It
  61  * returns the cache buffer in which the entry was found, and the entry
  62  * itself (as a parameter - res_dir). It does NOT read the inode of the
  63  * entry - you'll have to do that yourself if you want to.
  64  */
  65 static struct buffer_head * sysv_find_entry(struct inode * dir,
     /* [previous][next][first][last][top][bottom][index][help] */
  66         const char * name, int namelen, struct sysv_dir_entry ** res_dir)
  67 {
  68         struct super_block * sb;
  69         unsigned long pos, block, offset; /* pos = block * block_size + offset */
  70         struct buffer_head * bh;
  71         char * bh_data;
  72 
  73         *res_dir = NULL;
  74         if (!dir)
  75                 return NULL;
  76         sb = dir->i_sb;
  77         if (namelen > SYSV_NAMELEN)
  78                 if (sb->sv_truncate)
  79                         namelen = SYSV_NAMELEN;
  80                 else
  81                         return NULL;
  82         bh = NULL;
  83         pos = block = offset = 0;
  84         while (pos < dir->i_size) {
  85                 if (!bh) {
  86                         bh = sysv_file_bread(dir,block,0,&bh_data);
  87                         if (!bh) {
  88                                 /* offset = 0; */ block++;
  89                                 pos += sb->sv_block_size;
  90                                 continue;
  91                         }
  92                 }
  93                 if (sysv_match(namelen, name,
  94                                *res_dir = (struct sysv_dir_entry *) (bh_data + offset) ))
  95                         return bh;
  96                 pos += SYSV_DIRSIZE;
  97                 offset += SYSV_DIRSIZE;
  98                 if (offset < sb->sv_block_size)
  99                         continue;
 100                 brelse(bh);
 101                 bh = NULL;
 102                 offset = 0; block++;
 103         }
 104         brelse(bh);
 105         *res_dir = NULL;
 106         return NULL;
 107 }
 108 
 109 int sysv_lookup(struct inode * dir,const char * name, int len,
     /* [previous][next][first][last][top][bottom][index][help] */
 110         struct inode ** result)
 111 {
 112         int ino;
 113         struct sysv_dir_entry * de;
 114         struct buffer_head * bh;
 115 
 116         *result = NULL;
 117         if (!dir)
 118                 return -ENOENT;
 119         if (!S_ISDIR(dir->i_mode)) {
 120                 iput(dir);
 121                 return -ENOENT;
 122         }
 123         if (!(bh = sysv_find_entry(dir,name,len,&de))) {
 124                 iput(dir);
 125                 return -ENOENT;
 126         }
 127         ino = de->inode;
 128         brelse(bh);
 129         if (!(*result = iget(dir->i_sb,ino))) {
 130                 iput(dir);
 131                 return -EACCES;
 132         }
 133         iput(dir);
 134         return 0;
 135 }
 136 
 137 /*
 138  *      sysv_add_entry()
 139  *
 140  * adds a file entry to the specified directory, returning a possible
 141  * error value if it fails.
 142  *
 143  * NOTE!! The inode part of 'de' is left at 0 - which means you
 144  * may not sleep between calling this and putting something into
 145  * the entry, as someone else might have used it while you slept.
 146  */
 147 static int sysv_add_entry(struct inode * dir,
     /* [previous][next][first][last][top][bottom][index][help] */
 148         const char * name, int namelen,
 149         struct buffer_head ** res_buf,
 150         struct sysv_dir_entry ** res_dir)
 151 {
 152         struct super_block * sb;
 153         int i;
 154         unsigned long pos, block, offset; /* pos = block * block_size + offset */
 155         struct buffer_head * bh;
 156         char * bh_data;
 157         struct sysv_dir_entry * de;
 158 
 159         *res_buf = NULL;
 160         *res_dir = NULL;
 161         if (!dir)
 162                 return -ENOENT;
 163         sb = dir->i_sb;
 164         if (namelen > SYSV_NAMELEN)
 165                 if (sb->sv_truncate)
 166                         namelen = SYSV_NAMELEN;
 167                 else
 168                         return -ENAMETOOLONG;
 169         if (!namelen)
 170                 return -ENOENT;
 171         bh = NULL;
 172         pos = block = offset = 0;
 173         while (1) {
 174                 if (!bh) {
 175                         bh = sysv_file_bread(dir,block,1,&bh_data);
 176                         if (!bh)
 177                                 return -ENOSPC;
 178                 }
 179                 de = (struct sysv_dir_entry *) (bh_data + offset);
 180                 pos += SYSV_DIRSIZE;
 181                 offset += SYSV_DIRSIZE;
 182                 if (pos > dir->i_size) {
 183                         de->inode = 0;
 184                         dir->i_size = pos;
 185                         dir->i_dirt = 1;
 186                 }
 187                 if (de->inode) {
 188                         if (namecompare(namelen, SYSV_NAMELEN, name, de->name)) {
 189                                 brelse(bh);
 190                                 return -EEXIST;
 191                         }
 192                 } else {
 193                         dir->i_mtime = dir->i_ctime = CURRENT_TIME;
 194                         for (i = 0; i < SYSV_NAMELEN ; i++)
 195                                 de->name[i] = (i < namelen) ? name[i] : 0;
 196                         mark_buffer_dirty(bh, 1);
 197                         *res_dir = de;
 198                         break;
 199                 }
 200                 if (offset < sb->sv_block_size)
 201                         continue;
 202                 brelse(bh);
 203                 bh = NULL;
 204                 offset = 0; block++;
 205         }
 206         *res_buf = bh;
 207         return 0;
 208 }
 209 
 210 int sysv_create(struct inode * dir,const char * name, int len, int mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 211         struct inode ** result)
 212 {
 213         int error;
 214         struct inode * inode;
 215         struct buffer_head * bh;
 216         struct sysv_dir_entry * de;
 217 
 218         *result = NULL;
 219         if (!dir)
 220                 return -ENOENT;
 221         inode = sysv_new_inode(dir);
 222         if (!inode) {
 223                 iput(dir);
 224                 return -ENOSPC;
 225         }
 226         if (inode->i_sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
 227                 inode->i_op = &sysv_file_inode_operations_with_bmap;
 228         else
 229                 inode->i_op = &sysv_file_inode_operations;
 230         inode->i_mode = mode;
 231         inode->i_dirt = 1;
 232         error = sysv_add_entry(dir,name,len, &bh ,&de);
 233         if (error) {
 234                 inode->i_nlink--;
 235                 inode->i_dirt = 1;
 236                 iput(inode);
 237                 iput(dir);
 238                 return error;
 239         }
 240         de->inode = inode->i_ino;
 241         mark_buffer_dirty(bh, 1);
 242         brelse(bh);
 243         iput(dir);
 244         *result = inode;
 245         return 0;
 246 }
 247 
 248 int sysv_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250         int error;
 251         struct inode * inode;
 252         struct buffer_head * bh;
 253         struct sysv_dir_entry * de;
 254 
 255         if (!dir)
 256                 return -ENOENT;
 257         bh = sysv_find_entry(dir,name,len,&de);
 258         if (bh) {
 259                 brelse(bh);
 260                 iput(dir);
 261                 return -EEXIST;
 262         }
 263         inode = sysv_new_inode(dir);
 264         if (!inode) {
 265                 iput(dir);
 266                 return -ENOSPC;
 267         }
 268         inode->i_uid = current->euid;
 269         inode->i_mode = mode;
 270         inode->i_op = NULL;
 271         if (S_ISREG(inode->i_mode))
 272                 if (inode->i_sb->sv_block_size_ratio_bits == 0) /* block_size == BLOCK_SIZE ? */
 273                         inode->i_op = &sysv_file_inode_operations_with_bmap;
 274                 else
 275                         inode->i_op = &sysv_file_inode_operations;
 276         else if (S_ISDIR(inode->i_mode)) {
 277                 inode->i_op = &sysv_dir_inode_operations;
 278                 if (dir->i_mode & S_ISGID)
 279                         inode->i_mode |= S_ISGID;
 280         }
 281         else if (S_ISLNK(inode->i_mode))
 282                 inode->i_op = &sysv_symlink_inode_operations;
 283         else if (S_ISCHR(inode->i_mode))
 284                 inode->i_op = &chrdev_inode_operations;
 285         else if (S_ISBLK(inode->i_mode))
 286                 inode->i_op = &blkdev_inode_operations;
 287         else if (S_ISFIFO(inode->i_mode))
 288                 init_fifo(inode);
 289         if (S_ISBLK(mode) || S_ISCHR(mode))
 290                 inode->i_rdev = rdev;
 291         inode->i_dirt = 1;
 292         error = sysv_add_entry(dir, name, len, &bh, &de);
 293         if (error) {
 294                 inode->i_nlink--;
 295                 inode->i_dirt = 1;
 296                 iput(inode);
 297                 iput(dir);
 298                 return error;
 299         }
 300         de->inode = inode->i_ino;
 301         mark_buffer_dirty(bh, 1);
 302         brelse(bh);
 303         iput(dir);
 304         iput(inode);
 305         return 0;
 306 }
 307 
 308 int sysv_mkdir(struct inode * dir, const char * name, int len, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 309 {
 310         int error;
 311         struct inode * inode;
 312         struct buffer_head * bh, *dir_block;
 313         char * bh_data;
 314         struct sysv_dir_entry * de;
 315 
 316         if (!dir) {
 317                 iput(dir);
 318                 return -EINVAL;
 319         }
 320         bh = sysv_find_entry(dir,name,len,&de);
 321         if (bh) {
 322                 brelse(bh);
 323                 iput(dir);
 324                 return -EEXIST;
 325         }
 326         if (dir->i_nlink >= dir->i_sb->sv_link_max) {
 327                 iput(dir);
 328                 return -EMLINK;
 329         }
 330         inode = sysv_new_inode(dir);
 331         if (!inode) {
 332                 iput(dir);
 333                 return -ENOSPC;
 334         }
 335         inode->i_op = &sysv_dir_inode_operations;
 336         inode->i_size = 2 * SYSV_DIRSIZE;
 337         dir_block = sysv_file_bread(inode,0,1,&bh_data);
 338         if (!dir_block) {
 339                 iput(dir);
 340                 inode->i_nlink--;
 341                 inode->i_dirt = 1;
 342                 iput(inode);
 343                 return -ENOSPC;
 344         }
 345         de = (struct sysv_dir_entry *) (bh_data + 0*SYSV_DIRSIZE);
 346         de->inode = inode->i_ino;
 347         strcpy(de->name,"."); /* rest of de->name is zero, see sysv_new_block */
 348         de = (struct sysv_dir_entry *) (bh_data + 1*SYSV_DIRSIZE);
 349         de->inode = dir->i_ino;
 350         strcpy(de->name,".."); /* rest of de->name is zero, see sysv_new_block */
 351         inode->i_nlink = 2;
 352         mark_buffer_dirty(dir_block, 1);
 353         brelse(dir_block);
 354         inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
 355         if (dir->i_mode & S_ISGID)
 356                 inode->i_mode |= S_ISGID;
 357         inode->i_dirt = 1;
 358         error = sysv_add_entry(dir, name, len, &bh, &de);
 359         if (error) {
 360                 iput(dir);
 361                 inode->i_nlink=0;
 362                 iput(inode);
 363                 return error;
 364         }
 365         de->inode = inode->i_ino;
 366         mark_buffer_dirty(bh, 1);
 367         dir->i_nlink++;
 368         dir->i_dirt = 1;
 369         iput(dir);
 370         iput(inode);
 371         brelse(bh);
 372         return 0;
 373 }
 374 
 375 /*
 376  * routine to check that the specified directory is empty (for rmdir)
 377  */
 378 static int empty_dir(struct inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 379 {
 380         struct super_block * sb;
 381         unsigned long pos, block, offset; /* pos = block * block_size + offset */
 382         struct buffer_head * bh;
 383         char * bh_data;
 384         struct sysv_dir_entry * de;
 385 
 386         if (!inode)
 387                 return 1;
 388         block = 0;
 389         bh = NULL;
 390         pos = offset = 2*SYSV_DIRSIZE;
 391         if (inode->i_size % SYSV_DIRSIZE)
 392                 goto bad_dir;
 393         if (inode->i_size < pos)
 394                 goto bad_dir;
 395         bh = sysv_file_bread(inode,0,0,&bh_data);
 396         if (!bh)
 397                 goto bad_dir;
 398         de = (struct sysv_dir_entry *) (bh_data + 0*SYSV_DIRSIZE);
 399         if (!de->inode || strcmp(de->name,"."))
 400                 goto bad_dir;
 401         de = (struct sysv_dir_entry *) (bh_data + 1*SYSV_DIRSIZE);
 402         if (!de->inode || strcmp(de->name,".."))
 403                 goto bad_dir;
 404         sb = inode->i_sb;
 405         while (pos < inode->i_size) {
 406                 if (!bh) {
 407                         bh = sysv_file_bread(inode,block,0,&bh_data);
 408                         if (!bh) {
 409                                 /* offset = 0; */ block++;
 410                                 pos += sb->sv_block_size;
 411                                 continue;
 412                         }
 413                 }
 414                 de = (struct sysv_dir_entry *) (bh_data + offset);
 415                 pos += SYSV_DIRSIZE;
 416                 offset += SYSV_DIRSIZE;
 417                 if (de->inode) {
 418                         brelse(bh);
 419                         return 0;
 420                 }
 421                 if (offset < sb->sv_block_size)
 422                         continue;
 423                 brelse(bh);
 424                 bh = NULL;
 425                 offset = 0; block++;
 426         }
 427         brelse(bh);
 428         return 1;
 429 bad_dir:
 430         brelse(bh);
 431         printk("Bad directory on device %04x\n",inode->i_dev);
 432         return 1;
 433 }
 434 
 435 int sysv_rmdir(struct inode * dir, const char * name, int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437         int retval;
 438         struct inode * inode;
 439         struct buffer_head * bh;
 440         struct sysv_dir_entry * de;
 441 
 442         inode = NULL;
 443         bh = sysv_find_entry(dir,name,len,&de);
 444         retval = -ENOENT;
 445         if (!bh)
 446                 goto end_rmdir;
 447         retval = -EPERM;
 448         if (!(inode = iget(dir->i_sb, de->inode)))
 449                 goto end_rmdir;
 450         if ((dir->i_mode & S_ISVTX) && !suser() &&
 451             current->euid != inode->i_uid &&
 452             current->euid != dir->i_uid)
 453                 goto end_rmdir;
 454         if (inode->i_dev != dir->i_dev)
 455                 goto end_rmdir;
 456         if (inode == dir)       /* we may not delete ".", but "../dir" is ok */
 457                 goto end_rmdir;
 458         if (!S_ISDIR(inode->i_mode)) {
 459                 retval = -ENOTDIR;
 460                 goto end_rmdir;
 461         }
 462         if (!empty_dir(inode)) {
 463                 retval = -ENOTEMPTY;
 464                 goto end_rmdir;
 465         }
 466         if (de->inode != inode->i_ino) {
 467                 retval = -ENOENT;
 468                 goto end_rmdir;
 469         }
 470         if (inode->i_count > 1) {
 471                 retval = -EBUSY;
 472                 goto end_rmdir;
 473         }
 474         if (inode->i_nlink != 2)
 475                 printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
 476         de->inode = 0;
 477         mark_buffer_dirty(bh, 1);
 478         inode->i_nlink=0;
 479         inode->i_dirt=1;
 480         dir->i_nlink--;
 481         inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 482         dir->i_dirt=1;
 483         retval = 0;
 484 end_rmdir:
 485         iput(dir);
 486         iput(inode);
 487         brelse(bh);
 488         return retval;
 489 }
 490 
 491 int sysv_unlink(struct inode * dir, const char * name, int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 492 {
 493         int retval;
 494         struct inode * inode;
 495         struct buffer_head * bh;
 496         struct sysv_dir_entry * de;
 497 
 498 repeat:
 499         retval = -ENOENT;
 500         inode = NULL;
 501         bh = sysv_find_entry(dir,name,len,&de);
 502         if (!bh)
 503                 goto end_unlink;
 504         if (!(inode = iget(dir->i_sb, de->inode)))
 505                 goto end_unlink;
 506         retval = -EPERM;
 507         if (S_ISDIR(inode->i_mode))
 508                 goto end_unlink;
 509         if (de->inode != inode->i_ino) {
 510                 iput(inode);
 511                 brelse(bh);
 512                 current->counter = 0;
 513                 schedule();
 514                 goto repeat;
 515         }
 516         if ((dir->i_mode & S_ISVTX) && !suser() &&
 517             current->euid != inode->i_uid &&
 518             current->euid != dir->i_uid)
 519                 goto end_unlink;
 520         if (de->inode != inode->i_ino) {
 521                 retval = -ENOENT;
 522                 goto end_unlink;
 523         }
 524         if (!inode->i_nlink) {
 525                 printk("Deleting nonexistent file (%04x:%lu), %d\n",
 526                         inode->i_dev,inode->i_ino,inode->i_nlink);
 527                 inode->i_nlink=1;
 528         }
 529         de->inode = 0;
 530         mark_buffer_dirty(bh, 1);
 531         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 532         dir->i_dirt = 1;
 533         inode->i_nlink--;
 534         inode->i_ctime = dir->i_ctime;
 535         inode->i_dirt = 1;
 536         retval = 0;
 537 end_unlink:
 538         brelse(bh);
 539         iput(inode);
 540         iput(dir);
 541         return retval;
 542 }
 543 
 544 int sysv_symlink(struct inode * dir, const char * name, int len, const char * symname)
     /* [previous][next][first][last][top][bottom][index][help] */
 545 {
 546         struct sysv_dir_entry * de;
 547         struct inode * inode;
 548         struct buffer_head * name_block;
 549         char * name_block_data;
 550         struct super_block * sb;
 551         int i;
 552         char c;
 553         struct buffer_head * bh;
 554 
 555         if (!(inode = sysv_new_inode(dir))) {
 556                 iput(dir);
 557                 return -ENOSPC;
 558         }
 559         inode->i_mode = S_IFLNK | 0777;
 560         inode->i_op = &sysv_symlink_inode_operations;
 561         name_block = sysv_file_bread(inode,0,1,&name_block_data);
 562         if (!name_block) {
 563                 iput(dir);
 564                 inode->i_nlink--;
 565                 inode->i_dirt = 1;
 566                 iput(inode);
 567                 return -ENOSPC;
 568         }
 569         sb = inode->i_sb;
 570         i = 0;
 571         while (i < sb->sv_block_size_1 && (c = *(symname++)))
 572                 name_block_data[i++] = c;
 573         name_block_data[i] = 0;
 574         mark_buffer_dirty(name_block, 1);
 575         brelse(name_block);
 576         inode->i_size = i;
 577         inode->i_dirt = 1;
 578         bh = sysv_find_entry(dir,name,len,&de);
 579         if (bh) {
 580                 inode->i_nlink--;
 581                 inode->i_dirt = 1;
 582                 iput(inode);
 583                 brelse(bh);
 584                 iput(dir);
 585                 return -EEXIST;
 586         }
 587         i = sysv_add_entry(dir, name, len, &bh, &de);
 588         if (i) {
 589                 inode->i_nlink--;
 590                 inode->i_dirt = 1;
 591                 iput(inode);
 592                 iput(dir);
 593                 return i;
 594         }
 595         de->inode = inode->i_ino;
 596         mark_buffer_dirty(bh, 1);
 597         brelse(bh);
 598         iput(dir);
 599         iput(inode);
 600         return 0;
 601 }
 602 
 603 int sysv_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 604 {
 605         int error;
 606         struct sysv_dir_entry * de;
 607         struct buffer_head * bh;
 608 
 609         if (S_ISDIR(oldinode->i_mode)) {
 610                 iput(oldinode);
 611                 iput(dir);
 612                 return -EPERM;
 613         }
 614         if (oldinode->i_nlink >= oldinode->i_sb->sv_link_max) {
 615                 iput(oldinode);
 616                 iput(dir);
 617                 return -EMLINK;
 618         }
 619         bh = sysv_find_entry(dir,name,len,&de);
 620         if (bh) {
 621                 brelse(bh);
 622                 iput(dir);
 623                 iput(oldinode);
 624                 return -EEXIST;
 625         }
 626         error = sysv_add_entry(dir, name, len, &bh, &de);
 627         if (error) {
 628                 iput(dir);
 629                 iput(oldinode);
 630                 return error;
 631         }
 632         de->inode = oldinode->i_ino;
 633         mark_buffer_dirty(bh, 1);
 634         brelse(bh);
 635         iput(dir);
 636         oldinode->i_nlink++;
 637         oldinode->i_ctime = CURRENT_TIME;
 638         oldinode->i_dirt = 1;
 639         iput(oldinode);
 640         return 0;
 641 }
 642 
 643 /* return 1 if `new' is a subdir of `old' on the same device */
 644 static int subdir(struct inode * new_inode, struct inode * old_inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 645 {
 646         int ino;
 647         int result;
 648 
 649         new_inode->i_count++;
 650         result = 0;
 651         for (;;) {
 652                 if (new_inode == old_inode) {
 653                         result = 1;
 654                         break;
 655                 }
 656                 if (new_inode->i_dev != old_inode->i_dev)
 657                         break;
 658                 ino = new_inode->i_ino;
 659                 if (sysv_lookup(new_inode,"..",2,&new_inode))
 660                         break;
 661                 if (new_inode->i_ino == ino) /* root dir reached ? */
 662                         break;
 663         }
 664         iput(new_inode);
 665         return result;
 666 }
 667 
 668 #define PARENT_INO(buffer) \
 669 (((struct sysv_dir_entry *) ((buffer) + 1*SYSV_DIRSIZE))->inode)
 670 
 671 /*
 672  * rename uses retrying to avoid race-conditions: at least they should be minimal.
 673  * it tries to allocate all the blocks, then sanity-checks, and if the sanity-
 674  * checks fail, it tries to restart itself again. Very practical - no changes
 675  * are done until we know everything works ok.. and then all the changes can be
 676  * done in one fell swoop when we have claimed all the buffers needed.
 677  *
 678  * Anybody can rename anything with this: the permission checks are left to the
 679  * higher-level routines.
 680  */
 681 static int do_sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 682         struct inode * new_dir, const char * new_name, int new_len)
 683 {
 684         struct inode * old_inode, * new_inode;
 685         struct buffer_head * old_bh, * new_bh, * dir_bh;
 686         char * dir_bh_data;
 687         struct sysv_dir_entry * old_de, * new_de;
 688         int retval;
 689 
 690         goto start_up;
 691 try_again:
 692         brelse(old_bh);
 693         brelse(new_bh);
 694         brelse(dir_bh);
 695         iput(old_inode);
 696         iput(new_inode);
 697         current->counter = 0;
 698         schedule();
 699 start_up:
 700         old_inode = new_inode = NULL;
 701         old_bh = new_bh = dir_bh = NULL;
 702         old_bh = sysv_find_entry(old_dir,old_name,old_len,&old_de);
 703         retval = -ENOENT;
 704         if (!old_bh)
 705                 goto end_rename;
 706         old_inode = __iget(old_dir->i_sb, old_de->inode, 0); /* don't cross mnt-points */
 707         if (!old_inode)
 708                 goto end_rename;
 709         retval = -EPERM;
 710         if ((old_dir->i_mode & S_ISVTX) && 
 711             current->euid != old_inode->i_uid &&
 712             current->euid != old_dir->i_uid && !suser())
 713                 goto end_rename;
 714         new_bh = sysv_find_entry(new_dir,new_name,new_len,&new_de);
 715         if (new_bh) {
 716                 new_inode = __iget(new_dir->i_sb, new_de->inode, 0);
 717                 if (!new_inode) {
 718                         brelse(new_bh);
 719                         new_bh = NULL;
 720                 }
 721         }
 722         if (new_inode == old_inode) {
 723                 retval = 0;
 724                 goto end_rename;
 725         }
 726         if (new_inode && S_ISDIR(new_inode->i_mode)) {
 727                 retval = -EISDIR;
 728                 if (!S_ISDIR(old_inode->i_mode))
 729                         goto end_rename;
 730                 retval = -EINVAL;
 731                 if (subdir(new_dir, old_inode))
 732                         goto end_rename;
 733                 retval = -ENOTEMPTY;
 734                 if (!empty_dir(new_inode))
 735                         goto end_rename;
 736                 retval = -EBUSY;
 737                 if (new_inode->i_count > 1)
 738                         goto end_rename;
 739         }
 740         retval = -EPERM;
 741         if (new_inode && (new_dir->i_mode & S_ISVTX) && 
 742             current->euid != new_inode->i_uid &&
 743             current->euid != new_dir->i_uid && !suser())
 744                 goto end_rename;
 745         if (S_ISDIR(old_inode->i_mode)) {
 746                 retval = -ENOTDIR;
 747                 if (new_inode && !S_ISDIR(new_inode->i_mode))
 748                         goto end_rename;
 749                 retval = -EINVAL;
 750                 if (subdir(new_dir, old_inode))
 751                         goto end_rename;
 752                 retval = -EIO;
 753                 dir_bh = sysv_file_bread(old_inode,0,0,&dir_bh_data);
 754                 if (!dir_bh)
 755                         goto end_rename;
 756                 if (PARENT_INO(dir_bh_data) != old_dir->i_ino)
 757                         goto end_rename;
 758                 retval = -EMLINK;
 759                 if (!new_inode && new_dir->i_nlink >= new_dir->i_sb->sv_link_max)
 760                         goto end_rename;
 761         }
 762         if (!new_bh) {
 763                 retval = sysv_add_entry(new_dir,new_name,new_len,&new_bh,&new_de);
 764                 if (retval)
 765                         goto end_rename;
 766         }
 767 /* sanity checking before doing the rename - avoid races */
 768         if (new_inode && (new_de->inode != new_inode->i_ino))
 769                 goto try_again;
 770         if (new_de->inode && !new_inode)
 771                 goto try_again;
 772         if (old_de->inode != old_inode->i_ino)
 773                 goto try_again;
 774 /* ok, that's it */
 775         old_de->inode = 0;
 776         new_de->inode = old_inode->i_ino;
 777         old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
 778         old_dir->i_dirt = 1;
 779         new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME;
 780         new_dir->i_dirt = 1;
 781         if (new_inode) {
 782                 new_inode->i_nlink--;
 783                 new_inode->i_ctime = CURRENT_TIME;
 784                 new_inode->i_dirt = 1;
 785         }
 786         mark_buffer_dirty(old_bh, 1);
 787         mark_buffer_dirty(new_bh, 1);
 788         if (dir_bh) {
 789                 PARENT_INO(dir_bh_data) = new_dir->i_ino;
 790                 mark_buffer_dirty(dir_bh, 1);
 791                 old_dir->i_nlink--;
 792                 old_dir->i_dirt = 1;
 793                 if (new_inode) {
 794                         new_inode->i_nlink--;
 795                         new_inode->i_dirt = 1;
 796                 } else {
 797                         new_dir->i_nlink++;
 798                         new_dir->i_dirt = 1;
 799                 }
 800         }
 801         retval = 0;
 802 end_rename:
 803         brelse(dir_bh);
 804         brelse(old_bh);
 805         brelse(new_bh);
 806         iput(old_inode);
 807         iput(new_inode);
 808         iput(old_dir);
 809         iput(new_dir);
 810         return retval;
 811 }
 812 
 813 /*
 814  * Ok, rename also locks out other renames, as they can change the parent of
 815  * a directory, and we don't want any races. Other races are checked for by
 816  * "do_rename()", which restarts if there are inconsistencies.
 817  *
 818  * Note that there is no race between different filesystems: it's only within
 819  * the same device that races occur: many renames can happen at once, as long
 820  * as they are on different partitions.
 821  */
 822 int sysv_rename(struct inode * old_dir, const char * old_name, int old_len,
     /* [previous][next][first][last][top][bottom][index][help] */
 823         struct inode * new_dir, const char * new_name, int new_len)
 824 {
 825         static struct wait_queue * wait = NULL;
 826         static int lock = 0;
 827         int result;
 828 
 829         while (lock)
 830                 sleep_on(&wait);
 831         lock = 1;
 832         result = do_sysv_rename(old_dir, old_name, old_len,
 833                 new_dir, new_name, new_len);
 834         lock = 0;
 835         wake_up(&wait);
 836         return result;
 837 }

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