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        hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 269        entry_size = hfsplus_fill_cat_thread(sb, &entry,
 270                S_ISDIR(inode->i_mode) ?
 271                        HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
 272                dir->i_ino, str);
 273        if (unlikely(entry_size < 0)) {
 274                err = entry_size;
 275                goto err2;
 276        }
 277
 278        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 279        if (err != -ENOENT) {
 280                if (!err)
 281                        err = -EEXIST;
 282                goto err2;
 283        }
 284        err = hfs_brec_insert(&fd, &entry, entry_size);
 285        if (err)
 286                goto err2;
 287
 288        err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
 289        if (unlikely(err))
 290                goto err1;
 291
 292        entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
 293        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 294        if (err != -ENOENT) {
 295                /* panic? */
 296                if (!err)
 297                        err = -EEXIST;
 298                goto err1;
 299        }
 300        err = hfs_brec_insert(&fd, &entry, entry_size);
 301        if (err)
 302                goto err1;
 303
 304        dir->i_size++;
 305        if (S_ISDIR(inode->i_mode))
 306                hfsplus_subfolders_inc(dir);
 307        dir->i_mtime = dir->i_ctime = current_time(dir);
 308        hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
 309
 310        hfs_find_exit(&fd);
 311        return 0;
 312
 313err1:
 314        hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 315        if (!hfs_brec_find(&fd, hfs_find_rec_by_key))
 316                hfs_brec_remove(&fd);
 317err2:
 318        hfs_find_exit(&fd);
 319        return err;
 320}
 321
 322int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str)
 323{
 324        struct super_block *sb = dir->i_sb;
 325        struct hfs_find_data fd;
 326        struct hfsplus_fork_raw fork;
 327        struct list_head *pos;
 328        int err, off;
 329        u16 type;
 330
 331        hfs_dbg(CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
 332        err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &fd);
 333        if (err)
 334                return err;
 335
 336        if (!str) {
 337                int len;
 338
 339                hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 340                err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 341                if (err)
 342                        goto out;
 343
 344                off = fd.entryoffset +
 345                        offsetof(struct hfsplus_cat_thread, nodeName);
 346                fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
 347                hfs_bnode_read(fd.bnode,
 348                        &fd.search_key->cat.name.length, off, 2);
 349                len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
 350                hfs_bnode_read(fd.bnode,
 351                        &fd.search_key->cat.name.unicode,
 352                        off + 2, len);
 353                fd.search_key->key_len = cpu_to_be16(6 + len);
 354        } else {
 355                err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str);
 356                if (unlikely(err))
 357                        goto out;
 358        }
 359
 360        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 361        if (err)
 362                goto out;
 363
 364        type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
 365        if (type == HFSPLUS_FILE) {
 366#if 0
 367                off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
 368                hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
 369                hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
 370#endif
 371
 372                off = fd.entryoffset +
 373                        offsetof(struct hfsplus_cat_file, rsrc_fork);
 374                hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
 375                hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
 376        }
 377
 378        /* we only need to take spinlock for exclusion with ->release() */
 379        spin_lock(&HFSPLUS_I(dir)->open_dir_lock);
 380        list_for_each(pos, &HFSPLUS_I(dir)->open_dir_list) {
 381                struct hfsplus_readdir_data *rd =
 382                        list_entry(pos, struct hfsplus_readdir_data, list);
 383                if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
 384                        rd->file->f_pos--;
 385        }
 386        spin_unlock(&HFSPLUS_I(dir)->open_dir_lock);
 387
 388        err = hfs_brec_remove(&fd);
 389        if (err)
 390                goto out;
 391
 392        hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid);
 393        err = hfs_brec_find(&fd, hfs_find_rec_by_key);
 394        if (err)
 395                goto out;
 396
 397        err = hfs_brec_remove(&fd);
 398        if (err)
 399                goto out;
 400
 401        dir->i_size--;
 402        if (type == HFSPLUS_FOLDER)
 403                hfsplus_subfolders_dec(dir);
 404        dir->i_mtime = dir->i_ctime = current_time(dir);
 405        hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY);
 406
 407        if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) {
 408                if (HFSPLUS_SB(sb)->attr_tree)
 409                        hfsplus_delete_all_attrs(dir, cnid);
 410        }
 411
 412out:
 413        hfs_find_exit(&fd);
 414
 415        return err;
 416}
 417
 418int hfsplus_rename_cat(u32 cnid,
 419                       struct inode *src_dir, const struct qstr *src_name,
 420                       struct inode *dst_dir, const struct qstr *dst_name)
 421{
 422        struct super_block *sb = src_dir->i_sb;
 423        struct hfs_find_data src_fd, dst_fd;
 424        hfsplus_cat_entry entry;
 425        int entry_size, type;
 426        int err;
 427
 428        hfs_dbg(CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n",
 429                cnid, src_dir->i_ino, src_name->name,
 430                dst_dir->i_ino, dst_name->name);
 431        err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd);
 432        if (err)
 433                return err;
 434        dst_fd = src_fd;
 435
 436        /* find the old dir entry and read the data */
 437        err = hfsplus_cat_build_key(sb, src_fd.search_key,
 438                        src_dir->i_ino, src_name);
 439        if (unlikely(err))
 440                goto out;
 441
 442        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 443        if (err)
 444                goto out;
 445        if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) {
 446                err = -EIO;
 447                goto out;
 448        }
 449
 450        hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
 451                                src_fd.entrylength);
 452        type = be16_to_cpu(entry.type);
 453
 454        /* create new dir entry with the data from the old entry */
 455        err = hfsplus_cat_build_key(sb, dst_fd.search_key,
 456                        dst_dir->i_ino, dst_name);
 457        if (unlikely(err))
 458                goto out;
 459
 460        err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
 461        if (err != -ENOENT) {
 462                if (!err)
 463                        err = -EEXIST;
 464                goto out;
 465        }
 466
 467        err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
 468        if (err)
 469                goto out;
 470        dst_dir->i_size++;
 471        if (type == HFSPLUS_FOLDER)
 472                hfsplus_subfolders_inc(dst_dir);
 473        dst_dir->i_mtime = dst_dir->i_ctime = current_time(dst_dir);
 474
 475        /* finally remove the old entry */
 476        err = hfsplus_cat_build_key(sb, src_fd.search_key,
 477                        src_dir->i_ino, src_name);
 478        if (unlikely(err))
 479                goto out;
 480
 481        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 482        if (err)
 483                goto out;
 484        err = hfs_brec_remove(&src_fd);
 485        if (err)
 486                goto out;
 487        src_dir->i_size--;
 488        if (type == HFSPLUS_FOLDER)
 489                hfsplus_subfolders_dec(src_dir);
 490        src_dir->i_mtime = src_dir->i_ctime = current_time(src_dir);
 491
 492        /* remove old thread entry */
 493        hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid);
 494        err = hfs_brec_find(&src_fd, hfs_find_rec_by_key);
 495        if (err)
 496                goto out;
 497        type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
 498        err = hfs_brec_remove(&src_fd);
 499        if (err)
 500                goto out;
 501
 502        /* create new thread entry */
 503        hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid);
 504        entry_size = hfsplus_fill_cat_thread(sb, &entry, type,
 505                dst_dir->i_ino, dst_name);
 506        if (unlikely(entry_size < 0)) {
 507                err = entry_size;
 508                goto out;
 509        }
 510
 511        err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key);
 512        if (err != -ENOENT) {
 513                if (!err)
 514                        err = -EEXIST;
 515                goto out;
 516        }
 517        err = hfs_brec_insert(&dst_fd, &entry, entry_size);
 518
 519        hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY);
 520        hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY);
 521out:
 522        hfs_bnode_put(dst_fd.bnode);
 523        hfs_find_exit(&src_fd);
 524        return err;
 525}
 526