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

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