root/fs/hfsplus/catalog.c

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

DEFINITIONS

This source file includes following definitions.
  1. hfsplus_cat_case_cmp_key
  2. hfsplus_cat_bin_cmp_key
  3. hfsplus_cat_build_key
  4. hfsplus_cat_build_key_with_cnid
  5. hfsplus_cat_build_key_uni
  6. hfsplus_cat_set_perms
  7. hfsplus_cat_build_record
  8. hfsplus_fill_cat_thread
  9. hfsplus_find_cat
  10. hfsplus_subfolders_inc
  11. hfsplus_subfolders_dec
  12. hfsplus_create_cat
  13. hfsplus_delete_cat
  14. hfsplus_rename_cat

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *  linux/fs/hfsplus/catalog.c
   4  *
   5  * Copyright (C) 2001
   6  * Brad Boyer (flar@allandria.com)
   7  * (C) 2003 Ardis Technologies <roman@ardistech.com>
   8  *
   9  * Handling of catalog records
  10  */
  11 
  12 
  13 #include "hfsplus_fs.h"
  14 #include "hfsplus_raw.h"
  15 
  16 int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
  17                              const hfsplus_btree_key *k2)
  18 {
  19         __be32 k1p, k2p;
  20 
  21         k1p = k1->cat.parent;
  22         k2p = k2->cat.parent;
  23         if (k1p != k2p)
  24                 return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
  25 
  26         return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
  27 }
  28 
  29 int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
  30                             const hfsplus_btree_key *k2)
  31 {
  32         __be32 k1p, k2p;
  33 
  34         k1p = k1->cat.parent;
  35         k2p = k2->cat.parent;
  36         if (k1p != k2p)
  37                 return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
  38 
  39         return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
  40 }
  41 
  42 /* Generates key for catalog file/folders record. */
  43 int hfsplus_cat_build_key(struct super_block *sb,
  44                 hfsplus_btree_key *key, u32 parent, const struct qstr *str)
  45 {
  46         int len, err;
  47 
  48         key->cat.parent = cpu_to_be32(parent);
  49         err = hfsplus_asc2uni(sb, &key->cat.name, HFSPLUS_MAX_STRLEN,
  50                         str->name, str->len);
  51         if (unlikely(err < 0))
  52                 return err;
  53 
  54         len = be16_to_cpu(key->cat.name.length);
  55         key->key_len = cpu_to_be16(6 + 2 * len);
  56         return 0;
  57 }
  58 
  59 /* Generates key for catalog thread record. */
  60 void hfsplus_cat_build_key_with_cnid(struct super_block *sb,
  61                         hfsplus_btree_key *key, u32 parent)
  62 {
  63         key->cat.parent = cpu_to_be32(parent);
  64         key->cat.name.length = 0;
  65         key->key_len = cpu_to_be16(6);
  66 }
  67 
  68 static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
  69                                       struct hfsplus_unistr *name)
  70 {
  71         int ustrlen;
  72 
  73         ustrlen = be16_to_cpu(name->length);
  74         key->cat.parent = cpu_to_be32(parent);
  75         key->cat.name.length = cpu_to_be16(ustrlen);
  76         ustrlen *= 2;
  77         memcpy(key->cat.name.unicode, name->unicode, ustrlen);
  78         key->key_len = cpu_to_be16(6 + ustrlen);
  79 }
  80 
  81 void hfsplus_cat_set_perms(struct inode *inode, struct hfsplus_perm *perms)
  82 {
  83         if (inode->i_flags & S_IMMUTABLE)
  84                 perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
  85         else
  86                 perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
  87         if (inode->i_flags & S_APPEND)
  88                 perms->rootflags |= HFSPLUS_FLG_APPEND;
  89         else
  90                 perms->rootflags &= ~HFSPLUS_FLG_APPEND;
  91 
  92         perms->userflags = HFSPLUS_I(inode)->userflags;
  93         perms->mode = cpu_to_be16(inode->i_mode);
  94         perms->owner = cpu_to_be32(i_uid_read(inode));
  95         perms->group = cpu_to_be32(i_gid_read(inode));
  96 
  97         if (S_ISREG(inode->i_mode))
  98                 perms->dev = cpu_to_be32(inode->i_nlink);
  99         else if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode))
 100                 perms->dev = cpu_to_be32(inode->i_rdev);
 101         else
 102                 perms->dev = 0;
 103 }
 104 
 105 static int hfsplus_cat_build_record(hfsplus_cat_entry *entry,
 106                 u32 cnid, struct inode *inode)
 107 {
 108         struct hfsplus_sb_info *sbi = HFSPLUS_SB(inode->i_sb);
 109 
 110         if (S_ISDIR(inode->i_mode)) {
 111                 struct hfsplus_cat_folder *folder;
 112 
 113                 folder = &entry->folder;
 114                 memset(folder, 0, sizeof(*folder));
 115                 folder->type = cpu_to_be16(HFSPLUS_FOLDER);
 116                 if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags))
 117                         folder->flags |= cpu_to_be16(HFSPLUS_HAS_FOLDER_COUNT);
 118                 folder->id = cpu_to_be32(inode->i_ino);
 119                 HFSPLUS_I(inode)->create_date =
 120                         folder->create_date =
 121                         folder->content_mod_date =
 122                         folder->attribute_mod_date =
 123                         folder->access_date = hfsp_now2mt();
 124                 hfsplus_cat_set_perms(inode, &folder->permissions);
 125                 if (inode == sbi->hidden_dir)
 126                         /* invisible and namelocked */
 127                         folder->user_info.frFlags = cpu_to_be16(0x5000);
 128                 return sizeof(*folder);
 129         } else {
 130                 struct hfsplus_cat_file *file;
 131 
 132                 file = &entry->file;
 133                 memset(file, 0, sizeof(*file));
 134                 file->type = cpu_to_be16(HFSPLUS_FILE);
 135                 file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
 136                 file->id = cpu_to_be32(cnid);
 137                 HFSPLUS_I(inode)->create_date =
 138                         file->create_date =
 139                         file->content_mod_date =
 140                         file->attribute_mod_date =
 141                         file->access_date = hfsp_now2mt();
 142                 if (cnid == inode->i_ino) {
 143                         hfsplus_cat_set_perms(inode, &file->permissions);
 144                         if (S_ISLNK(inode->i_mode)) {
 145                                 file->user_info.fdType =
 146                                         cpu_to_be32(HFSP_SYMLINK_TYPE);
 147                                 file->user_info.fdCreator =
 148                                         cpu_to_be32(HFSP_SYMLINK_CREATOR);
 149                         } else {
 150                                 file->user_info.fdType =
 151                                         cpu_to_be32(sbi->type);
 152                                 file->user_info.fdCreator =
 153                                         cpu_to_be32(sbi->creator);
 154                         }
 155                         if (HFSPLUS_FLG_IMMUTABLE &
 156                                         (file->permissions.rootflags |
 157                                         file->permissions.userflags))
 158                                 file->flags |=
 159                                         cpu_to_be16(HFSPLUS_FILE_LOCKED);
 160                 } else {
 161                         file->user_info.fdType =
 162                                 cpu_to_be32(HFSP_HARDLINK_TYPE);
 163                         file->user_info.fdCreator =
 164                                 cpu_to_be32(HFSP_HFSPLUS_CREATOR);
 165                         file->user_info.fdFlags =
 166                                 cpu_to_be16(0x100);
 167                         file->create_date =
 168                                 HFSPLUS_I(sbi->hidden_dir)->create_date;
 169                         file->permissions.dev =
 170                                 cpu_to_be32(HFSPLUS_I(inode)->linkid);
 171                 }
 172                 return sizeof(*file);
 173         }
 174 }
 175 
 176 static int hfsplus_fill_cat_thread(struct super_block *sb,
 177                                    hfsplus_cat_entry *entry, int type,
 178                                    u32 parentid, const struct qstr *str)
 179 {
 180         int err;
 181 
 182         entry->type = cpu_to_be16(type);
 183         entry->thread.reserved = 0;
 184         entry->thread.parentID = cpu_to_be32(parentid);
 185         err = hfsplus_asc2uni(sb, &entry->thread.nodeName, HFSPLUS_MAX_STRLEN,
 186                                 str->name, str->len);
 187         if (unlikely(err < 0))
 188                 return err;
 189 
 190         return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
 191 }
 192 
 193 /* Try to get a catalog entry for given catalog id */
 194 int hfsplus_find_cat(struct super_block *sb, u32 cnid,
 195                      struct hfs_find_data *fd)
 196 {
 197         hfsplus_cat_entry tmp;
 198         int err;
 199         u16 type;
 200 
 201         hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid);
 202         err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
 203         if (err)
 204                 return err;
 205 
 206         type = be16_to_cpu(tmp.type);
 207         if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
 208                 pr_err("found bad thread record in catalog\n");
 209                 return -EIO;
 210         }
 211 
 212         if (be16_to_cpu(tmp.thread.nodeName.length) > 255) {
 213                 pr_err("catalog name length corrupted\n");
 214                 return -EIO;
 215         }
 216 
 217         hfsplus_cat_build_key_uni(fd->search_key,
 218                 be32_to_cpu(tmp.thread.parentID),
 219                 &tmp.thread.nodeName);
 220         return hfs_brec_find(fd, hfs_find_rec_by_key);
 221 }
 222 
 223 static void hfsplus_subfolders_inc(struct inode *dir)
 224 {
 225         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 226 
 227         if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
 228                 /*
 229                  * Increment subfolder count. Note, the value is only meaningful
 230                  * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
 231                  */
 232                 HFSPLUS_I(dir)->subfolders++;
 233         }
 234 }
 235 
 236 static void hfsplus_subfolders_dec(struct inode *dir)
 237 {
 238         struct hfsplus_sb_info *sbi = HFSPLUS_SB(dir->i_sb);
 239 
 240         if (test_bit(HFSPLUS_SB_HFSX, &sbi->flags)) {
 241                 /*
 242                  * Decrement subfolder count. Note, the value is only meaningful
 243                  * for folders with HFSPLUS_HAS_FOLDER_COUNT flag set.
 244                  *
 245                  * Check for zero. Some subfolders may have been created
 246                  * by an implementation ignorant of this counter.
 247                  */
 248                 if (HFSPLUS_I(dir)->subfolders)
 249                         HFSPLUS_I(dir)->subfolders--;
 250         }
 251 }
 252 
 253 int hfsplus_create_cat(u32 cnid, struct inode *dir,
 254                 const struct qstr *str, struct inode *inode)
 255 {
 256         struct super_block *sb = dir->i_sb;
 257         struct hfs_find_data fd;
 258         hfsplus_cat_entry entry;
 259         int entry_size;
 260         int err;
 261 
 262         hfs_dbg(CAT_MOD, "create_cat: %s,%u(%d)\n",
 263                 str->name, cnid, inode->i_nlink);
 264         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 265         if (err)
 266                 return err;
 267 
 268         /*
 269          * Fail early and avoid ENOSPC during the btree operations. We may
 270          * have to split the root node at most once.
 271          */
 272         err = hfs_bmap_reserve(fd.tree, 2 * fd.tree->depth);
 273         if (err)
 274                 goto err2;
 275 
 276         hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 277         entry_size = hfsplus_fill_cat_thread(sb, &entry,
 278                 S_ISDIR(inode->i_mode) ?
 279                         HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
 280                 dir->i_ino, str);
 281         if (unlikely(entry_size < 0)) {
 282                 err = entry_size;
 283                 goto err2;
 284         }
 285 
 286         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 287         if (err != -ENOENT) {
 288                 if (!err)
 289                         err = -EEXIST;
 290                 goto err2;
 291         }
 292         err = hfs_brec_insert(&fd, &entry, entry_size);
 293         if (err)
 294                 goto err2;
 295 
 296         err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
 297         if (unlikely(err))
 298                 goto err1;
 299 
 300         entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
 301         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 302         if (err != -ENOENT) {
 303                 /* panic? */
 304                 if (!err)
 305                         err = -EEXIST;
 306                 goto err1;
 307         }
 308         err = hfs_brec_insert(&fd, &entry, entry_size);
 309         if (err)
 310                 goto err1;
 311 
 312         dir->i_size++;
 313         if (S_ISDIR(inode->i_mode))
 314                 hfsplus_subfolders_inc(dir);
 315         dir->i_mtime = dir->i_ctime = current_time(dir);
 316         hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
 317 
 318         hfs_find_exit(&fd);
 319         return 0;
 320 
 321 err1:
 322         hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 323         if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
 324                 hfs_brec_remove(&fd);
 325 err2:
 326         hfs_find_exit(&fd);
 327         return err;
 328 }
 329 
 330 int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
 331 {
 332         struct super_block *sb = dir->i_sb;
 333         struct hfs_find_data fd;
 334         struct hfsplus_fork_raw fork;
 335         struct list_head *pos;
 336         int err, off;
 337         u16 type;
 338 
 339         hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
 340         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 341         if (err)
 342                 return err;
 343 
 344         /*
 345          * Fail early and avoid ENOSPC during the btree operations. We may
 346          * have to split the root node at most once.
 347          */
 348         err = hfs_bmap_reserve(fd.tree, 2 * (int)fd.tree->depth - 2);
 349         if (err)
 350                 goto out;
 351 
 352         if (!str) {
 353                 int len;
 354 
 355                 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 356                 err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 357                 if (err)
 358                         goto out;
 359 
 360                 off = fd.entryoffset +
 361                         offsetof(struct hfsplus_cat_thread, nodeName);
 362                 fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
 363                 hfs_bnode_read(fd.bnode,
 364                         &fd.search_key->cat.name.length, off, 2);
 365                 len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
 366                 hfs_bnode_read(fd.bnode,
 367                         &fd.search_key->cat.name.unicode,
 368                         off + 2, len);
 369                 fd.search_key->key_len = cpu_to_be16(6 + len);
 370         } else {
 371                 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
 372                 if (unlikely(err))
 373                         goto out;
 374         }
 375 
 376         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 377         if (err)
 378                 goto out;
 379 
 380         type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
 381         if (type == HFSPLUS_FILE) {
 382 #if 0
 383                 off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
 384                 hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
 385                 hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
 386 #endif
 387 
 388                 off = fd.entryoffset +
 389                         offsetof(struct hfsplus_cat_file, rsrc_fork);
 390                 hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
 391                 hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
 392         }
 393 
 394         /* we only need to take spinlock for exclusion with ->release() */
 395         spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
 396         list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
 397                 struct hfsplus_readdir_data *rd =
 398                         list_entry(pos, struct hfsplus_readdir_data, list);
 399                 if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
 400                         rd->file->f_pos--;
 401         }
 402         spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
 403 
 404         err = hfs_brec_remove(&fd);
 405         if (err)
 406                 goto out;
 407 
 408         hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 409         err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 410         if (err)
 411                 goto out;
 412 
 413         err = hfs_brec_remove(&fd);
 414         if (err)
 415                 goto out;
 416 
 417         dir->i_size--;
 418         if (type == HFSPLUS_FOLDER)
 419                 hfsplus_subfolders_dec(dir);
 420         dir->i_mtime = dir->i_ctime = current_time(dir);
 421         hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
 422 
 423         if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
 424                 if (HFSPLUS_SB(sb)->attr_tree)
 425                         hfsplus_delete_all_attrs(dir, cnid);
 426         }
 427 
 428 out:
 429         hfs_find_exit(&fd);
 430 
 431         return err;
 432 }
 433 
 434 int hfsplus_rename_cat(u32 cnid,
 435                        struct inode *src_dir, const struct qstr *src_name,
 436                        struct inode *dst_dir, const struct qstr *dst_name)
 437 {
 438         struct super_block *sb = src_dir->i_sb;
 439         struct hfs_find_data src_fd, dst_fd;
 440         hfsplus_cat_entry entry;
 441         int entry_size, type;
 442         int err;
 443 
 444         hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
 445                 cnid, src_dir->i_ino, src_name->name,
 446                 dst_dir->i_ino, dst_name->name);
 447         err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
 448         if (err)
 449                 return err;
 450         dst_fd = src_fd;
 451 
 452         /*
 453          * Fail early and avoid ENOSPC during the btree operations. We may
 454          * have to split the root node at most twice.
 455          */
 456         err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1);
 457         if (err)
 458                 goto out;
 459 
 460         /* find the old dir entry and read the data */
 461         err = hfsplus_cat_build_key(sb, src_fd.search_key,
 462                         src_dir->i_ino, src_name);
 463         if (unlikely(err))
 464                 goto out;
 465 
 466         err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 467         if (err)
 468                 goto out;
 469         if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
 470                 err = -EIO;
 471                 goto out;
 472         }
 473 
 474         hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
 475                                 src_fd.entrylength);
 476         type = be16_to_cpu(entry.type);
 477 
 478         /* create new dir entry with the data from the old entry */
 479         err = hfsplus_cat_build_key(sb, dst_fd.search_key,
 480                         dst_dir->i_ino, dst_name);
 481         if (unlikely(err))
 482                 goto out;
 483 
 484         err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
 485         if (err != -ENOENT) {
 486                 if (!err)
 487                         err = -EEXIST;
 488                 goto out;
 489         }
 490 
 491         err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
 492         if (err)
 493                 goto out;
 494         dst_dir->i_size++;
 495         if (type == HFSPLUS_FOLDER)
 496                 hfsplus_subfolders_inc(dst_dir);
 497         dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
 498 
 499         /* finally remove the old entry */
 500         err = hfsplus_cat_build_key(sb, src_fd.search_key,
 501                         src_dir->i_ino, src_name);
 502         if (unlikely(err))
 503                 goto out;
 504 
 505         err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 506         if (err)
 507                 goto out;
 508         err = hfs_brec_remove(&src_fd);
 509         if (err)
 510                 goto out;
 511         src_dir->i_size--;
 512         if (type == HFSPLUS_FOLDER)
 513                 hfsplus_subfolders_dec(src_dir);
 514         src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
 515 
 516         /* remove old thread entry */
 517         hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
 518         err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 519         if (err)
 520                 goto out;
 521         type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
 522         err = hfs_brec_remove(&src_fd);
 523         if (err)
 524                 goto out;
 525 
 526         /* create new thread entry */
 527         hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
 528         entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
 529                 dst_dir->i_ino, dst_name);
 530         if (unlikely(entry_size < 0)) {
 531                 err = entry_size;
 532                 goto out;
 533         }
 534 
 535         err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
 536         if (err != -ENOENT) {
 537                 if (!err)
 538                         err = -EEXIST;
 539                 goto out;
 540         }
 541         err = hfs_brec_insert(&dst_fd, &entry, entry_size);
 542 
 543         hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
 544         hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
 545 out:
 546         hfs_bnode_put(dst_fd.bnode);
 547         hfs_find_exit(&src_fd);
 548         return err;
 549 }

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