root/fs/affs/namei.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. affs_toupper
  2. affs_intl_toupper
  3. affs_get_toupper
  4. __affs_hash_dentry
  5. affs_hash_dentry
  6. affs_intl_hash_dentry
  7. __affs_compare_dentry
  8. affs_compare_dentry
  9. affs_intl_compare_dentry
  10. affs_match
  11. affs_hash_name
  12. affs_find_entry
  13. affs_lookup
  14. affs_unlink
  15. affs_create
  16. affs_mkdir
  17. affs_rmdir
  18. affs_symlink
  19. affs_link
  20. affs_rename
  21. affs_xrename
  22. affs_rename2
  23. affs_get_parent
  24. affs_nfs_get_inode
  25. affs_fh_to_dentry
  26. affs_fh_to_parent

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  linux/fs/affs/namei.c
   4  *
   5  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
   6  *
   7  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
   8  *
   9  *  (C) 1991  Linus Torvalds - minix filesystem
  10  */
  11 
  12 #include "affs.h"
  13 #include <linux/exportfs.h>
  14 
  15 typedef int (*toupper_t)(int);
  16 
  17 /* Simple toupper() for DOS\1 */
  18 
  19 static int
  20 affs_toupper(int ch)
  21 {
  22         return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
  23 }
  24 
  25 /* International toupper() for DOS\3 ("international") */
  26 
  27 static int
  28 affs_intl_toupper(int ch)
  29 {
  30         return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
  31                 && ch <= 0xFE && ch != 0xF7) ?
  32                 ch - ('a' - 'A') : ch;
  33 }
  34 
  35 static inline toupper_t
  36 affs_get_toupper(struct super_block *sb)
  37 {
  38         return affs_test_opt(AFFS_SB(sb)->s_flags, SF_INTL) ?
  39                affs_intl_toupper : affs_toupper;
  40 }
  41 
  42 /*
  43  * Note: the dentry argument is the parent dentry.
  44  */
  45 static inline int
  46 __affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr, toupper_t toupper, bool notruncate)
  47 {
  48         const u8 *name = qstr->name;
  49         unsigned long hash;
  50         int retval;
  51         u32 len;
  52 
  53         retval = affs_check_name(qstr->name, qstr->len, notruncate);
  54         if (retval)
  55                 return retval;
  56 
  57         hash = init_name_hash(dentry);
  58         len = min(qstr->len, AFFSNAMEMAX);
  59         for (; len > 0; name++, len--)
  60                 hash = partial_name_hash(toupper(*name), hash);
  61         qstr->hash = end_name_hash(hash);
  62 
  63         return 0;
  64 }
  65 
  66 static int
  67 affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
  68 {
  69         return __affs_hash_dentry(dentry, qstr, affs_toupper,
  70                                   affs_nofilenametruncate(dentry));
  71 
  72 }
  73 
  74 static int
  75 affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
  76 {
  77         return __affs_hash_dentry(dentry, qstr, affs_intl_toupper,
  78                                   affs_nofilenametruncate(dentry));
  79 
  80 }
  81 
  82 static inline int __affs_compare_dentry(unsigned int len,
  83                 const char *str, const struct qstr *name, toupper_t toupper,
  84                 bool notruncate)
  85 {
  86         const u8 *aname = str;
  87         const u8 *bname = name->name;
  88 
  89         /*
  90          * 'str' is the name of an already existing dentry, so the name
  91          * must be valid. 'name' must be validated first.
  92          */
  93 
  94         if (affs_check_name(name->name, name->len, notruncate))
  95                 return 1;
  96 
  97         /*
  98          * If the names are longer than the allowed 30 chars,
  99          * the excess is ignored, so their length may differ.
 100          */
 101         if (len >= AFFSNAMEMAX) {
 102                 if (name->len < AFFSNAMEMAX)
 103                         return 1;
 104                 len = AFFSNAMEMAX;
 105         } else if (len != name->len)
 106                 return 1;
 107 
 108         for (; len > 0; len--)
 109                 if (toupper(*aname++) != toupper(*bname++))
 110                         return 1;
 111 
 112         return 0;
 113 }
 114 
 115 static int
 116 affs_compare_dentry(const struct dentry *dentry,
 117                 unsigned int len, const char *str, const struct qstr *name)
 118 {
 119 
 120         return __affs_compare_dentry(len, str, name, affs_toupper,
 121                                      affs_nofilenametruncate(dentry));
 122 }
 123 
 124 static int
 125 affs_intl_compare_dentry(const struct dentry *dentry,
 126                 unsigned int len, const char *str, const struct qstr *name)
 127 {
 128         return __affs_compare_dentry(len, str, name, affs_intl_toupper,
 129                                      affs_nofilenametruncate(dentry));
 130 
 131 }
 132 
 133 /*
 134  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
 135  */
 136 
 137 static inline int
 138 affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
 139 {
 140         const u8 *name = dentry->d_name.name;
 141         int len = dentry->d_name.len;
 142 
 143         if (len >= AFFSNAMEMAX) {
 144                 if (*name2 < AFFSNAMEMAX)
 145                         return 0;
 146                 len = AFFSNAMEMAX;
 147         } else if (len != *name2)
 148                 return 0;
 149 
 150         for (name2++; len > 0; len--)
 151                 if (toupper(*name++) != toupper(*name2++))
 152                         return 0;
 153         return 1;
 154 }
 155 
 156 int
 157 affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
 158 {
 159         toupper_t toupper = affs_get_toupper(sb);
 160         u32 hash;
 161 
 162         hash = len = min(len, AFFSNAMEMAX);
 163         for (; len > 0; len--)
 164                 hash = (hash * 13 + toupper(*name++)) & 0x7ff;
 165 
 166         return hash % AFFS_SB(sb)->s_hashsize;
 167 }
 168 
 169 static struct buffer_head *
 170 affs_find_entry(struct inode *dir, struct dentry *dentry)
 171 {
 172         struct super_block *sb = dir->i_sb;
 173         struct buffer_head *bh;
 174         toupper_t toupper = affs_get_toupper(sb);
 175         u32 key;
 176 
 177         pr_debug("%s(\"%pd\")\n", __func__, dentry);
 178 
 179         bh = affs_bread(sb, dir->i_ino);
 180         if (!bh)
 181                 return ERR_PTR(-EIO);
 182 
 183         key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
 184 
 185         for (;;) {
 186                 affs_brelse(bh);
 187                 if (key == 0)
 188                         return NULL;
 189                 bh = affs_bread(sb, key);
 190                 if (!bh)
 191                         return ERR_PTR(-EIO);
 192                 if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
 193                         return bh;
 194                 key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
 195         }
 196 }
 197 
 198 struct dentry *
 199 affs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
 200 {
 201         struct super_block *sb = dir->i_sb;
 202         struct buffer_head *bh;
 203         struct inode *inode = NULL;
 204         struct dentry *res;
 205 
 206         pr_debug("%s(\"%pd\")\n", __func__, dentry);
 207 
 208         affs_lock_dir(dir);
 209         bh = affs_find_entry(dir, dentry);
 210         if (IS_ERR(bh)) {
 211                 affs_unlock_dir(dir);
 212                 return ERR_CAST(bh);
 213         }
 214         if (bh) {
 215                 u32 ino = bh->b_blocknr;
 216 
 217                 /* store the real header ino in d_fsdata for faster lookups */
 218                 dentry->d_fsdata = (void *)(long)ino;
 219                 switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
 220                 //link to dirs disabled
 221                 //case ST_LINKDIR:
 222                 case ST_LINKFILE:
 223                         ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
 224                 }
 225                 affs_brelse(bh);
 226                 inode = affs_iget(sb, ino);
 227         }
 228         res = d_splice_alias(inode, dentry);
 229         if (!IS_ERR_OR_NULL(res))
 230                 res->d_fsdata = dentry->d_fsdata;
 231         affs_unlock_dir(dir);
 232         return res;
 233 }
 234 
 235 int
 236 affs_unlink(struct inode *dir, struct dentry *dentry)
 237 {
 238         pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
 239                  d_inode(dentry)->i_ino, dentry);
 240 
 241         return affs_remove_header(dentry);
 242 }
 243 
 244 int
 245 affs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
 246 {
 247         struct super_block *sb = dir->i_sb;
 248         struct inode    *inode;
 249         int              error;
 250 
 251         pr_debug("%s(%lu,\"%pd\",0%ho)\n",
 252                  __func__, dir->i_ino, dentry, mode);
 253 
 254         inode = affs_new_inode(dir);
 255         if (!inode)
 256                 return -ENOSPC;
 257 
 258         inode->i_mode = mode;
 259         affs_mode_to_prot(inode);
 260         mark_inode_dirty(inode);
 261 
 262         inode->i_op = &affs_file_inode_operations;
 263         inode->i_fop = &affs_file_operations;
 264         inode->i_mapping->a_ops = affs_test_opt(AFFS_SB(sb)->s_flags, SF_OFS) ?
 265                                   &affs_aops_ofs : &affs_aops;
 266         error = affs_add_entry(dir, inode, dentry, ST_FILE);
 267         if (error) {
 268                 clear_nlink(inode);
 269                 iput(inode);
 270                 return error;
 271         }
 272         return 0;
 273 }
 274 
 275 int
 276 affs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 277 {
 278         struct inode            *inode;
 279         int                      error;
 280 
 281         pr_debug("%s(%lu,\"%pd\",0%ho)\n",
 282                  __func__, dir->i_ino, dentry, mode);
 283 
 284         inode = affs_new_inode(dir);
 285         if (!inode)
 286                 return -ENOSPC;
 287 
 288         inode->i_mode = S_IFDIR | mode;
 289         affs_mode_to_prot(inode);
 290 
 291         inode->i_op = &affs_dir_inode_operations;
 292         inode->i_fop = &affs_dir_operations;
 293 
 294         error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
 295         if (error) {
 296                 clear_nlink(inode);
 297                 mark_inode_dirty(inode);
 298                 iput(inode);
 299                 return error;
 300         }
 301         return 0;
 302 }
 303 
 304 int
 305 affs_rmdir(struct inode *dir, struct dentry *dentry)
 306 {
 307         pr_debug("%s(dir=%lu, %lu \"%pd\")\n", __func__, dir->i_ino,
 308                  d_inode(dentry)->i_ino, dentry);
 309 
 310         return affs_remove_header(dentry);
 311 }
 312 
 313 int
 314 affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 315 {
 316         struct super_block      *sb = dir->i_sb;
 317         struct buffer_head      *bh;
 318         struct inode            *inode;
 319         char                    *p;
 320         int                      i, maxlen, error;
 321         char                     c, lc;
 322 
 323         pr_debug("%s(%lu,\"%pd\" -> \"%s\")\n",
 324                  __func__, dir->i_ino, dentry, symname);
 325 
 326         maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1;
 327         inode  = affs_new_inode(dir);
 328         if (!inode)
 329                 return -ENOSPC;
 330 
 331         inode->i_op = &affs_symlink_inode_operations;
 332         inode_nohighmem(inode);
 333         inode->i_data.a_ops = &affs_symlink_aops;
 334         inode->i_mode = S_IFLNK | 0777;
 335         affs_mode_to_prot(inode);
 336 
 337         error = -EIO;
 338         bh = affs_bread(sb, inode->i_ino);
 339         if (!bh)
 340                 goto err;
 341         i  = 0;
 342         p  = (char *)AFFS_HEAD(bh)->table;
 343         lc = '/';
 344         if (*symname == '/') {
 345                 struct affs_sb_info *sbi = AFFS_SB(sb);
 346                 while (*symname == '/')
 347                         symname++;
 348                 spin_lock(&sbi->symlink_lock);
 349                 while (sbi->s_volume[i])        /* Cannot overflow */
 350                         *p++ = sbi->s_volume[i++];
 351                 spin_unlock(&sbi->symlink_lock);
 352         }
 353         while (i < maxlen && (c = *symname++)) {
 354                 if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
 355                         *p++ = '/';
 356                         i++;
 357                         symname += 2;
 358                         lc = '/';
 359                 } else if (c == '.' && lc == '/' && *symname == '/') {
 360                         symname++;
 361                         lc = '/';
 362                 } else {
 363                         *p++ = c;
 364                         lc   = c;
 365                         i++;
 366                 }
 367                 if (lc == '/')
 368                         while (*symname == '/')
 369                                 symname++;
 370         }
 371         *p = 0;
 372         inode->i_size = i + 1;
 373         mark_buffer_dirty_inode(bh, inode);
 374         affs_brelse(bh);
 375         mark_inode_dirty(inode);
 376 
 377         error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
 378         if (error)
 379                 goto err;
 380 
 381         return 0;
 382 
 383 err:
 384         clear_nlink(inode);
 385         mark_inode_dirty(inode);
 386         iput(inode);
 387         return error;
 388 }
 389 
 390 int
 391 affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 392 {
 393         struct inode *inode = d_inode(old_dentry);
 394 
 395         pr_debug("%s(%lu, %lu, \"%pd\")\n", __func__, inode->i_ino, dir->i_ino,
 396                  dentry);
 397 
 398         return affs_add_entry(dir, inode, dentry, ST_LINKFILE);
 399 }
 400 
 401 static int
 402 affs_rename(struct inode *old_dir, struct dentry *old_dentry,
 403             struct inode *new_dir, struct dentry *new_dentry)
 404 {
 405         struct super_block *sb = old_dir->i_sb;
 406         struct buffer_head *bh = NULL;
 407         int retval;
 408 
 409         retval = affs_check_name(new_dentry->d_name.name,
 410                                  new_dentry->d_name.len,
 411                                  affs_nofilenametruncate(old_dentry));
 412 
 413         if (retval)
 414                 return retval;
 415 
 416         /* Unlink destination if it already exists */
 417         if (d_really_is_positive(new_dentry)) {
 418                 retval = affs_remove_header(new_dentry);
 419                 if (retval)
 420                         return retval;
 421         }
 422 
 423         bh = affs_bread(sb, d_inode(old_dentry)->i_ino);
 424         if (!bh)
 425                 return -EIO;
 426 
 427         /* Remove header from its parent directory. */
 428         affs_lock_dir(old_dir);
 429         retval = affs_remove_hash(old_dir, bh);
 430         affs_unlock_dir(old_dir);
 431         if (retval)
 432                 goto done;
 433 
 434         /* And insert it into the new directory with the new name. */
 435         affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
 436         affs_fix_checksum(sb, bh);
 437         affs_lock_dir(new_dir);
 438         retval = affs_insert_hash(new_dir, bh);
 439         affs_unlock_dir(new_dir);
 440         /* TODO: move it back to old_dir, if error? */
 441 
 442 done:
 443         mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
 444         affs_brelse(bh);
 445         return retval;
 446 }
 447 
 448 static int
 449 affs_xrename(struct inode *old_dir, struct dentry *old_dentry,
 450              struct inode *new_dir, struct dentry *new_dentry)
 451 {
 452 
 453         struct super_block *sb = old_dir->i_sb;
 454         struct buffer_head *bh_old = NULL;
 455         struct buffer_head *bh_new = NULL;
 456         int retval;
 457 
 458         bh_old = affs_bread(sb, d_inode(old_dentry)->i_ino);
 459         if (!bh_old)
 460                 return -EIO;
 461 
 462         bh_new = affs_bread(sb, d_inode(new_dentry)->i_ino);
 463         if (!bh_new)
 464                 return -EIO;
 465 
 466         /* Remove old header from its parent directory. */
 467         affs_lock_dir(old_dir);
 468         retval = affs_remove_hash(old_dir, bh_old);
 469         affs_unlock_dir(old_dir);
 470         if (retval)
 471                 goto done;
 472 
 473         /* Remove new header from its parent directory. */
 474         affs_lock_dir(new_dir);
 475         retval = affs_remove_hash(new_dir, bh_new);
 476         affs_unlock_dir(new_dir);
 477         if (retval)
 478                 goto done;
 479 
 480         /* Insert old into the new directory with the new name. */
 481         affs_copy_name(AFFS_TAIL(sb, bh_old)->name, new_dentry);
 482         affs_fix_checksum(sb, bh_old);
 483         affs_lock_dir(new_dir);
 484         retval = affs_insert_hash(new_dir, bh_old);
 485         affs_unlock_dir(new_dir);
 486 
 487         /* Insert new into the old directory with the old name. */
 488         affs_copy_name(AFFS_TAIL(sb, bh_new)->name, old_dentry);
 489         affs_fix_checksum(sb, bh_new);
 490         affs_lock_dir(old_dir);
 491         retval = affs_insert_hash(old_dir, bh_new);
 492         affs_unlock_dir(old_dir);
 493 done:
 494         mark_buffer_dirty_inode(bh_old, new_dir);
 495         mark_buffer_dirty_inode(bh_new, old_dir);
 496         affs_brelse(bh_old);
 497         affs_brelse(bh_new);
 498         return retval;
 499 }
 500 
 501 int affs_rename2(struct inode *old_dir, struct dentry *old_dentry,
 502                         struct inode *new_dir, struct dentry *new_dentry,
 503                         unsigned int flags)
 504 {
 505 
 506         if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
 507                 return -EINVAL;
 508 
 509         pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__,
 510                  old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry);
 511 
 512         if (flags & RENAME_EXCHANGE)
 513                 return affs_xrename(old_dir, old_dentry, new_dir, new_dentry);
 514 
 515         return affs_rename(old_dir, old_dentry, new_dir, new_dentry);
 516 }
 517 
 518 static struct dentry *affs_get_parent(struct dentry *child)
 519 {
 520         struct inode *parent;
 521         struct buffer_head *bh;
 522 
 523         bh = affs_bread(child->d_sb, d_inode(child)->i_ino);
 524         if (!bh)
 525                 return ERR_PTR(-EIO);
 526 
 527         parent = affs_iget(child->d_sb,
 528                            be32_to_cpu(AFFS_TAIL(child->d_sb, bh)->parent));
 529         brelse(bh);
 530         if (IS_ERR(parent))
 531                 return ERR_CAST(parent);
 532 
 533         return d_obtain_alias(parent);
 534 }
 535 
 536 static struct inode *affs_nfs_get_inode(struct super_block *sb, u64 ino,
 537                                         u32 generation)
 538 {
 539         struct inode *inode;
 540 
 541         if (!affs_validblock(sb, ino))
 542                 return ERR_PTR(-ESTALE);
 543 
 544         inode = affs_iget(sb, ino);
 545         if (IS_ERR(inode))
 546                 return ERR_CAST(inode);
 547 
 548         return inode;
 549 }
 550 
 551 static struct dentry *affs_fh_to_dentry(struct super_block *sb, struct fid *fid,
 552                                         int fh_len, int fh_type)
 553 {
 554         return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
 555                                     affs_nfs_get_inode);
 556 }
 557 
 558 static struct dentry *affs_fh_to_parent(struct super_block *sb, struct fid *fid,
 559                                         int fh_len, int fh_type)
 560 {
 561         return generic_fh_to_parent(sb, fid, fh_len, fh_type,
 562                                     affs_nfs_get_inode);
 563 }
 564 
 565 const struct export_operations affs_export_ops = {
 566         .fh_to_dentry = affs_fh_to_dentry,
 567         .fh_to_parent = affs_fh_to_parent,
 568         .get_parent = affs_get_parent,
 569 };
 570 
 571 const struct dentry_operations affs_dentry_operations = {
 572         .d_hash         = affs_hash_dentry,
 573         .d_compare      = affs_compare_dentry,
 574 };
 575 
 576 const struct dentry_operations affs_intl_dentry_operations = {
 577         .d_hash         = affs_intl_hash_dentry,
 578         .d_compare      = affs_intl_compare_dentry,
 579 };

/* [<][>][^][v][top][bottom][index][help] */