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

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