Fork me on GitHub

root/fs/ext2/namei.c

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

DEFINITIONS

This source file includes following definitions.
  1. find_dir_entry
  2. add_dir_entry
  3. is_dir_empty
  4. is_subdir
  5. ext2_lookup
  6. ext2_rmdir
  7. ext2_link
  8. ext2_unlink
  9. ext2_symlink
  10. ext2_mkdir
  11. ext2_mknod
  12. ext2_create
  13. ext2_rename

   1 /*
   2  * fiwix/fs/ext2/namei.c
   3  *
   4  * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved.
   5  * Distributed under the terms of the Fiwix License.
   6  */
   7 
   8 #include <fiwix/kernel.h>
   9 #include <fiwix/types.h>
  10 #include <fiwix/fs.h>
  11 #include <fiwix/filesystems.h>
  12 #include <fiwix/fs_ext2.h>
  13 #include <fiwix/buffer.h>
  14 #include <fiwix/mm.h>
  15 #include <fiwix/errno.h>
  16 #include <fiwix/stat.h>
  17 #include <fiwix/stdio.h>
  18 #include <fiwix/string.h>
  19 
  20 /* finds the entry 'name' with (optionally) inode 'i' in the directory 'dir' */
  21 static struct buffer *find_dir_entry(struct inode *dir, struct inode *i, struct ext2_dir_entry_2 **d_res, char *name)
  22 {
  23         __blk_t block;
  24         unsigned int blksize;
  25         unsigned int offset, doffset;
  26         struct buffer *buf;
  27         int basesize, rlen, nlen;
  28 
  29         basesize = sizeof((*d_res)->inode) + sizeof((*d_res)->rec_len) + sizeof((*d_res)->name_len) + sizeof((*d_res)->file_type);
  30         blksize = dir->sb->s_blocksize;
  31         offset = 0;
  32 
  33         /* nlen is the length of the new entry, used when searching for the first usable entry */
  34         nlen = basesize + strlen(name) + 3;
  35         nlen &= ~3;
  36 
  37         while(offset < dir->i_size) {
  38                 if((block = bmap(dir, offset, FOR_READING)) < 0) {
  39                         break;
  40                 }
  41                 if(block) {
  42                         if(!(buf = bread(dir->dev, block, blksize))) {
  43                                 break;
  44                         }
  45                         doffset = 0;
  46                         do {
  47                                 *d_res = (struct ext2_dir_entry_2 *)(buf->data + doffset);
  48                                 if(!i) {
  49                                         /* calculates the real length of the current entry */
  50                                         rlen = basesize + strlen((*d_res)->name) + 3;
  51                                         rlen &= ~3;
  52                                         /* returns the first entry where *name can fit in */
  53                                         if(!(*d_res)->inode) {
  54                                                 if(nlen <= (*d_res)->rec_len) {
  55                                                         return buf;
  56                                                 }
  57                                         } else {
  58                                                 if(rlen + nlen <= (*d_res)->rec_len) {
  59                                                         int nrec_len;
  60 
  61                                                         nrec_len = (*d_res)->rec_len - rlen;
  62                                                         (*d_res)->rec_len = rlen;
  63                                                         doffset += (*d_res)->rec_len;
  64                                                         *d_res = (struct ext2_dir_entry_2 *)(buf->data + doffset);
  65                                                         (*d_res)->rec_len = nrec_len;
  66                                                         return buf;
  67                                                 }
  68                                         }
  69                                         doffset += (*d_res)->rec_len;
  70                                 } else {
  71                                         if((*d_res)->inode == i->inode) {
  72                                                 /* returns the first matching inode */
  73                                                 if(!name) {
  74                                                         return buf;
  75                                                 }
  76                                                 /* returns the matching inode and name */
  77                                                 if((*d_res)->name_len == strlen(name)) {
  78                                                         if(!strncmp((*d_res)->name, name, (*d_res)->name_len)) {
  79                                                                 return buf;
  80                                                         }
  81                                                 }
  82                                         }
  83                                         doffset += (*d_res)->rec_len;
  84                                 }
  85                         } while(doffset < blksize);
  86                         brelse(buf);
  87                         offset += blksize;
  88                 } else {
  89                         break;
  90                 }
  91         }
  92 
  93         *d_res = NULL;
  94         return NULL;
  95 }
  96 
  97 static struct buffer *add_dir_entry(struct inode *dir, struct ext2_dir_entry_2 **d_res, char *name)
  98 {
  99         __blk_t block;
 100         struct buffer *buf;
 101 
 102         if(!(buf = find_dir_entry(dir, NULL, d_res, name))) {
 103                 if((block = bmap(dir, dir->i_size, FOR_WRITING)) < 0) {
 104                         return NULL;
 105                 }
 106                 if(!(buf = bread(dir->dev, block, dir->sb->s_blocksize))) {
 107                         return NULL;
 108                 }
 109                 *d_res = (struct ext2_dir_entry_2 *)buf->data;
 110                 dir->i_size += dir->sb->s_blocksize;
 111                 (*d_res)->rec_len = dir->sb->s_blocksize;
 112         }
 113 
 114         return buf;
 115 }
 116 
 117 static int is_dir_empty(struct inode *dir)
 118 {
 119         __blk_t block;
 120         unsigned int blksize;
 121         unsigned int offset, doffset;
 122         struct buffer *buf;
 123         struct ext2_dir_entry_2 *d;
 124 
 125         blksize = dir->sb->s_blocksize;
 126         offset = 0;
 127 
 128         while(offset < dir->i_size) {
 129                 if((block = bmap(dir, offset, FOR_READING)) < 0) {
 130                         break;
 131                 }
 132                 if(block) {
 133                         if(!(buf = bread(dir->dev, block, blksize))) {
 134                                 break;
 135                         }
 136                         doffset = 0;
 137                         do {
 138                                 if(doffset + offset >= dir->i_size) {
 139                                         break;
 140                                 }
 141                                 d = (struct ext2_dir_entry_2 *)(buf->data + doffset);
 142                                 doffset += d->rec_len;
 143                                 if(d->inode && d->name_len == 1 && d->name[0] == '.') {
 144                                         continue;
 145                                 }
 146                                 if(d->inode && d->name_len == 2 && d->name[0] == '.' && d->name[1] == '.') {
 147                                         continue;
 148                                 }
 149                                 if(d->inode) {
 150                                         brelse(buf);
 151                                         return 0;
 152                                 }
 153                         } while(doffset < blksize);
 154                         brelse(buf);
 155                         offset += blksize;
 156                 } else {
 157                         break;
 158                 }
 159         }
 160 
 161         return 1;
 162 }
 163 
 164 static int is_subdir(struct inode *dir_new, struct inode *i_old)
 165 {
 166         __ino_t inode;
 167         int errno;
 168 
 169         errno = 0;
 170         dir_new->count++;
 171         for(;;) {
 172                 if(dir_new == i_old) {
 173                         errno = 1;
 174                         break;
 175                 }
 176                 inode = dir_new->inode;
 177                 if(ext2_lookup("..", dir_new, &dir_new)) {
 178                         break;
 179                 }
 180                 if(dir_new->inode == inode) {
 181                         break;
 182                 }
 183         }
 184         iput(dir_new);
 185         return errno;
 186 }
 187 
 188 int ext2_lookup(const char *name, struct inode *dir, struct inode **i_res)
 189 {
 190         __blk_t block;
 191         unsigned int blksize;
 192         unsigned int offset, doffset;
 193         struct buffer *buf;
 194         struct ext2_dir_entry_2 *d;
 195         __ino_t inode;
 196 
 197         blksize = dir->sb->s_blocksize;
 198         inode = offset = 0;
 199 
 200         while(offset < dir->i_size && !inode) {
 201                 if((block = bmap(dir, offset, FOR_READING)) < 0) {
 202                         return block;
 203                 }
 204                 if(block) {
 205                         if(!(buf = bread(dir->dev, block, blksize))) {
 206                                 iput(dir);
 207                                 return -EIO;
 208                         }
 209                         doffset = 0;
 210                         do {
 211                                 d = (struct ext2_dir_entry_2 *)(buf->data + doffset);
 212                                 if(d->inode) {
 213                                         if(d->name_len == strlen(name)) {
 214                                                 if(strncmp(d->name, name, d->name_len) == 0) {
 215                                                         inode = d->inode;
 216                                                 }
 217                                         }
 218                                 }
 219                                 doffset += d->rec_len;
 220                         } while((doffset < blksize) && (!inode));
 221 
 222                         brelse(buf);
 223                         offset += blksize;
 224                         if(inode) {
 225                                 /*
 226                                  * This prevents a deadlock in iget() when
 227                                  * trying to lock '.' when 'dir' is the same
 228                                  * directory (ls -lai <dir>).
 229                                  */
 230                                 if(inode == dir->inode) {
 231                                         *i_res = dir;
 232                                         return 0;
 233                                 }
 234 
 235                                 if(!(*i_res = iget(dir->sb, inode))) {
 236                                         iput(dir);
 237                                         return -EACCES;
 238                                 }
 239                                 iput(dir);
 240                                 return 0;
 241                         }
 242                 } else {
 243                         break;
 244                 }
 245         }
 246         iput(dir);
 247         return -ENOENT;
 248 }
 249 
 250 int ext2_rmdir(struct inode *dir, struct inode *i)
 251 {
 252         struct buffer *buf;
 253         struct ext2_dir_entry_2 *d;
 254 
 255         inode_lock(i);
 256 
 257         if(!is_dir_empty(i)) {
 258                 inode_unlock(i);
 259                 return -ENOTEMPTY;
 260         }
 261 
 262         inode_lock(dir);
 263 
 264         if(!(buf = find_dir_entry(dir, i, &d, NULL))) {
 265                 inode_unlock(i);
 266                 inode_unlock(dir);
 267                 return -ENOENT;
 268         }
 269 
 270         d->inode = 0;
 271         i->i_nlink = 0;
 272         dir->i_nlink--;
 273 
 274         i->i_ctime = CURRENT_TIME;
 275         i->u.ext2.i_dtime = CURRENT_TIME;
 276         dir->i_mtime = CURRENT_TIME;
 277         dir->i_ctime = CURRENT_TIME;
 278 
 279         i->dirty = 1;
 280         dir->dirty = 1;
 281 
 282         bwrite(buf);
 283 
 284         inode_unlock(i);
 285         inode_unlock(dir);
 286         return 0;
 287 }
 288 
 289 int ext2_link(struct inode *i_old, struct inode *dir_new, char *name)
 290 {
 291         struct buffer *buf;
 292         struct ext2_dir_entry_2 *d;
 293         char c;
 294         int n;
 295 
 296         inode_lock(i_old);
 297         inode_lock(dir_new);
 298 
 299         if(!(buf = add_dir_entry(dir_new, &d, name))) {
 300                 inode_unlock(i_old);
 301                 inode_unlock(dir_new);
 302                 return -ENOSPC;
 303         }
 304 
 305         d->inode = i_old->inode;
 306         d->name_len = strlen(name);
 307         /* strcpy() can't be used here because it places a trailing NULL */
 308         for(n = 0; n < NAME_MAX; n++) {
 309                 if((c = name[n])) {
 310                         d->name[n] = c;
 311                         continue;
 312                 }
 313                 break;
 314         }
 315         d->file_type = 0;       /* not used */
 316 
 317         i_old->i_nlink++;
 318         i_old->i_ctime = CURRENT_TIME;
 319         dir_new->i_mtime = CURRENT_TIME;
 320         dir_new->i_ctime = CURRENT_TIME;
 321 
 322         i_old->dirty = 1;
 323         dir_new->dirty = 1;
 324 
 325         bwrite(buf);
 326 
 327         inode_unlock(i_old);
 328         inode_unlock(dir_new);
 329         return 0;
 330 }
 331 
 332 int ext2_unlink(struct inode *dir, struct inode *i, char *name)
 333 {
 334         struct buffer *buf;
 335         struct ext2_dir_entry_2 *d;
 336 
 337         inode_lock(dir);
 338         inode_lock(i);
 339 
 340         if(!(buf = find_dir_entry(dir, i, &d, name))) {
 341                 inode_unlock(dir);
 342                 inode_unlock(i);
 343                 return -ENOENT;
 344         }
 345 
 346         /*
 347          * FIXME: in order to avoid low performance when traversing large
 348          * directories plenty of blank entries, it would be interesting
 349          * to merge every removed entry with the previous entry.
 350          */
 351         d->inode = 0;
 352         if(!--i->i_nlink) {
 353                 i->u.ext2.i_dtime = CURRENT_TIME;
 354         }
 355 
 356         i->i_ctime = CURRENT_TIME;
 357         dir->i_mtime = CURRENT_TIME;
 358         dir->i_ctime = CURRENT_TIME;
 359 
 360         i->dirty = 1;
 361         dir->dirty = 1;
 362 
 363         bwrite(buf);
 364 
 365         inode_unlock(dir);
 366         inode_unlock(i);
 367         return 0;
 368 }
 369 
 370 int ext2_symlink(struct inode *dir, char *name, char *oldname)
 371 {
 372         struct buffer *buf, *buf2;
 373         struct inode *i;
 374         struct ext2_dir_entry_2 *d;
 375         __blk_t block;
 376         char c, *data;
 377         int n;
 378 
 379         inode_lock(dir);
 380 
 381         if(!(i = ialloc(dir->sb, S_IFLNK))) {
 382                 inode_unlock(dir);
 383                 return -ENOSPC;
 384         }
 385 
 386         if(!(buf = add_dir_entry(dir, &d, name))) {
 387                 iput(i);
 388                 inode_unlock(dir);
 389                 return -ENOSPC;
 390         }
 391 
 392         i->i_mode = S_IFLNK | (S_IRWXU | S_IRWXG | S_IRWXO);
 393         i->i_uid = current->euid;
 394         i->i_gid = current->egid;
 395         i->dev = dir->dev;
 396         i->count = 1;
 397         i->fsop = &ext2_symlink_fsop;
 398 
 399         if(strlen(oldname) >= EXT2_N_BLOCKS * sizeof(__u32)) {
 400                 /* this will be a slow symlink */
 401                 if((block = ext2_balloc(dir->sb)) < 0) {
 402                         iput(i);
 403                         brelse(buf);
 404                         inode_unlock(dir);
 405                         return block;
 406                 }
 407                 if(!(buf2 = bread(dir->dev, block, dir->sb->s_blocksize))) {
 408                         iput(i);
 409                         brelse(buf);
 410                         ext2_bfree(dir->sb, block);
 411                         inode_unlock(dir);
 412                         return -EIO;
 413                 }
 414                 i->u.ext2.i_data[0] = block;
 415                 for(n = 0; n < NAME_MAX; n++) {
 416                         if((c = oldname[n])) {
 417                                 buf2->data[n] = c;
 418                                 continue;
 419                         }
 420                         break;
 421                 }
 422                 buf2->data[n] = 0;
 423                 i->i_blocks = dir->sb->s_blocksize / 512;
 424                 bwrite(buf2);
 425         } else {
 426                 /* this will be a fast symlink */
 427                 data = (char *)i->u.ext2.i_data;
 428                 for(n = 0; n < NAME_MAX; n++) {
 429                         if((c = oldname[n])) {
 430                                 data[n] = c;
 431                                 continue;
 432                         }
 433                         break;
 434                 }
 435                 data[n] = 0;
 436         }
 437 
 438         i->i_size = n;
 439         i->dirty = 1;
 440         i->i_nlink = 1;
 441         d->inode = i->inode;
 442         d->name_len = strlen(name);
 443         /* strcpy() can't be used here because it places a trailing NULL */
 444         for(n = 0; n < NAME_MAX; n++) {
 445                 if((c = name[n])) {
 446                         d->name[n] = c;
 447                         continue;
 448                 }
 449                 break;
 450         }
 451         d->file_type = 0;       /* EXT2_FT_SYMLINK not used */
 452 
 453         dir->i_mtime = CURRENT_TIME;
 454         dir->i_ctime = CURRENT_TIME;
 455         dir->dirty = 1;
 456 
 457         bwrite(buf);
 458         iput(i);
 459         inode_unlock(dir);
 460         return 0;
 461 }
 462 
 463 int ext2_mkdir(struct inode *dir, char *name, __mode_t mode)
 464 {
 465         struct buffer *buf, *buf2;
 466         struct inode *i;
 467         struct ext2_dir_entry_2 *d, *d2;
 468         __blk_t block;
 469         char c;
 470         int n;
 471 
 472         inode_lock(dir);
 473 
 474         if(!(i = ialloc(dir->sb, S_IFDIR))) {
 475                 inode_unlock(dir);
 476                 return -ENOSPC;
 477         }
 478 
 479         i->i_mode = ((mode & (S_IRWXU | S_IRWXG | S_IRWXO)) & ~current->umask);
 480         i->i_mode |= S_IFDIR;
 481         i->i_uid = current->euid;
 482         i->i_gid = current->egid;
 483         i->dev = dir->dev;
 484         i->count = 1;
 485         i->fsop = &ext2_dir_fsop;
 486 
 487         if((block = bmap(i, 0, FOR_WRITING)) < 0) {
 488                 iput(i);
 489                 inode_unlock(dir);
 490                 return block;
 491         }
 492 
 493         if(!(buf2 = bread(i->dev, block, dir->sb->s_blocksize))) {
 494                 ext2_bfree(dir->sb, block);
 495                 iput(i);
 496                 inode_unlock(dir);
 497                 return -EIO;
 498         }
 499 
 500         if(!(buf = add_dir_entry(dir, &d, name))) {
 501                 ext2_bfree(dir->sb, block);
 502                 iput(i);
 503                 brelse(buf2);
 504                 inode_unlock(dir);
 505                 return -ENOSPC;
 506         }
 507 
 508         d->inode = i->inode;
 509         d->name_len = strlen(name);
 510         /* strcpy() can't be used here because it places a trailing NULL */
 511         for(n = 0; n < NAME_MAX; n++) {
 512                 if((c = name[n])) {
 513                         if(c != '/') {
 514                                 d->name[n] = c;
 515                                 continue;
 516                         }
 517                 }
 518                 break;
 519         }
 520         d->file_type = 0;       /* EXT2_FT_DIR not used */
 521 
 522         d2 = (struct ext2_dir_entry_2 *)buf2->data;
 523         d2->inode = i->inode;
 524         d2->name[0] = '.';
 525         d2->name[1] = 0;
 526         d2->name_len = 1;
 527         d2->rec_len = 12;
 528         d2->file_type = 0;      /* EXT2_FT_DIR not used */
 529         i->i_nlink = 1;
 530         d2 = (struct ext2_dir_entry_2 *)(buf2->data + 12);
 531         d2->inode = dir->inode;
 532         d2->name[0] = '.';
 533         d2->name[1] = '.';
 534         d2->name[2] = 0;
 535         d2->name_len = 2;
 536         d2->rec_len = i->sb->s_blocksize - 12;
 537         d2->file_type = 0;      /* EXT2_FT_DIR not used */
 538         i->i_nlink++;
 539         i->i_size = i->sb->s_blocksize;
 540         i->i_blocks = dir->sb->s_blocksize / 512;
 541         i->dirty = 1;
 542 
 543         dir->i_mtime = CURRENT_TIME;
 544         dir->i_ctime = CURRENT_TIME;
 545         dir->i_nlink++;
 546         dir->dirty = 1;
 547 
 548         bwrite(buf);
 549         bwrite(buf2);
 550         iput(i);
 551         inode_unlock(dir);
 552         return 0;
 553 }
 554 
 555 int ext2_mknod(struct inode *dir, char *name, __mode_t mode, __dev_t dev)
 556 {
 557         struct buffer *buf;
 558         struct inode *i;
 559         struct ext2_dir_entry_2 *d;
 560         char c;
 561         int n;
 562 
 563         inode_lock(dir);
 564 
 565         if(!(i = ialloc(dir->sb, mode & S_IFMT))) {
 566                 inode_unlock(dir);
 567                 return -ENOSPC;
 568         }
 569 
 570         if(!(buf = add_dir_entry(dir, &d, name))) {
 571                 i->i_nlink = 0;
 572                 iput(i);
 573                 inode_unlock(dir);
 574                 return -ENOSPC;
 575         }
 576 
 577         d->inode = i->inode;
 578         d->name_len = strlen(name);
 579         /* strcpy() can't be used here because it places a trailing NULL */
 580         for(n = 0; n < NAME_MAX; n++) {
 581                 if((c = name[n])) {
 582                         d->name[n] = c;
 583                         continue;
 584                 }
 585                 break;
 586         }
 587 
 588         i->i_mode = (mode & ~current->umask) & ~S_IFMT;
 589         i->i_uid = current->euid;
 590         i->i_gid = current->egid;
 591         i->i_nlink = 1;
 592         i->dev = dir->dev;
 593         i->count = 1;
 594         i->dirty = 1;
 595 
 596         switch(mode & S_IFMT) {
 597                 case S_IFCHR:
 598                         i->fsop = &def_chr_fsop;
 599                         i->rdev = dev;
 600                         i->i_mode |= S_IFCHR;
 601                         d->file_type = 0;       /* EXT2_FT_CHRDEV not used */
 602                         break;
 603                 case S_IFBLK:
 604                         i->fsop = &def_blk_fsop;
 605                         i->rdev = dev;
 606                         i->i_mode |= S_IFBLK;
 607                         d->file_type = 0;       /* EXT2_FT_BLKDEV not used */
 608                         break;
 609                 case S_IFIFO:
 610                         i->fsop = &pipefs_fsop;
 611                         i->i_mode |= S_IFIFO;
 612                         /* it's a union so we need to clear pipefs_i */
 613                         memset_b(&i->u.pipefs, 0, sizeof(struct pipefs_inode));
 614                         d->file_type = 0;       /* EXT2_FT_FIFO not used */
 615                         break;
 616         }
 617 
 618         dir->i_mtime = CURRENT_TIME;
 619         dir->i_ctime = CURRENT_TIME;
 620         dir->dirty = 1;
 621 
 622         bwrite(buf);
 623         iput(i);
 624         inode_unlock(dir);
 625         return 0;
 626 }
 627 
 628 int ext2_create(struct inode *dir, char *name, __mode_t mode, struct inode **i_res)
 629 {
 630         struct buffer *buf;
 631         struct inode *i;
 632         struct ext2_dir_entry_2 *d;
 633         char c;
 634         int n;
 635 
 636         if(IS_RDONLY_FS(dir)) {
 637                 return -EROFS;
 638         }
 639 
 640         inode_lock(dir);
 641 
 642         if(!(i = ialloc(dir->sb, S_IFREG))) {
 643                 inode_unlock(dir);
 644                 return -ENOSPC;
 645         }
 646 
 647         if(!(buf = add_dir_entry(dir, &d, name))) {
 648                 i->i_nlink = 0;
 649                 iput(i);
 650                 inode_unlock(dir);
 651                 return -ENOSPC;
 652         }
 653 
 654         d->inode = i->inode;
 655         d->name_len = strlen(name);
 656         /* strcpy() can't be used here because it places a trailing NULL */
 657         for(n = 0; n < NAME_MAX; n++) {
 658                 if((c = name[n])) {
 659                         d->name[n] = c;
 660                         continue;
 661                 }
 662                 break;
 663         }
 664         d->file_type = 0;       /* EXT2_FT_REG_FILE not used */
 665 
 666         i->i_mode = (mode & ~current->umask) & ~S_IFMT;
 667         i->i_mode |= S_IFREG;
 668         i->i_uid = current->euid;
 669         i->i_gid = current->egid;
 670         i->i_nlink = 1;
 671         i->i_blocks = 0;
 672         i->dev = dir->dev;
 673         i->fsop = &ext2_file_fsop;
 674         i->count = 1;
 675         i->dirty = 1;
 676 
 677         i->u.ext2.i_dtime = 0;
 678 
 679         dir->i_mtime = CURRENT_TIME;
 680         dir->i_ctime = CURRENT_TIME;
 681         dir->dirty = 1;
 682 
 683         *i_res = i;
 684         bwrite(buf);
 685         inode_unlock(dir);
 686         return 0;
 687 }
 688 
 689 int ext2_rename(struct inode *i_old, struct inode *dir_old, struct inode *i_new, struct inode *dir_new, char *oldpath, char *newpath)
 690 {
 691         struct buffer *buf_old, *buf_new;
 692         struct ext2_dir_entry_2 *d_old, *d_new;
 693         char c;
 694         int n, errno;
 695 
 696         errno = 0;
 697 
 698         if(is_subdir(dir_new, i_old)) {
 699                 return -EINVAL;
 700         }
 701 
 702         inode_lock(i_old);
 703         inode_lock(dir_old);
 704         if(dir_old != dir_new) {
 705                 inode_lock(dir_new);
 706         }
 707 
 708         if(!(buf_old = find_dir_entry(dir_old, i_old, &d_old, oldpath))) {
 709                 errno = -ENOENT;
 710                 goto end;
 711         }
 712         if(dir_old == dir_new) {
 713                 /* free that buffer now to not block buf_new */
 714                 brelse(buf_old);
 715                 buf_old = NULL;
 716         }
 717 
 718         if(i_new) {
 719                 if(S_ISDIR(i_old->i_mode)) {
 720                         if(!is_dir_empty(i_new)) {
 721                                 if(buf_old) {
 722                                         brelse(buf_old);
 723                                 }
 724                                 errno = -ENOTEMPTY;
 725                                 goto end;
 726                         }
 727                 }
 728                 if(!(buf_new = find_dir_entry(dir_new, i_new, &d_new, newpath))) {
 729                         if(buf_old) {
 730                                 brelse(buf_old);
 731                         }
 732                         errno = -ENOENT;
 733                         goto end;
 734                 }
 735         } else {
 736                 if(!(buf_new = add_dir_entry(dir_new, &d_new, newpath))) {
 737                         if(buf_old) {
 738                                 brelse(buf_old);
 739                         }
 740                         errno = -ENOSPC;
 741                         goto end;
 742                 }
 743                 if(S_ISDIR(i_old->i_mode)) {
 744                         dir_old->i_nlink--;
 745                         dir_new->i_nlink++;
 746                 }
 747         }
 748         if(i_new) {
 749                 i_new->i_nlink--;
 750         } else {
 751                 i_new = i_old;
 752                 d_new->name_len = strlen(newpath);
 753                 /* strcpy() can't be used here because it places a trailing NULL */
 754                 for(n = 0; n < NAME_MAX; n++) {
 755                         if((c = newpath[n])) {
 756                                 d_new->name[n] = c;
 757                                 continue;
 758                         }
 759                         break;
 760                 }
 761         }
 762 
 763         d_new->inode = i_old->inode;
 764         dir_new->i_mtime = CURRENT_TIME;
 765         dir_new->i_ctime = CURRENT_TIME;
 766         i_new->dirty = 1;
 767         dir_new->dirty = 1;
 768 
 769         dir_old->i_mtime = CURRENT_TIME;
 770         dir_old->i_ctime = CURRENT_TIME;
 771         i_old->dirty = 1;
 772         dir_old->dirty = 1;
 773         bwrite(buf_new);
 774 
 775         if(!buf_old) {
 776                 if(!(buf_old = find_dir_entry(dir_old, i_old, &d_old, oldpath))) {
 777                         errno = -ENOENT;
 778                         goto end;
 779                 }
 780         }
 781         d_old->inode = 0;
 782         bwrite(buf_old);
 783 
 784         /* update the parent directory */
 785         if(S_ISDIR(i_old->i_mode)) {
 786                 buf_new = find_dir_entry(i_old, dir_old, &d_new, "..");
 787                 if(buf_new) {
 788                         d_new->inode = dir_new->inode;
 789                         bwrite(buf_new);
 790                 }
 791         }
 792 
 793 end:
 794         inode_unlock(i_old);
 795         inode_unlock(dir_old);
 796         inode_unlock(dir_new);
 797         return errno;
 798 }

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