root/fs/namei.c

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

DEFINITIONS

This source file includes following definitions.
  1. permission
  2. match
  3. find_entry
  4. add_entry
  5. get_dir
  6. dir_namei
  7. namei
  8. open_namei
  9. sys_mknod
  10. sys_mkdir
  11. empty_dir
  12. sys_rmdir
  13. sys_unlink
  14. sys_link

   1 /*
   2  *  linux/fs/namei.c
   3  *
   4  *  (C) 1991  Linus Torvalds
   5  */
   6 
   7 /*
   8  * Some corrections by tytso.
   9  */
  10 
  11 #include <linux/sched.h>
  12 #include <linux/kernel.h>
  13 #include <asm/segment.h>
  14 
  15 #include <string.h>
  16 #include <fcntl.h>
  17 #include <errno.h>
  18 #include <const.h>
  19 #include <sys/stat.h>
  20 
  21 #define ACC_MODE(x) ("\004\002\006\377"[(x)&O_ACCMODE])
  22 
  23 /*
  24  * comment out this line if you want names > NAME_LEN chars to be
  25  * truncated. Else they will be disallowed.
  26  */
  27 /* #define NO_TRUNCATE */
  28 
  29 #define MAY_EXEC 1
  30 #define MAY_WRITE 2
  31 #define MAY_READ 4
  32 
  33 /*
  34  *      permission()
  35  *
  36  * is used to check for read/write/execute permissions on a file.
  37  * I don't know if we should look at just the euid or both euid and
  38  * uid, but that should be easily changed.
  39  */
  40 static int permission(struct m_inode * inode,int mask)
     /* [previous][next][first][last][top][bottom][index][help] */
  41 {
  42         int mode = inode->i_mode;
  43 
  44 /* special case: not even root can read/write a deleted file */
  45         if (inode->i_dev && !inode->i_nlinks)
  46                 return 0;
  47         else if (current->euid==inode->i_uid)
  48                 mode >>= 6;
  49         else if (current->egid==inode->i_gid)
  50                 mode >>= 3;
  51         if (((mode & mask & 0007) == mask) || suser())
  52                 return 1;
  53         return 0;
  54 }
  55 
  56 /*
  57  * ok, we cannot use strncmp, as the name is not in our data space.
  58  * Thus we'll have to use match. No big problem. Match also makes
  59  * some sanity tests.
  60  *
  61  * NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
  62  */
  63 static int match(int len,const char * name,struct dir_entry * de)
     /* [previous][next][first][last][top][bottom][index][help] */
  64 {
  65         register int same __asm__("ax");
  66 
  67         if (!de || !de->inode || len > NAME_LEN)
  68                 return 0;
  69         if (len < NAME_LEN && de->name[len])
  70                 return 0;
  71         __asm__("cld\n\t"
  72                 "fs ; repe ; cmpsb\n\t"
  73                 "setz %%al"
  74                 :"=a" (same)
  75                 :"0" (0),"S" ((long) name),"D" ((long) de->name),"c" (len)
  76                 :"cx","di","si");
  77         return same;
  78 }
  79 
  80 /*
  81  *      find_entry()
  82  *
  83  * finds an entry in the specified directory with the wanted name. It
  84  * returns the cache buffer in which the entry was found, and the entry
  85  * itself (as a parameter - res_dir). It does NOT read the inode of the
  86  * entry - you'll have to do that yourself if you want to.
  87  *
  88  * This also takes care of the few special cases due to '..'-traversal
  89  * over a pseudo-root and a mount point.
  90  */
  91 static struct buffer_head * find_entry(struct m_inode ** dir,
     /* [previous][next][first][last][top][bottom][index][help] */
  92         const char * name, int namelen, struct dir_entry ** res_dir)
  93 {
  94         int entries;
  95         int block,i;
  96         struct buffer_head * bh;
  97         struct dir_entry * de;
  98         struct super_block * sb;
  99 
 100 #ifdef NO_TRUNCATE
 101         if (namelen > NAME_LEN)
 102                 return NULL;
 103 #else
 104         if (namelen > NAME_LEN)
 105                 namelen = NAME_LEN;
 106 #endif
 107         entries = (*dir)->i_size / (sizeof (struct dir_entry));
 108         *res_dir = NULL;
 109         if (!namelen)
 110                 return NULL;
 111 /* check for '..', as we might have to do some "magic" for it */
 112         if (namelen==2 && get_fs_byte(name)=='.' && get_fs_byte(name+1)=='.') {
 113 /* '..' in a pseudo-root results in a faked '.' (just change namelen) */
 114                 if ((*dir) == current->root)
 115                         namelen=1;
 116                 else if ((*dir)->i_num == ROOT_INO) {
 117 /* '..' over a mount-point results in 'dir' being exchanged for the mounted
 118    directory-inode. NOTE! We set mounted, so that we can iput the new dir */
 119                         sb=get_super((*dir)->i_dev);
 120                         if (sb->s_imount) {
 121                                 iput(*dir);
 122                                 (*dir)=sb->s_imount;
 123                                 (*dir)->i_count++;
 124                         }
 125                 }
 126         }
 127         if (!(block = (*dir)->i_zone[0]))
 128                 return NULL;
 129         if (!(bh = bread((*dir)->i_dev,block)))
 130                 return NULL;
 131         i = 0;
 132         de = (struct dir_entry *) bh->b_data;
 133         while (i < entries) {
 134                 if ((char *)de >= BLOCK_SIZE+bh->b_data) {
 135                         brelse(bh);
 136                         bh = NULL;
 137                         if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
 138                             !(bh = bread((*dir)->i_dev,block))) {
 139                                 i += DIR_ENTRIES_PER_BLOCK;
 140                                 continue;
 141                         }
 142                         de = (struct dir_entry *) bh->b_data;
 143                 }
 144                 if (match(namelen,name,de)) {
 145                         *res_dir = de;
 146                         return bh;
 147                 }
 148                 de++;
 149                 i++;
 150         }
 151         brelse(bh);
 152         return NULL;
 153 }
 154 
 155 /*
 156  *      add_entry()
 157  *
 158  * adds a file entry to the specified directory, using the same
 159  * semantics as find_entry(). It returns NULL if it failed.
 160  *
 161  * NOTE!! The inode part of 'de' is left at 0 - which means you
 162  * may not sleep between calling this and putting something into
 163  * the entry, as someone else might have used it while you slept.
 164  */
 165 static struct buffer_head * add_entry(struct m_inode * dir,
     /* [previous][next][first][last][top][bottom][index][help] */
 166         const char * name, int namelen, struct dir_entry ** res_dir)
 167 {
 168         int block,i;
 169         struct buffer_head * bh;
 170         struct dir_entry * de;
 171 
 172         *res_dir = NULL;
 173 #ifdef NO_TRUNCATE
 174         if (namelen > NAME_LEN)
 175                 return NULL;
 176 #else
 177         if (namelen > NAME_LEN)
 178                 namelen = NAME_LEN;
 179 #endif
 180         if (!namelen)
 181                 return NULL;
 182         if (!(block = dir->i_zone[0]))
 183                 return NULL;
 184         if (!(bh = bread(dir->i_dev,block)))
 185                 return NULL;
 186         i = 0;
 187         de = (struct dir_entry *) bh->b_data;
 188         while (1) {
 189                 if ((char *)de >= BLOCK_SIZE+bh->b_data) {
 190                         brelse(bh);
 191                         bh = NULL;
 192                         block = create_block(dir,i/DIR_ENTRIES_PER_BLOCK);
 193                         if (!block)
 194                                 return NULL;
 195                         if (!(bh = bread(dir->i_dev,block))) {
 196                                 i += DIR_ENTRIES_PER_BLOCK;
 197                                 continue;
 198                         }
 199                         de = (struct dir_entry *) bh->b_data;
 200                 }
 201                 if (i*sizeof(struct dir_entry) >= dir->i_size) {
 202                         de->inode=0;
 203                         dir->i_size = (i+1)*sizeof(struct dir_entry);
 204                         dir->i_dirt = 1;
 205                         dir->i_ctime = CURRENT_TIME;
 206                 }
 207                 if (!de->inode) {
 208                         dir->i_mtime = CURRENT_TIME;
 209                         for (i=0; i < NAME_LEN ; i++)
 210                                 de->name[i]=(i<namelen)?get_fs_byte(name+i):0;
 211                         bh->b_dirt = 1;
 212                         *res_dir = de;
 213                         return bh;
 214                 }
 215                 de++;
 216                 i++;
 217         }
 218         brelse(bh);
 219         return NULL;
 220 }
 221 
 222 /*
 223  *      get_dir()
 224  *
 225  * Getdir traverses the pathname until it hits the topmost directory.
 226  * It returns NULL on failure.
 227  */
 228 static struct m_inode * get_dir(const char * pathname)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230         char c;
 231         const char * thisname;
 232         struct m_inode * inode;
 233         struct buffer_head * bh;
 234         int namelen,inr,idev;
 235         struct dir_entry * de;
 236 
 237         if (!current->root || !current->root->i_count)
 238                 panic("No root inode");
 239         if (!current->pwd || !current->pwd->i_count)
 240                 panic("No cwd inode");
 241         if ((c=get_fs_byte(pathname))=='/') {
 242                 inode = current->root;
 243                 pathname++;
 244         } else if (c)
 245                 inode = current->pwd;
 246         else
 247                 return NULL;    /* empty name is bad */
 248         inode->i_count++;
 249         while (1) {
 250                 thisname = pathname;
 251                 if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) {
 252                         iput(inode);
 253                         return NULL;
 254                 }
 255                 for(namelen=0;(c=get_fs_byte(pathname++))&&(c!='/');namelen++)
 256                         /* nothing */ ;
 257                 if (!c)
 258                         return inode;
 259                 if (!(bh = find_entry(&inode,thisname,namelen,&de))) {
 260                         iput(inode);
 261                         return NULL;
 262                 }
 263                 inr = de->inode;
 264                 idev = inode->i_dev;
 265                 brelse(bh);
 266                 iput(inode);
 267                 if (!(inode = iget(idev,inr)))
 268                         return NULL;
 269         }
 270 }
 271 
 272 /*
 273  *      dir_namei()
 274  *
 275  * dir_namei() returns the inode of the directory of the
 276  * specified name, and the name within that directory.
 277  */
 278 static struct m_inode * dir_namei(const char * pathname,
     /* [previous][next][first][last][top][bottom][index][help] */
 279         int * namelen, const char ** name)
 280 {
 281         char c;
 282         const char * basename;
 283         struct m_inode * dir;
 284 
 285         if (!(dir = get_dir(pathname)))
 286                 return NULL;
 287         basename = pathname;
 288         while (c=get_fs_byte(pathname++))
 289                 if (c=='/')
 290                         basename=pathname;
 291         *namelen = pathname-basename-1;
 292         *name = basename;
 293         return dir;
 294 }
 295 
 296 /*
 297  *      namei()
 298  *
 299  * is used by most simple commands to get the inode of a specified name.
 300  * Open, link etc use their own routines, but this is enough for things
 301  * like 'chmod' etc.
 302  */
 303 struct m_inode * namei(const char * pathname)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305         const char * basename;
 306         int inr,dev,namelen;
 307         struct m_inode * dir;
 308         struct buffer_head * bh;
 309         struct dir_entry * de;
 310 
 311         if (!(dir = dir_namei(pathname,&namelen,&basename)))
 312                 return NULL;
 313         if (!namelen)                   /* special case: '/usr/' etc */
 314                 return dir;
 315         bh = find_entry(&dir,basename,namelen,&de);
 316         if (!bh) {
 317                 iput(dir);
 318                 return NULL;
 319         }
 320         inr = de->inode;
 321         dev = dir->i_dev;
 322         brelse(bh);
 323         iput(dir);
 324         dir=iget(dev,inr);
 325         if (dir) {
 326                 dir->i_atime=CURRENT_TIME;
 327                 dir->i_dirt=1;
 328         }
 329         return dir;
 330 }
 331 
 332 /*
 333  *      open_namei()
 334  *
 335  * namei for open - this is in fact almost the whole open-routine.
 336  */
 337 int open_namei(const char * pathname, int flag, int mode,
     /* [previous][next][first][last][top][bottom][index][help] */
 338         struct m_inode ** res_inode)
 339 {
 340         const char * basename;
 341         int inr,dev,namelen;
 342         struct m_inode * dir, *inode;
 343         struct buffer_head * bh;
 344         struct dir_entry * de;
 345 
 346         if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
 347                 flag |= O_WRONLY;
 348         mode &= 0777 & ~current->umask;
 349         mode |= I_REGULAR;
 350         if (!(dir = dir_namei(pathname,&namelen,&basename)))
 351                 return -ENOENT;
 352         if (!namelen) {                 /* special case: '/usr/' etc */
 353                 if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) {
 354                         *res_inode=dir;
 355                         return 0;
 356                 }
 357                 iput(dir);
 358                 return -EISDIR;
 359         }
 360         bh = find_entry(&dir,basename,namelen,&de);
 361         if (!bh) {
 362                 if (!(flag & O_CREAT)) {
 363                         iput(dir);
 364                         return -ENOENT;
 365                 }
 366                 if (!permission(dir,MAY_WRITE)) {
 367                         iput(dir);
 368                         return -EACCES;
 369                 }
 370                 inode = new_inode(dir->i_dev);
 371                 if (!inode) {
 372                         iput(dir);
 373                         return -ENOSPC;
 374                 }
 375                 inode->i_uid = current->euid;
 376                 inode->i_mode = mode;
 377                 inode->i_dirt = 1;
 378                 bh = add_entry(dir,basename,namelen,&de);
 379                 if (!bh) {
 380                         inode->i_nlinks--;
 381                         iput(inode);
 382                         iput(dir);
 383                         return -ENOSPC;
 384                 }
 385                 de->inode = inode->i_num;
 386                 bh->b_dirt = 1;
 387                 brelse(bh);
 388                 iput(dir);
 389                 *res_inode = inode;
 390                 return 0;
 391         }
 392         inr = de->inode;
 393         dev = dir->i_dev;
 394         brelse(bh);
 395         iput(dir);
 396         if (flag & O_EXCL)
 397                 return -EEXIST;
 398         if (!(inode=iget(dev,inr)))
 399                 return -EACCES;
 400         if ((S_ISDIR(inode->i_mode) && (flag & O_ACCMODE)) ||
 401             !permission(inode,ACC_MODE(flag))) {
 402                 iput(inode);
 403                 return -EPERM;
 404         }
 405         inode->i_atime = CURRENT_TIME;
 406         if (flag & O_TRUNC)
 407                 truncate(inode);
 408         *res_inode = inode;
 409         return 0;
 410 }
 411 
 412 int sys_mknod(const char * filename, int mode, int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 413 {
 414         const char * basename;
 415         int namelen;
 416         struct m_inode * dir, * inode;
 417         struct buffer_head * bh;
 418         struct dir_entry * de;
 419         
 420         if (!suser())
 421                 return -EPERM;
 422         if (!(dir = dir_namei(filename,&namelen,&basename)))
 423                 return -ENOENT;
 424         if (!namelen) {
 425                 iput(dir);
 426                 return -ENOENT;
 427         }
 428         if (!permission(dir,MAY_WRITE)) {
 429                 iput(dir);
 430                 return -EPERM;
 431         }
 432         bh = find_entry(&dir,basename,namelen,&de);
 433         if (bh) {
 434                 brelse(bh);
 435                 iput(dir);
 436                 return -EEXIST;
 437         }
 438         inode = new_inode(dir->i_dev);
 439         if (!inode) {
 440                 iput(dir);
 441                 return -ENOSPC;
 442         }
 443         inode->i_mode = mode;
 444         if (S_ISBLK(mode) || S_ISCHR(mode))
 445                 inode->i_zone[0] = dev;
 446         inode->i_mtime = inode->i_atime = CURRENT_TIME;
 447         inode->i_dirt = 1;
 448         bh = add_entry(dir,basename,namelen,&de);
 449         if (!bh) {
 450                 iput(dir);
 451                 inode->i_nlinks=0;
 452                 iput(inode);
 453                 return -ENOSPC;
 454         }
 455         de->inode = inode->i_num;
 456         bh->b_dirt = 1;
 457         iput(dir);
 458         iput(inode);
 459         brelse(bh);
 460         return 0;
 461 }
 462 
 463 int sys_mkdir(const char * pathname, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 464 {
 465         const char * basename;
 466         int namelen;
 467         struct m_inode * dir, * inode;
 468         struct buffer_head * bh, *dir_block;
 469         struct dir_entry * de;
 470 
 471         if (!suser())
 472                 return -EPERM;
 473         if (!(dir = dir_namei(pathname,&namelen,&basename)))
 474                 return -ENOENT;
 475         if (!namelen) {
 476                 iput(dir);
 477                 return -ENOENT;
 478         }
 479         if (!permission(dir,MAY_WRITE)) {
 480                 iput(dir);
 481                 return -EPERM;
 482         }
 483         bh = find_entry(&dir,basename,namelen,&de);
 484         if (bh) {
 485                 brelse(bh);
 486                 iput(dir);
 487                 return -EEXIST;
 488         }
 489         inode = new_inode(dir->i_dev);
 490         if (!inode) {
 491                 iput(dir);
 492                 return -ENOSPC;
 493         }
 494         inode->i_size = 32;
 495         inode->i_dirt = 1;
 496         inode->i_mtime = inode->i_atime = CURRENT_TIME;
 497         if (!(inode->i_zone[0]=new_block(inode->i_dev))) {
 498                 iput(dir);
 499                 inode->i_nlinks--;
 500                 iput(inode);
 501                 return -ENOSPC;
 502         }
 503         inode->i_dirt = 1;
 504         if (!(dir_block=bread(inode->i_dev,inode->i_zone[0]))) {
 505                 iput(dir);
 506                 free_block(inode->i_dev,inode->i_zone[0]);
 507                 inode->i_nlinks--;
 508                 iput(inode);
 509                 return -ERROR;
 510         }
 511         de = (struct dir_entry *) dir_block->b_data;
 512         de->inode=inode->i_num;
 513         strcpy(de->name,".");
 514         de++;
 515         de->inode = dir->i_num;
 516         strcpy(de->name,"..");
 517         inode->i_nlinks = 2;
 518         dir_block->b_dirt = 1;
 519         brelse(dir_block);
 520         inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
 521         inode->i_dirt = 1;
 522         bh = add_entry(dir,basename,namelen,&de);
 523         if (!bh) {
 524                 iput(dir);
 525                 free_block(inode->i_dev,inode->i_zone[0]);
 526                 inode->i_nlinks=0;
 527                 iput(inode);
 528                 return -ENOSPC;
 529         }
 530         de->inode = inode->i_num;
 531         bh->b_dirt = 1;
 532         dir->i_nlinks++;
 533         dir->i_dirt = 1;
 534         iput(dir);
 535         iput(inode);
 536         brelse(bh);
 537         return 0;
 538 }
 539 
 540 /*
 541  * routine to check that the specified directory is empty (for rmdir)
 542  */
 543 static int empty_dir(struct m_inode * inode)
     /* [previous][next][first][last][top][bottom][index][help] */
 544 {
 545         int nr,block;
 546         int len;
 547         struct buffer_head * bh;
 548         struct dir_entry * de;
 549 
 550         len = inode->i_size / sizeof (struct dir_entry);
 551         if (len<2 || !inode->i_zone[0] ||
 552             !(bh=bread(inode->i_dev,inode->i_zone[0]))) {
 553                 printk("warning - bad directory on dev %04x\n",inode->i_dev);
 554                 return 0;
 555         }
 556         de = (struct dir_entry *) bh->b_data;
 557         if (de[0].inode != inode->i_num || !de[1].inode || 
 558             strcmp(".",de[0].name) || strcmp("..",de[1].name)) {
 559                 printk("warning - bad directory on dev %04x\n",inode->i_dev);
 560                 return 0;
 561         }
 562         nr = 2;
 563         de += 2;
 564         while (nr<len) {
 565                 if ((void *) de >= (void *) (bh->b_data+BLOCK_SIZE)) {
 566                         brelse(bh);
 567                         block=bmap(inode,nr/DIR_ENTRIES_PER_BLOCK);
 568                         if (!block) {
 569                                 nr += DIR_ENTRIES_PER_BLOCK;
 570                                 continue;
 571                         }
 572                         if (!(bh=bread(inode->i_dev,block)))
 573                                 return 0;
 574                         de = (struct dir_entry *) bh->b_data;
 575                 }
 576                 if (de->inode) {
 577                         brelse(bh);
 578                         return 0;
 579                 }
 580                 de++;
 581                 nr++;
 582         }
 583         brelse(bh);
 584         return 1;
 585 }
 586 
 587 int sys_rmdir(const char * name)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589         const char * basename;
 590         int namelen;
 591         struct m_inode * dir, * inode;
 592         struct buffer_head * bh;
 593         struct dir_entry * de;
 594 
 595         if (!suser())
 596                 return -EPERM;
 597         if (!(dir = dir_namei(name,&namelen,&basename)))
 598                 return -ENOENT;
 599         if (!namelen) {
 600                 iput(dir);
 601                 return -ENOENT;
 602         }
 603         if (!permission(dir,MAY_WRITE)) {
 604                 iput(dir);
 605                 return -EPERM;
 606         }
 607         bh = find_entry(&dir,basename,namelen,&de);
 608         if (!bh) {
 609                 iput(dir);
 610                 return -ENOENT;
 611         }
 612         if (!(inode = iget(dir->i_dev, de->inode))) {
 613                 iput(dir);
 614                 brelse(bh);
 615                 return -EPERM;
 616         }
 617         if ((dir->i_mode & S_ISVTX) && current->euid &&
 618             inode->i_uid != current->euid) {
 619                 iput(dir);
 620                 iput(inode);
 621                 brelse(bh);
 622                 return -EPERM;
 623         }
 624         if (inode->i_dev != dir->i_dev || inode->i_count>1) {
 625                 iput(dir);
 626                 iput(inode);
 627                 brelse(bh);
 628                 return -EPERM;
 629         }
 630         if (inode == dir) {     /* we may not delete ".", but "../dir" is ok */
 631                 iput(inode);
 632                 iput(dir);
 633                 brelse(bh);
 634                 return -EPERM;
 635         }
 636         if (!S_ISDIR(inode->i_mode)) {
 637                 iput(inode);
 638                 iput(dir);
 639                 brelse(bh);
 640                 return -ENOTDIR;
 641         }
 642         if (!empty_dir(inode)) {
 643                 iput(inode);
 644                 iput(dir);
 645                 brelse(bh);
 646                 return -ENOTEMPTY;
 647         }
 648         if (inode->i_nlinks != 2)
 649                 printk("empty directory has nlink!=2 (%d)",inode->i_nlinks);
 650         de->inode = 0;
 651         bh->b_dirt = 1;
 652         brelse(bh);
 653         inode->i_nlinks=0;
 654         inode->i_dirt=1;
 655         dir->i_nlinks--;
 656         dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 657         dir->i_dirt=1;
 658         iput(dir);
 659         iput(inode);
 660         return 0;
 661 }
 662 
 663 int sys_unlink(const char * name)
     /* [previous][next][first][last][top][bottom][index][help] */
 664 {
 665         const char * basename;
 666         int namelen;
 667         struct m_inode * dir, * inode;
 668         struct buffer_head * bh;
 669         struct dir_entry * de;
 670 
 671         if (!(dir = dir_namei(name,&namelen,&basename)))
 672                 return -ENOENT;
 673         if (!namelen) {
 674                 iput(dir);
 675                 return -ENOENT;
 676         }
 677         if (!permission(dir,MAY_WRITE)) {
 678                 iput(dir);
 679                 return -EPERM;
 680         }
 681         bh = find_entry(&dir,basename,namelen,&de);
 682         if (!bh) {
 683                 iput(dir);
 684                 return -ENOENT;
 685         }
 686         if (!(inode = iget(dir->i_dev, de->inode))) {
 687                 iput(dir);
 688                 brelse(bh);
 689                 return -ENOENT;
 690         }
 691         if ((dir->i_mode & S_ISVTX) && !suser() &&
 692             current->euid != inode->i_uid &&
 693             current->euid != dir->i_uid) {
 694                 iput(dir);
 695                 iput(inode);
 696                 brelse(bh);
 697                 return -EPERM;
 698         }
 699         if (S_ISDIR(inode->i_mode)) {
 700                 iput(inode);
 701                 iput(dir);
 702                 brelse(bh);
 703                 return -EPERM;
 704         }
 705         if (!inode->i_nlinks) {
 706                 printk("Deleting nonexistent file (%04x:%d), %d\n",
 707                         inode->i_dev,inode->i_num,inode->i_nlinks);
 708                 inode->i_nlinks=1;
 709         }
 710         de->inode = 0;
 711         bh->b_dirt = 1;
 712         brelse(bh);
 713         inode->i_nlinks--;
 714         inode->i_dirt = 1;
 715         inode->i_ctime = CURRENT_TIME;
 716         iput(inode);
 717         iput(dir);
 718         return 0;
 719 }
 720 
 721 int sys_link(const char * oldname, const char * newname)
     /* [previous][next][first][last][top][bottom][index][help] */
 722 {
 723         struct dir_entry * de;
 724         struct m_inode * oldinode, * dir;
 725         struct buffer_head * bh;
 726         const char * basename;
 727         int namelen;
 728 
 729         oldinode=namei(oldname);
 730         if (!oldinode)
 731                 return -ENOENT;
 732         if (S_ISDIR(oldinode->i_mode)) {
 733                 iput(oldinode);
 734                 return -EPERM;
 735         }
 736         dir = dir_namei(newname,&namelen,&basename);
 737         if (!dir) {
 738                 iput(oldinode);
 739                 return -EACCES;
 740         }
 741         if (!namelen) {
 742                 iput(oldinode);
 743                 iput(dir);
 744                 return -EPERM;
 745         }
 746         if (dir->i_dev != oldinode->i_dev) {
 747                 iput(dir);
 748                 iput(oldinode);
 749                 return -EXDEV;
 750         }
 751         if (!permission(dir,MAY_WRITE)) {
 752                 iput(dir);
 753                 iput(oldinode);
 754                 return -EACCES;
 755         }
 756         bh = find_entry(&dir,basename,namelen,&de);
 757         if (bh) {
 758                 brelse(bh);
 759                 iput(dir);
 760                 iput(oldinode);
 761                 return -EEXIST;
 762         }
 763         bh = add_entry(dir,basename,namelen,&de);
 764         if (!bh) {
 765                 iput(dir);
 766                 iput(oldinode);
 767                 return -ENOSPC;
 768         }
 769         de->inode = oldinode->i_num;
 770         bh->b_dirt = 1;
 771         brelse(bh);
 772         iput(dir);
 773         oldinode->i_nlinks++;
 774         oldinode->i_ctime = CURRENT_TIME;
 775         oldinode->i_dirt = 1;
 776         iput(oldinode);
 777         return 0;
 778 }

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