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, HFS_REGULAR_NAME); 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, HFS_REGULAR_NAME); 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 = {0}; 198 int err; 199 u16 type; 200 201 hfsplus_cat_build_key_with_cnid(sb, fd->search_key, cnid); 202 err = hfsplus_brec_read_cat(fd, &tmp); 203 if (err) 204 return err; 205 206 type = be16_to_cpu(tmp.type); 207 if (!is_hfs_thread_record_type(type)) { 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("name %s, cnid %u, i_nlink %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 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 316 hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 317 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); 318 319 hfs_find_exit(&fd); 320 return 0; 321 322 err1: 323 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid); 324 if (!hfs_brec_find(&fd, hfs_find_rec_by_key)) 325 hfs_brec_remove(&fd); 326 err2: 327 hfs_find_exit(&fd); 328 return err; 329 } 330 331 int hfsplus_delete_cat(u32 cnid, struct inode *dir, const struct qstr *str) 332 { 333 struct super_block *sb = dir->i_sb; 334 struct hfs_find_data fd; 335 struct hfsplus_fork_raw fork; 336 int err, off; 337 u16 type; 338 339 hfs_dbg("name %s, cnid %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 hfsplus_cat_entry entry = {0}; 354 355 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid); 356 err = hfsplus_brec_read_cat(&fd, &entry); 357 if (err) 358 goto out; 359 360 type = be16_to_cpu(entry.type); 361 if (!is_hfs_thread_record_type(type)) { 362 pr_err("found bad thread record in catalog\n"); 363 err = -EIO; 364 goto out; 365 } 366 367 hfsplus_cat_build_key_uni(fd.search_key, dir->i_ino, 368 &entry.thread.nodeName); 369 } else { 370 err = hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, str); 371 if (unlikely(err)) 372 goto out; 373 } 374 375 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 376 if (err) 377 goto out; 378 379 type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset); 380 if (type == HFSPLUS_FILE) { 381 #if 0 382 off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork); 383 hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork)); 384 hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA); 385 #endif 386 387 off = fd.entryoffset + 388 offsetof(struct hfsplus_cat_file, rsrc_fork); 389 hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork)); 390 hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC); 391 } 392 393 err = hfs_brec_remove(&fd); 394 if (err) 395 goto out; 396 397 hfsplus_cat_build_key_with_cnid(sb, fd.search_key, cnid); 398 err = hfs_brec_find(&fd, hfs_find_rec_by_key); 399 if (err) 400 goto out; 401 402 err = hfs_brec_remove(&fd); 403 if (err) 404 goto out; 405 406 dir->i_size--; 407 if (type == HFSPLUS_FOLDER) 408 hfsplus_subfolders_dec(dir); 409 inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir)); 410 hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 411 hfsplus_mark_inode_dirty(dir, HFSPLUS_I_CAT_DIRTY); 412 413 if (type == HFSPLUS_FILE || type == HFSPLUS_FOLDER) { 414 if (HFSPLUS_SB(sb)->attr_tree) 415 hfsplus_delete_all_attrs(dir, cnid); 416 } 417 418 out: 419 hfs_find_exit(&fd); 420 421 return err; 422 } 423 424 int hfsplus_rename_cat(u32 cnid, 425 struct inode *src_dir, const struct qstr *src_name, 426 struct inode *dst_dir, const struct qstr *dst_name) 427 { 428 struct super_block *sb = src_dir->i_sb; 429 struct hfs_find_data src_fd, dst_fd; 430 hfsplus_cat_entry entry; 431 int entry_size, type; 432 int err; 433 434 hfs_dbg("cnid %u - ino %llu, name %s - ino %llu, name %s\n", 435 cnid, src_dir->i_ino, src_name->name, 436 dst_dir->i_ino, dst_name->name); 437 err = hfs_find_init(HFSPLUS_SB(sb)->cat_tree, &src_fd); 438 if (err) 439 return err; 440 dst_fd = src_fd; 441 442 /* 443 * Fail early and avoid ENOSPC during the btree operations. We may 444 * have to split the root node at most twice. 445 */ 446 err = hfs_bmap_reserve(src_fd.tree, 4 * (int)src_fd.tree->depth - 1); 447 if (err) 448 goto out; 449 450 /* find the old dir entry and read the data */ 451 err = hfsplus_cat_build_key(sb, src_fd.search_key, 452 src_dir->i_ino, src_name); 453 if (unlikely(err)) 454 goto out; 455 456 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 457 if (err) 458 goto out; 459 if (src_fd.entrylength > sizeof(entry) || src_fd.entrylength < 0) { 460 err = -EIO; 461 goto out; 462 } 463 464 hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset, 465 src_fd.entrylength); 466 type = be16_to_cpu(entry.type); 467 468 /* create new dir entry with the data from the old entry */ 469 err = hfsplus_cat_build_key(sb, dst_fd.search_key, 470 dst_dir->i_ino, dst_name); 471 if (unlikely(err)) 472 goto out; 473 474 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key); 475 if (err != -ENOENT) { 476 if (!err) 477 err = -EEXIST; 478 goto out; 479 } 480 481 err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength); 482 if (err) 483 goto out; 484 dst_dir->i_size++; 485 if (type == HFSPLUS_FOLDER) 486 hfsplus_subfolders_inc(dst_dir); 487 inode_set_mtime_to_ts(dst_dir, inode_set_ctime_current(dst_dir)); 488 489 /* finally remove the old entry */ 490 err = hfsplus_cat_build_key(sb, src_fd.search_key, 491 src_dir->i_ino, src_name); 492 if (unlikely(err)) 493 goto out; 494 495 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 496 if (err) 497 goto out; 498 err = hfs_brec_remove(&src_fd); 499 if (err) 500 goto out; 501 src_dir->i_size--; 502 if (type == HFSPLUS_FOLDER) 503 hfsplus_subfolders_dec(src_dir); 504 inode_set_mtime_to_ts(src_dir, inode_set_ctime_current(src_dir)); 505 506 /* remove old thread entry */ 507 hfsplus_cat_build_key_with_cnid(sb, src_fd.search_key, cnid); 508 err = hfs_brec_find(&src_fd, hfs_find_rec_by_key); 509 if (err) 510 goto out; 511 type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset); 512 err = hfs_brec_remove(&src_fd); 513 if (err) 514 goto out; 515 516 /* create new thread entry */ 517 hfsplus_cat_build_key_with_cnid(sb, dst_fd.search_key, cnid); 518 entry_size = hfsplus_fill_cat_thread(sb, &entry, type, 519 dst_dir->i_ino, dst_name); 520 if (unlikely(entry_size < 0)) { 521 err = entry_size; 522 goto out; 523 } 524 525 err = hfs_brec_find(&dst_fd, hfs_find_rec_by_key); 526 if (err != -ENOENT) { 527 if (!err) 528 err = -EEXIST; 529 goto out; 530 } 531 err = hfs_brec_insert(&dst_fd, &entry, entry_size); 532 533 hfsplus_mark_inode_dirty(HFSPLUS_CAT_TREE_I(sb), HFSPLUS_I_CAT_DIRTY); 534 hfsplus_mark_inode_dirty(dst_dir, HFSPLUS_I_CAT_DIRTY); 535 hfsplus_mark_inode_dirty(src_dir, HFSPLUS_I_CAT_DIRTY); 536 out: 537 hfs_bnode_put(dst_fd.bnode); 538 hfs_find_exit(&src_fd); 539 return err; 540 } 541