linux/fs/hfsplus/catalog.c
<<
>>
Prefs
   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
  16int 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
  29int 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. */
  43int 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. */
  60void 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
  68static 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
  81void 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
 105static 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
 176static 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 */
 194int 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
 223static 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
 236static 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
 253int 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
 321err1:
 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);
 325err2:
 326        hfs_find_exit(&fd);
 327        return err;
 328}
 329
 330int 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
 428out:
 429        hfs_find_exit(&fd);
 430
 431        return err;
 432}
 433
 434int 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);
 545out:
 546        hfs_bnode_put(dst_fd.bnode);
 547        hfs_find_exit(&src_fd);
 548        return err;
 549}
 550