Fork me on GitHub

root/fs/minix/namei.c

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

DEFINITIONS

This source file includes following definitions.
  1. is_dir_empty
  2. find_dir_entry
  3. add_dir_entry
  4. is_subdir
  5. minix_lookup
  6. minix_rmdir
  7. minix_link
  8. minix_unlink
  9. minix_symlink
  10. minix_mkdir
  11. minix_mknod
  12. minix_create
  13. minix_rename

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

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