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 inode_set_mtime_to_ts(dir, inode_set_ctime_current(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 inode_set_mtime_to_ts(dir, inode_set_ctime_current(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 inode_set_mtime_to_ts(dst_dir, inode_set_ctime_current(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 inode_set_mtime_to_ts(src_dir, inode_set_ctime_current(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 } 550
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.