root/fs/hfsplus/dir.c

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

DEFINITIONS

This source file includes following definitions.
  1. hfsplus_instantiate
  2. hfsplus_lookup
  3. hfsplus_readdir
  4. hfsplus_dir_release
  5. hfsplus_link
  6. hfsplus_unlink
  7. hfsplus_rmdir
  8. hfsplus_symlink
  9. hfsplus_mknod
  10. hfsplus_create
  11. hfsplus_mkdir
  12. hfsplus_rename

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  linux/fs/hfsplus/dir.c
   4  *
   5  * Copyright (C) 2001
   6  * Brad Boyer (flar@allandria.com)
   7  * (C) 2003 Ardis Technologies <roman@ardistech.com>
   8  *
   9  * Handling of directories
  10  */
  11 
  12 #include <linux/errno.h>
  13 #include <linux/fs.h>
  14 #include <linux/slab.h>
  15 #include <linux/random.h>
  16 #include <linux/nls.h>
  17 
  18 #include "hfsplus_fs.h"
  19 #include "hfsplus_raw.h"
  20 #include "xattr.h"
  21 
  22 static inline void hfsplus_instantiate(struct dentry *dentry,
  23                                        struct inode *inode, u32 cnid)
  24 {
  25         dentry->d_fsdata = (void *)(unsigned long)cnid;
  26         d_instantiate(dentry, inode);
  27 }
  28 
  29 /* Find the entry inside dir named dentry->d_name */
  30 static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
  31                                      unsigned int flags)
  32 {
  33         struct inode *inode = NULL;
  34         struct hfs_find_data fd;
  35         struct super_block *sb;
  36         hfsplus_cat_entry entry;
  37         int err;
  38         u32 cnid, linkid = 0;
  39         u16 type;
  40 
  41         sb = dir->i_sb;
  42 
  43         dentry->d_fsdata = NULL;
  44         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
  45         if (err)
  46                 return ERR_PTR(err);
  47         err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino,
  48                         &dentry->d_name);
  49         if (unlikely(err < 0))
  50                 goto fail;
  51 again:
  52         err = hfs_brec_read(&fd, &entry, sizeof(entry));
  53         if (err) {
  54                 if (err == -ENOENT) {
  55                         hfs_find_exit(&fd);
  56                         /* No such entry */
  57                         inode = NULL;
  58                         goto out;
  59                 }
  60                 goto fail;
  61         }
  62         type = be16_to_cpu(entry.type);
  63         if (type == HFSPLUS_FOLDER) {
  64                 if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
  65                         err = -EIO;
  66                         goto fail;
  67                 }
  68                 cnid = be32_to_cpu(entry.folder.id);
  69                 dentry->d_fsdata = (void *)(unsigned long)cnid;
  70         } else if (type == HFSPLUS_FILE) {
  71                 if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
  72                         err = -EIO;
  73                         goto fail;
  74                 }
  75                 cnid = be32_to_cpu(entry.file.id);
  76                 if (entry.file.user_info.fdType ==
  77                                 cpu_to_be32(HFSP_HARDLINK_TYPE) &&
  78                                 entry.file.user_info.fdCreator ==
  79                                 cpu_to_be32(HFSP_HFSPLUS_CREATOR) &&
  80                                 HFSPLUS_SB(sb)->hidden_dir &&
  81                                 (entry.file.create_date ==
  82                                         HFSPLUS_I(HFSPLUS_SB(sb)->hidden_dir)->
  83                                                 create_date ||
  84                                 entry.file.create_date ==
  85                                         HFSPLUS_I(d_inode(sb->s_root))->
  86                                                 create_date)) {
  87                         struct qstr str;
  88                         char name[32];
  89 
  90                         if (dentry->d_fsdata) {
  91                                 /*
  92                                  * We found a link pointing to another link,
  93                                  * so ignore it and treat it as regular file.
  94                                  */
  95                                 cnid = (unsigned long)dentry->d_fsdata;
  96                                 linkid = 0;
  97                         } else {
  98                                 dentry->d_fsdata = (void *)(unsigned long)cnid;
  99                                 linkid =
 100                                         be32_to_cpu(entry.file.permissions.dev);
 101                                 str.len = sprintf(name, "iNode%d", linkid);
 102                                 str.name = name;
 103                                 err = hfsplus_cat_build_key(sb, fd.search_key,
 104                                         HFSPLUS_SB(sb)->hidden_dir->i_ino,
 105                                         &str);
 106                                 if (unlikely(err < 0))
 107                                         goto fail;
 108                                 goto again;
 109                         }
 110                 } else if (!dentry->d_fsdata)
 111                         dentry->d_fsdata = (void *)(unsigned long)cnid;
 112         } else {
 113                 pr_err("invalid catalog entry type in lookup\n");
 114                 err = -EIO;
 115                 goto fail;
 116         }
 117         hfs_find_exit(&fd);
 118         inode = hfsplus_iget(dir->i_sb, cnid);
 119         if (IS_ERR(inode))
 120                 return ERR_CAST(inode);
 121         if (S_ISREG(inode->i_mode))
 122                 HFSPLUS_I(inode)->linkid = linkid;
 123 out:
 124         return d_splice_alias(inode, dentry);
 125 fail:
 126         hfs_find_exit(&fd);
 127         return ERR_PTR(err);
 128 }
 129 
 130 static int hfsplus_readdir(struct file *file, struct dir_context *ctx)
 131 {
 132         struct inode *inode = file_inode(file);
 133         struct super_block *sb = inode->i_sb;
 134         int len, err;
 135         char *strbuf;
 136         hfsplus_cat_entry entry;
 137         struct hfs_find_data fd;
 138         struct hfsplus_readdir_data *rd;
 139         u16 type;
 140 
 141         if (file->f_pos >= inode->i_size)
 142                 return 0;
 143 
 144         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 145         if (err)
 146                 return err;
 147         strbuf = kmalloc(NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN + 1, GFP_KERNEL);
 148         if (!strbuf) {
 149                 err = -ENOMEM;
 150                 goto out;
 151         }
 152         hfsplus_cat_build_key_with_cnid(sb, fd.search_key, inode->i_ino);
 153         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 154         if (err)
 155                 goto out;
 156 
 157         if (ctx->pos == 0) {
 158                 /* This is completely artificial... */
 159                 if (!dir_emit_dot(file, ctx))
 160                         goto out;
 161                 ctx->pos = 1;
 162         }
 163         if (ctx->pos == 1) {
 164                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
 165                         err = -EIO;
 166                         goto out;
 167                 }
 168 
 169                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
 170                         fd.entrylength);
 171                 if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
 172                         pr_err("bad catalog folder thread\n");
 173                         err = -EIO;
 174                         goto out;
 175                 }
 176                 if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
 177                         pr_err("truncated catalog thread\n");
 178                         err = -EIO;
 179                         goto out;
 180                 }
 181                 if (!dir_emit(ctx, "..", 2,
 182                             be32_to_cpu(entry.thread.parentID), DT_DIR))
 183                         goto out;
 184                 ctx->pos = 2;
 185         }
 186         if (ctx->pos >= inode->i_size)
 187                 goto out;
 188         err = hfs_brec_goto(&fd, ctx->pos - 1);
 189         if (err)
 190                 goto out;
 191         for (;;) {
 192                 if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
 193                         pr_err("walked past end of dir\n");
 194                         err = -EIO;
 195                         goto out;
 196                 }
 197 
 198                 if (fd.entrylength > sizeof(entry) || fd.entrylength < 0) {
 199                         err = -EIO;
 200                         goto out;
 201                 }
 202 
 203                 hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
 204                         fd.entrylength);
 205                 type = be16_to_cpu(entry.type);
 206                 len = NLS_MAX_CHARSET_SIZE * HFSPLUS_MAX_STRLEN;
 207                 err = hfsplus_uni2asc(sb, &fd.key->cat.name, strbuf, &len);
 208                 if (err)
 209                         goto out;
 210                 if (type == HFSPLUS_FOLDER) {
 211                         if (fd.entrylength <
 212                                         sizeof(struct hfsplus_cat_folder)) {
 213                                 pr_err("small dir entry\n");
 214                                 err = -EIO;
 215                                 goto out;
 216                         }
 217                         if (HFSPLUS_SB(sb)->hidden_dir &&
 218                             HFSPLUS_SB(sb)->hidden_dir->i_ino ==
 219                                         be32_to_cpu(entry.folder.id))
 220                                 goto next;
 221                         if (!dir_emit(ctx, strbuf, len,
 222                                     be32_to_cpu(entry.folder.id), DT_DIR))
 223                                 break;
 224                 } else if (type == HFSPLUS_FILE) {
 225                         u16 mode;
 226                         unsigned type = DT_UNKNOWN;
 227 
 228                         if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
 229                                 pr_err("small file entry\n");
 230                                 err = -EIO;
 231                                 goto out;
 232                         }
 233 
 234                         mode = be16_to_cpu(entry.file.permissions.mode);
 235                         if (S_ISREG(mode))
 236                                 type = DT_REG;
 237                         else if (S_ISLNK(mode))
 238                                 type = DT_LNK;
 239                         else if (S_ISFIFO(mode))
 240                                 type = DT_FIFO;
 241                         else if (S_ISCHR(mode))
 242                                 type = DT_CHR;
 243                         else if (S_ISBLK(mode))
 244                                 type = DT_BLK;
 245                         else if (S_ISSOCK(mode))
 246                                 type = DT_SOCK;
 247 
 248                         if (!dir_emit(ctx, strbuf, len,
 249                                       be32_to_cpu(entry.file.id), type))
 250                                 break;
 251                 } else {
 252                         pr_err("bad catalog entry type\n");
 253                         err = -EIO;
 254                         goto out;
 255                 }
 256 next:
 257                 ctx->pos++;
 258                 if (ctx->pos >= inode->i_size)
 259                         goto out;
 260                 err = hfs_brec_goto(&fd, 1);
 261                 if (err)
 262                         goto out;
 263         }
 264         rd = file->private_data;
 265         if (!rd) {
 266                 rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
 267                 if (!rd) {
 268                         err = -ENOMEM;
 269                         goto out;
 270                 }
 271                 file->private_data = rd;
 272                 rd->file = file;
 273                 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
 274                 list_add(&rd->list, &HFSPLUS_I(inode)->open_dir_list);
 275                 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
 276         }
 277         /*
 278          * Can be done after the list insertion; exclusion with
 279          * hfsplus_delete_cat() is provided by directory lock.
 280          */
 281         memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
 282 out:
 283         kfree(strbuf);
 284         hfs_find_exit(&fd);
 285         return err;
 286 }
 287 
 288 static int hfsplus_dir_release(struct inode *inode, struct file *file)
 289 {
 290         struct hfsplus_readdir_data *rd = file->private_data;
 291         if (rd) {
 292                 spin_lock(&HFSPLUS_I(inode)->open_dir_lock);
 293                 list_del(&rd->list);
 294                 spin_unlock(&HFSPLUS_I(inode)->open_dir_lock);
 295                 kfree(rd);
 296         }
 297         return 0;
 298 }
 299 
 300 static int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir,
 301                         struct dentry *dst_dentry)
 302 {
 303         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dst_dir->i_sb);
 304         struct inode *inode = d_inode(src_dentry);
 305         struct inode *src_dir = d_inode(src_dentry->d_parent);
 306         struct qstr str;
 307         char name[32];
 308         u32 cnid, id;
 309         int res;
 310 
 311         if (HFSPLUS_IS_RSRC(inode))
 312                 return -EPERM;
 313         if (!S_ISREG(inode->i_mode))
 314                 return -EPERM;
 315 
 316         mutex_lock(&sbi->vh_mutex);
 317         if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
 318                 for (;;) {
 319                         get_random_bytes(&id, sizeof(cnid));
 320                         id &= 0x3fffffff;
 321                         str.name = name;
 322                         str.len = sprintf(name, "iNode%d", id);
 323                         res = hfsplus_rename_cat(inode->i_ino,
 324                                                  src_dir, &src_dentry->d_name,
 325                                                  sbi->hidden_dir, &str);
 326                         if (!res)
 327                                 break;
 328                         if (res != -EEXIST)
 329                                 goto out;
 330                 }
 331                 HFSPLUS_I(inode)->linkid = id;
 332                 cnid = sbi->next_cnid++;
 333                 src_dentry->d_fsdata = (void *)(unsigned long)cnid;
 334                 res = hfsplus_create_cat(cnid, src_dir,
 335                         &src_dentry->d_name, inode);
 336                 if (res)
 337                         /* panic? */
 338                         goto out;
 339                 sbi->file_count++;
 340         }
 341         cnid = sbi->next_cnid++;
 342         res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
 343         if (res)
 344                 goto out;
 345 
 346         inc_nlink(inode);
 347         hfsplus_instantiate(dst_dentry, inode, cnid);
 348         ihold(inode);
 349         inode->i_ctime = current_time(inode);
 350         mark_inode_dirty(inode);
 351         sbi->file_count++;
 352         hfsplus_mark_mdb_dirty(dst_dir->i_sb);
 353 out:
 354         mutex_unlock(&sbi->vh_mutex);
 355         return res;
 356 }
 357 
 358 static int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
 359 {
 360         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 361         struct inode *inode = d_inode(dentry);
 362         struct qstr str;
 363         char name[32];
 364         u32 cnid;
 365         int res;
 366 
 367         if (HFSPLUS_IS_RSRC(inode))
 368                 return -EPERM;
 369 
 370         mutex_lock(&sbi->vh_mutex);
 371         cnid = (u32)(unsigned long)dentry->d_fsdata;
 372         if (inode->i_ino == cnid &&
 373             atomic_read(&HFSPLUS_I(inode)->opencnt)) {
 374                 str.name = name;
 375                 str.len = sprintf(name, "temp%lu", inode->i_ino);
 376                 res = hfsplus_rename_cat(inode->i_ino,
 377                                          dir, &dentry->d_name,
 378                                          sbi->hidden_dir, &str);
 379                 if (!res) {
 380                         inode->i_flags |= S_DEAD;
 381                         drop_nlink(inode);
 382                 }
 383                 goto out;
 384         }
 385         res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
 386         if (res)
 387                 goto out;
 388 
 389         if (inode->i_nlink > 0)
 390                 drop_nlink(inode);
 391         if (inode->i_ino == cnid)
 392                 clear_nlink(inode);
 393         if (!inode->i_nlink) {
 394                 if (inode->i_ino != cnid) {
 395                         sbi->file_count--;
 396                         if (!atomic_read(&HFSPLUS_I(inode)->opencnt)) {
 397                                 res = hfsplus_delete_cat(inode->i_ino,
 398                                                          sbi->hidden_dir,
 399                                                          NULL);
 400                                 if (!res)
 401                                         hfsplus_delete_inode(inode);
 402                         } else
 403                                 inode->i_flags |= S_DEAD;
 404                 } else
 405                         hfsplus_delete_inode(inode);
 406         } else
 407                 sbi->file_count--;
 408         inode->i_ctime = current_time(inode);
 409         mark_inode_dirty(inode);
 410 out:
 411         mutex_unlock(&sbi->vh_mutex);
 412         return res;
 413 }
 414 
 415 static int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
 416 {
 417         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 418         struct inode *inode = d_inode(dentry);
 419         int res;
 420 
 421         if (inode->i_size != 2)
 422                 return -ENOTEMPTY;
 423 
 424         mutex_lock(&sbi->vh_mutex);
 425         res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 426         if (res)
 427                 goto out;
 428         clear_nlink(inode);
 429         inode->i_ctime = current_time(inode);
 430         hfsplus_delete_inode(inode);
 431         mark_inode_dirty(inode);
 432 out:
 433         mutex_unlock(&sbi->vh_mutex);
 434         return res;
 435 }
 436 
 437 static int hfsplus_symlink(struct inode *dir, struct dentry *dentry,
 438                            const char *symname)
 439 {
 440         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 441         struct inode *inode;
 442         int res = -ENOMEM;
 443 
 444         mutex_lock(&sbi->vh_mutex);
 445         inode = hfsplus_new_inode(dir->i_sb, dir, S_IFLNK | S_IRWXUGO);
 446         if (!inode)
 447                 goto out;
 448 
 449         res = page_symlink(inode, symname, strlen(symname) + 1);
 450         if (res)
 451                 goto out_err;
 452 
 453         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
 454         if (res)
 455                 goto out_err;
 456 
 457         res = hfsplus_init_security(inode, dir, &dentry->d_name);
 458         if (res == -EOPNOTSUPP)
 459                 res = 0; /* Operation is not supported. */
 460         else if (res) {
 461                 /* Try to delete anyway without error analysis. */
 462                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 463                 goto out_err;
 464         }
 465 
 466         hfsplus_instantiate(dentry, inode, inode->i_ino);
 467         mark_inode_dirty(inode);
 468         goto out;
 469 
 470 out_err:
 471         clear_nlink(inode);
 472         hfsplus_delete_inode(inode);
 473         iput(inode);
 474 out:
 475         mutex_unlock(&sbi->vh_mutex);
 476         return res;
 477 }
 478 
 479 static int hfsplus_mknod(struct inode *dir, struct dentry *dentry,
 480                          umode_t mode, dev_t rdev)
 481 {
 482         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 483         struct inode *inode;
 484         int res = -ENOMEM;
 485 
 486         mutex_lock(&sbi->vh_mutex);
 487         inode = hfsplus_new_inode(dir->i_sb, dir, mode);
 488         if (!inode)
 489                 goto out;
 490 
 491         if (S_ISBLK(mode) || S_ISCHR(mode) || S_ISFIFO(mode) || S_ISSOCK(mode))
 492                 init_special_inode(inode, mode, rdev);
 493 
 494         res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
 495         if (res)
 496                 goto failed_mknod;
 497 
 498         res = hfsplus_init_security(inode, dir, &dentry->d_name);
 499         if (res == -EOPNOTSUPP)
 500                 res = 0; /* Operation is not supported. */
 501         else if (res) {
 502                 /* Try to delete anyway without error analysis. */
 503                 hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
 504                 goto failed_mknod;
 505         }
 506 
 507         hfsplus_instantiate(dentry, inode, inode->i_ino);
 508         mark_inode_dirty(inode);
 509         goto out;
 510 
 511 failed_mknod:
 512         clear_nlink(inode);
 513         hfsplus_delete_inode(inode);
 514         iput(inode);
 515 out:
 516         mutex_unlock(&sbi->vh_mutex);
 517         return res;
 518 }
 519 
 520 static int hfsplus_create(struct inode *dir, struct dentry *dentry, umode_t mode,
 521                           bool excl)
 522 {
 523         return hfsplus_mknod(dir, dentry, mode, 0);
 524 }
 525 
 526 static int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
 527 {
 528         return hfsplus_mknod(dir, dentry, mode | S_IFDIR, 0);
 529 }
 530 
 531 static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
 532                           struct inode *new_dir, struct dentry *new_dentry,
 533                           unsigned int flags)
 534 {
 535         int res;
 536 
 537         if (flags & ~RENAME_NOREPLACE)
 538                 return -EINVAL;
 539 
 540         /* Unlink destination if it already exists */
 541         if (d_really_is_positive(new_dentry)) {
 542                 if (d_is_dir(new_dentry))
 543                         res = hfsplus_rmdir(new_dir, new_dentry);
 544                 else
 545                         res = hfsplus_unlink(new_dir, new_dentry);
 546                 if (res)
 547                         return res;
 548         }
 549 
 550         res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
 551                                  old_dir, &old_dentry->d_name,
 552                                  new_dir, &new_dentry->d_name);
 553         if (!res)
 554                 new_dentry->d_fsdata = old_dentry->d_fsdata;
 555         return res;
 556 }
 557 
 558 const struct inode_operations hfsplus_dir_inode_operations = {
 559         .lookup                 = hfsplus_lookup,
 560         .create                 = hfsplus_create,
 561         .link                   = hfsplus_link,
 562         .unlink                 = hfsplus_unlink,
 563         .mkdir                  = hfsplus_mkdir,
 564         .rmdir                  = hfsplus_rmdir,
 565         .symlink                = hfsplus_symlink,
 566         .mknod                  = hfsplus_mknod,
 567         .rename                 = hfsplus_rename,
 568         .getattr                = hfsplus_getattr,
 569         .listxattr              = hfsplus_listxattr,
 570 };
 571 
 572 const struct file_operations hfsplus_dir_operations = {
 573         .fsync          = hfsplus_file_fsync,
 574         .read           = generic_read_dir,
 575         .iterate_shared = hfsplus_readdir,
 576         .unlocked_ioctl = hfsplus_ioctl,
 577         .llseek         = generic_file_llseek,
 578         .release        = hfsplus_dir_release,
 579 };

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