1 2 3#include "hfs.h" 4 5/*================ Variable-like macros ================*/ 6 7/* Number of hash table slots */ 8#define C_HASHBITS 10 9#define C_HASHSIZE (1UL << C_HASHBITS) 10#define C_HASHMASK (C_HASHSIZE - 1) 11 12/* Number of entries to fit in a single page on an i386. 13 * Actually, now it's used to increment the free entry pool. */ 14#define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry)) 15#define CCACHE_MAX (CCACHE_INC * 8) 16 17/*================ File-local data types ================*/ 18 19/* The catalog record for a file */ 20typedef struct { 21 hfs_byte_t Flags; /* Flags such as read-only */ 22 hfs_byte_t Typ; /* file version number = 0 */ 23 hfs_finfo_t UsrWds; /* data used by the Finder */ 24 hfs_lword_t FlNum; /* The CNID */ 25 hfs_word_t StBlk; /* obsolete */ 26 hfs_lword_t LgLen; /* The logical EOF of the data fork*/ 27 hfs_lword_t PyLen; /* The physical EOF of the data fork */ 28 hfs_word_t RStBlk; /* obsolete */ 29 hfs_lword_t RLgLen; /* The logical EOF of the rsrc fork */ 30 hfs_lword_t RPyLen; /* The physical EOF of the rsrc fork */ 31 hfs_lword_t CrDat; /* The creation date */ 32 hfs_lword_t MdDat; /* The modified date */ 33 hfs_lword_t BkDat; /* The last backup date */ 34 hfs_fxinfo_t FndrInfo; /* more data for the Finder */ 35 hfs_word_t ClpSize; /* number of bytes to allocate 36 when extending files */ 37 hfs_byte_t ExtRec[12]; /* first extent record 38 for the data fork */ 39 hfs_byte_t RExtRec[12]; /* first extent record 40 for the resource fork */ 41 hfs_lword_t Resrv; /* reserved by Apple */ 42} __attribute__((packed)) FIL_REC; 43 44/* the catalog record for a directory */ 45typedef struct { 46 hfs_word_t Flags; /* flags */ 47 hfs_word_t Val; /* Valence: number of files and 48 dirs in the directory */ 49 hfs_lword_t DirID; /* The CNID */ 50 hfs_lword_t CrDat; /* The creation date */ 51 hfs_lword_t MdDat; /* The modification date */ 52 hfs_lword_t BkDat; /* The last backup date */ 53 hfs_dinfo_t UsrInfo; /* data used by the Finder */ 54 hfs_dxinfo_t FndrInfo; /* more data used by Finder */ 55 hfs_byte_t Resrv[16]; /* reserved by Apple */ 56} __attribute__((packed)) DIR_REC; 57 58/* the catalog record for a thread */ 59typedef struct { 60 hfs_byte_t Reserv[8]; /* reserved by Apple */ 61 hfs_lword_t ParID; /* CNID of parent directory */ 62 struct hfs_name CName; /* The name of this entry */ 63} __attribute__((packed)) THD_REC; 64 65/* A catalog tree record */ 66struct hfs_cat_rec { 67 hfs_byte_t cdrType; /* The type of entry */ 68 hfs_byte_t cdrResrv2; /* padding */ 69 union { 70 FIL_REC fil; 71 DIR_REC dir; 72 THD_REC thd; 73 } u; 74} __attribute__((packed)); 75 76/*================ File-local variables ================*/ 77 78static LIST_HEAD(entry_in_use); 79static LIST_HEAD(entry_unused); 80static struct list_head hash_table[C_HASHSIZE]; 81 82static spinlock_t entry_lock = SPIN_LOCK_UNLOCKED; 83 84static struct { 85 int nr_entries; 86 int nr_free_entries; 87} entries_stat; 88 89/*================ File-local functions ================*/ 90 91/* 92 * brec_to_id 93 * 94 * Get the CNID from a brec 95 */ 96static inline hfs_u32 brec_to_id(struct hfs_brec *brec) 97{ 98 struct hfs_cat_rec *rec = brec->data; 99 100 return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ? 101 rec->u.fil.FlNum : rec->u.dir.DirID); 102} 103 104/* 105 * hashfn() 106 * 107 * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer. 108 */ 109static inline unsigned int hashfn(const struct hfs_mdb *mdb, 110 const struct hfs_cat_key *key) 111{ 112 unsigned int hash; 113 114 hash = (unsigned long) mdb | (unsigned long) key->ParID[3] | 115 hfs_strhash(key->CName.Name, key->CName.Len); 116 hash = hash ^ (hash >> C_HASHBITS) ^ (hash >> C_HASHBITS*2); 117 return hash & C_HASHMASK; 118} 119 120/* 121 * hash() 122 * 123 * hash an (struct mdb *) and a (struct hfs_cat_key *) 124 * to a pointer to a slot in the hash table. 125 */ 126static inline struct list_head *hash(struct hfs_mdb *mdb, 127 const struct hfs_cat_key *key) 128{ 129 return hash_table + hashfn(mdb, key); 130} 131 132static inline void insert_hash(struct hfs_cat_entry *entry) 133{ 134 struct list_head *head = hash(entry->mdb, &entry->key); 135 list_add(&entry->hash, head); 136} 137 138static inline void remove_hash(struct hfs_cat_entry *entry) 139{ 140 list_del(&entry->hash); 141 INIT_LIST_HEAD(&entry->hash); 142} 143 144/* 145 * wait_on_entry() 146 * 147 * Sleep until a locked entry is unlocked. 148 */ 149static inline void wait_on_entry(struct hfs_cat_entry * entry) 150{ 151 while ((entry->state & HFS_LOCK)) { 152 hfs_sleep_on(&entry->wait); 153 } 154} 155 156/* 157 * lock_entry() 158 * 159 * Obtain an exclusive lock on an entry. 160 */ 161static void lock_entry(struct hfs_cat_entry * entry) 162{ 163 wait_on_entry(entry); 164 spin_lock(&entry_lock); 165 entry->state |= HFS_LOCK; 166 spin_unlock(&entry_lock); 167} 168 169/* 170 * lock_entry() 171 * 172 * Relinquish an exclusive lock on an entry. 173 */ 174static void unlock_entry(struct hfs_cat_entry * entry) 175{ 176 spin_lock(&entry_lock); 177 entry->state &= ~HFS_LOCK; 178 spin_unlock(&entry_lock); 179 hfs_wake_up(&entry->wait); 180} 181 182/* put entry on mdb dirty list. */ 183void hfs_cat_mark_dirty(struct hfs_cat_entry *entry) 184{ 185 struct hfs_mdb *mdb = entry->mdb; 186 187 spin_lock(&entry_lock); 188 if (!(entry->state & HFS_DIRTY)) { 189 entry->state |= HFS_DIRTY; 190 191 /* Only add valid (ie hashed) entries to the dirty list. */ 192 if (!list_empty(&entry->hash)) { 193 list_del(&entry->list); 194 list_add(&entry->list, &mdb->entry_dirty); 195 } 196 } 197 spin_unlock(&entry_lock); 198} 199 200/* delete an entry and remove it from the hash table. */ 201static void delete_entry(struct hfs_cat_entry *entry) 202{ 203 if (!(entry->state & HFS_DELETED)) { 204 entry->state |= HFS_DELETED; 205 list_del(&entry->hash); 206 INIT_LIST_HEAD(&entry->hash); 207 208 if (entry->type == HFS_CDR_FIL) { 209 /* free all extents */ 210 entry->u.file.data_fork.lsize = 0; 211 hfs_extent_adj(&entry->u.file.data_fork); 212 entry->u.file.rsrc_fork.lsize = 0; 213 hfs_extent_adj(&entry->u.file.rsrc_fork); 214 } 215 } 216} 217 218 219static inline void init_entry(struct hfs_cat_entry *entry) 220{ 221 memset(entry, 0, sizeof(*entry)); 222 hfs_init_waitqueue(&entry->wait); 223 INIT_LIST_HEAD(&entry->hash); 224 INIT_LIST_HEAD(&entry->list); 225} 226 227/* 228 * hfs_cat_alloc() 229 * 230 * Try to allocate another entry. 231 */ 232static inline struct hfs_cat_entry *hfs_cat_alloc(void) 233{ 234 struct hfs_cat_entry *entry; 235 236 if (!HFS_NEW(entry)) 237 return NULL; 238 239 init_entry(entry); 240 return entry; 241} 242 243/* this gets called with the spinlock held. */ 244static int grow_entries(void) 245{ 246 struct hfs_cat_entry *entry; 247 int i; 248 249 for (i = 0; i < CCACHE_INC; i++) { 250 if (!(entry = hfs_cat_alloc())) 251 break; 252 list_add(&entry->list, &entry_unused); 253 } 254 255 entries_stat.nr_entries += i; 256 entries_stat.nr_free_entries += i; 257 258 return i; 259} 260 261/* 262 * __read_entry() 263 * 264 * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry). 265 */ 266static void __read_entry(struct hfs_cat_entry *entry, 267 const struct hfs_cat_rec *cat) 268{ 269 entry->type = cat->cdrType; 270 271 if (cat->cdrType == HFS_CDR_DIR) { 272 struct hfs_dir *dir = &entry->u.dir; 273 274 entry->cnid = hfs_get_nl(cat->u.dir.DirID); 275 276 dir->magic = HFS_DIR_MAGIC; 277 dir->flags = hfs_get_ns(cat->u.dir.Flags); 278 memcpy(&entry->info.dir.dinfo, &cat->u.dir.UsrInfo, 16); 279 memcpy(&entry->info.dir.dxinfo, &cat->u.dir.FndrInfo, 16); 280 entry->create_date = hfs_get_nl(cat->u.dir.CrDat); 281 entry->modify_date = hfs_get_nl(cat->u.dir.MdDat); 282 entry->backup_date = hfs_get_nl(cat->u.dir.BkDat); 283 dir->dirs = dir->files = 0; 284 hfs_init_waitqueue(&dir->read_wait); 285 hfs_init_waitqueue(&dir->write_wait); 286 } else if (cat->cdrType == HFS_CDR_FIL) { 287 struct hfs_file *fil = &entry->u.file; 288 289 entry->cnid = hfs_get_nl(cat->u.fil.FlNum); 290 291 fil->magic = HFS_FILE_MAGIC; 292 293 fil->data_fork.fork = HFS_FK_DATA; 294 fil->data_fork.entry = entry; 295 fil->data_fork.lsize = hfs_get_hl(cat->u.fil.LgLen); 296 fil->data_fork.psize = hfs_get_hl(cat->u.fil.PyLen) >> 297 HFS_SECTOR_SIZE_BITS; 298 hfs_extent_in(&fil->data_fork, cat->u.fil.ExtRec); 299 300 fil->rsrc_fork.fork = HFS_FK_RSRC; 301 fil->rsrc_fork.entry = entry; 302 fil->rsrc_fork.lsize = hfs_get_hl(cat->u.fil.RLgLen); 303 fil->rsrc_fork.psize = hfs_get_hl(cat->u.fil.RPyLen) >> 304 HFS_SECTOR_SIZE_BITS; 305 hfs_extent_in(&fil->rsrc_fork, cat->u.fil.RExtRec); 306 307 memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16); 308 memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16); 309 310 entry->create_date = hfs_get_nl(cat->u.fil.CrDat); 311 entry->modify_date = hfs_get_nl(cat->u.fil.MdDat); 312 entry->backup_date = hfs_get_nl(cat->u.fil.BkDat); 313 fil->clumpablks = (hfs_get_hs(cat->u.fil.ClpSize) 314 / entry->mdb->alloc_blksz) 315 >> HFS_SECTOR_SIZE_BITS; 316 fil->flags = cat->u.fil.Flags; 317 } else { 318 hfs_warn("hfs_fs: entry is neither file nor directory!\n"); 319 } 320} 321 322/* 323 * count_dir_entries() 324 * 325 * Count the number of files and directories in a given directory. 326 */ 327static inline void count_dir_entries(struct hfs_cat_entry *entry, 328 struct hfs_brec *brec) 329{ 330 int error = 0; 331 hfs_u32 cnid; 332 hfs_u8 type; 333 334 if (!hfs_cat_open(entry, brec)) { 335 while (!(error = hfs_cat_next(entry, brec, 1, &cnid, &type))) { 336 if (type == HFS_CDR_FIL) { 337 ++entry->u.dir.files; 338 } else if (type == HFS_CDR_DIR) { 339 ++entry->u.dir.dirs; 340 } 341 } /* -ENOENT is normal termination */ 342 } 343 if (error != -ENOENT) { 344 entry->cnid = 0; 345 } 346} 347 348/* 349 * read_entry() 350 * 351 * Convert a (struct hfs_brec) to a (struct hfs_cat_entry). 352 */ 353static inline void read_entry(struct hfs_cat_entry *entry, 354 struct hfs_brec *brec) 355{ 356 int need_count; 357 struct hfs_cat_rec *rec = brec->data; 358 359 __read_entry(entry, rec); 360 361 need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val; 362 363 hfs_brec_relse(brec, NULL); 364 365 if (need_count) { 366 count_dir_entries(entry, brec); 367 } 368} 369 370/* 371 * __write_entry() 372 * 373 * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec). 374 */ 375static void __write_entry(const struct hfs_cat_entry *entry, 376 struct hfs_cat_rec *cat) 377{ 378 if (entry->type == HFS_CDR_DIR) { 379 const struct hfs_dir *dir = &entry->u.dir; 380 381 hfs_put_ns(dir->flags, cat->u.dir.Flags); 382 hfs_put_hs(dir->dirs + dir->files, cat->u.dir.Val); 383 hfs_put_nl(entry->cnid, cat->u.dir.DirID); 384 hfs_put_nl(entry->create_date, cat->u.dir.CrDat); 385 hfs_put_nl(entry->modify_date, cat->u.dir.MdDat); 386 hfs_put_nl(entry->backup_date, cat->u.dir.BkDat); 387 memcpy(&cat->u.dir.UsrInfo, &entry->info.dir.dinfo, 16); 388 memcpy(&cat->u.dir.FndrInfo, &entry->info.dir.dxinfo, 16); 389 } else if (entry->type == HFS_CDR_FIL) { 390 const struct hfs_file *fil = &entry->u.file; 391 392 cat->u.fil.Flags = fil->flags; 393 hfs_put_nl(entry->cnid, cat->u.fil.FlNum); 394 memcpy(&cat->u.fil.UsrWds, &entry->info.file.finfo, 16); 395 hfs_put_hl(fil->data_fork.lsize, cat->u.fil.LgLen); 396 hfs_put_hl(fil->data_fork.psize << HFS_SECTOR_SIZE_BITS, 397 cat->u.fil.PyLen); 398 hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen); 399 hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS, 400 cat->u.fil.RPyLen); 401 hfs_put_nl(entry->create_date, cat->u.fil.CrDat); 402 hfs_put_nl(entry->modify_date, cat->u.fil.MdDat); 403 hfs_put_nl(entry->backup_date, cat->u.fil.BkDat); 404 memcpy(&cat->u.fil.FndrInfo, &entry->info.file.fxinfo, 16); 405 hfs_put_hs((fil->clumpablks * entry->mdb->alloc_blksz) 406 << HFS_SECTOR_SIZE_BITS, cat->u.fil.ClpSize); 407 hfs_extent_out(&fil->data_fork, cat->u.fil.ExtRec); 408 hfs_extent_out(&fil->rsrc_fork, cat->u.fil.RExtRec); 409 } else { 410 hfs_warn("__write_entry: invalid entry\n"); 411 } 412} 413 414/* 415 * write_entry() 416 * 417 * Write a modified entry back to the catalog B-tree. this gets called 418 * with the entry locked. 419 */ 420static void write_entry(struct hfs_cat_entry * entry) 421{ 422 struct hfs_brec brec; 423 int error; 424 425 if (!(entry->state & HFS_DELETED)) { 426 error = hfs_bfind(&brec, entry->mdb->cat_tree, 427 HFS_BKEY(&entry->key), HFS_BFIND_WRITE); 428 if (!error) { 429 if ((entry->state & HFS_KEYDIRTY)) { 430 /* key may have changed case due to a rename */ 431 entry->state &= ~HFS_KEYDIRTY; 432 if (brec.key->KeyLen != entry->key.KeyLen) { 433 hfs_warn("hfs_write_entry: key length " 434 "changed!\n"); 435 error = 1; 436 } else { 437 memcpy(brec.key, &entry->key, 438 entry->key.KeyLen); 439 } 440 } else if (entry->cnid != brec_to_id(&brec)) { 441 hfs_warn("hfs_write_entry: CNID " 442 "changed unexpectedly!\n"); 443 error = 1; 444 } 445 if (!error) { 446 __write_entry(entry, brec.data); 447 } 448 hfs_brec_relse(&brec, NULL); 449 } 450 if (error) { 451 hfs_warn("hfs_write_entry: unable to write " 452 "entry %08x\n", entry->cnid); 453 } 454 } 455} 456 457 458/* this gets called with the spinlock held. */ 459static struct hfs_cat_entry *find_entry(struct hfs_mdb *mdb, 460 const struct hfs_cat_key *key) 461{ 462 struct list_head *tmp, *head = hash(mdb, key); 463 struct hfs_cat_entry * entry; 464 465 tmp = head; 466 for (;;) { 467 tmp = tmp->next; 468 entry = NULL; 469 if (tmp == head) 470 break; 471 entry = list_entry(tmp, struct hfs_cat_entry, hash); 472 if (entry->mdb != mdb) 473 continue; 474 if (hfs_cat_compare(&entry->key, key)) { 475 continue; 476 } 477 entry->count++; 478 break; 479 } 480 481 return entry; 482} 483 484 485/* be careful. this gets called with the spinlock held. */ 486static struct hfs_cat_entry *get_new_entry(struct hfs_mdb *mdb, 487 const struct hfs_cat_key *key, 488 const int read) 489{ 490 struct hfs_cat_entry *entry; 491 struct list_head *head = hash(mdb, key); 492 struct list_head *tmp; 493 494add_new_entry: 495 tmp = entry_unused.next; 496 if ((tmp != &entry_unused) ) { 497 list_del(tmp); 498 entries_stat.nr_free_entries--; 499 entry = list_entry(tmp, struct hfs_cat_entry, list); 500 list_add(&entry->list, &entry_in_use); 501 list_add(&entry->hash, head); 502 entry->mdb = mdb; 503 entry->count = 1; 504 memcpy(&entry->key, key, sizeof(*key)); 505 entry->state = HFS_LOCK; 506 spin_unlock(&entry_lock); 507 508 if (read) { 509 struct hfs_brec brec; 510 511 if (hfs_bfind(&brec, mdb->cat_tree, 512 HFS_BKEY(key), HFS_BFIND_READ_EQ)) { 513 /* uh oh. we failed to read the record. 514 * the entry doesn't actually exist. */ 515 goto read_fail; 516 } 517 518 read_entry(entry, &brec); 519 520 /* error */ 521 if (!entry->cnid) { 522 goto read_fail; 523 } 524 525 /* we don't have to acquire a spinlock here or 526 * below for the unlocking bits as we're the first 527 * user of this entry. */ 528 entry->state &= ~HFS_LOCK; 529 hfs_wake_up(&entry->wait); 530 } 531 532 return entry; 533 } 534 535 536 /* try to allocate more entries. grow_entries() doesn't release 537 * the spinlock. */ 538 if (grow_entries()) 539 goto add_new_entry; 540 541 spin_unlock(&entry_lock); 542 return NULL; 543 544read_fail: 545 /* short-cut hfs_cat_put by doing everything here. */ 546 spin_lock(&entry_lock); 547 list_del(&entry->hash); 548 list_del(&entry->list); 549 init_entry(entry); 550 list_add(&entry->list, &entry_unused); 551 entries_stat.nr_free_entries++; 552 spin_unlock(&entry_lock); 553 return NULL; 554} 555 556/* 557 * get_entry() 558 * 559 * Try to return an entry for the indicated file or directory. 560 * If ('read' == 0) then no attempt will be made to read it from disk 561 * and a locked, but uninitialized, entry is returned. 562 */ 563static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb, 564 const struct hfs_cat_key *key, 565 const int read) 566{ 567 struct hfs_cat_entry * entry; 568 569#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 570 hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n", 571 mdb, key->CName.Name, read); 572#endif 573 574 spin_lock(&entry_lock); 575 entry = find_entry(mdb, key); 576 if (!entry) { 577 return get_new_entry(mdb, key, read); 578 } 579 spin_unlock(&entry_lock); 580 wait_on_entry(entry); 581 return entry; 582} 583 584/* 585 * new_cnid() 586 * 587 * Allocate a CNID to use for a new file or directory. 588 */ 589static inline hfs_u32 new_cnid(struct hfs_mdb *mdb) 590{ 591 /* If the create succeeds then the mdb will get dirtied */ 592 return htonl(mdb->next_id++); 593} 594 595/* 596 * update_dir() 597 * 598 * Update counts, times and dirt on a changed directory 599 */ 600static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir, 601 int is_dir, int count) 602{ 603 /* update counts */ 604 if (is_dir) { 605 mdb->dir_count += count; 606 dir->u.dir.dirs += count; 607 if (dir->cnid == htonl(HFS_ROOT_CNID)) { 608 mdb->root_dirs += count; 609 } 610 } else { 611 mdb->file_count += count; 612 dir->u.dir.files += count; 613 if (dir->cnid == htonl(HFS_ROOT_CNID)) { 614 mdb->root_files += count; 615 } 616 } 617 618 /* update times and dirt */ 619 dir->modify_date = hfs_time(); 620 hfs_cat_mark_dirty(dir); 621} 622 623static inline void start_write(struct hfs_cat_entry *dir) 624{ 625 if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) { 626 hfs_sleep_on(&dir->u.dir.write_wait); 627 } 628 ++dir->u.dir.writers; 629} 630 631/* 632 * Add a reader to dir, excluding writers. 633 */ 634static inline void start_read(struct hfs_cat_entry *dir) 635{ 636 if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) { 637 hfs_sleep_on(&dir->u.dir.read_wait); 638 } 639 ++dir->u.dir.readers; 640} 641 642/* 643 * Remove a writer from dir, possibly admitting readers. 644 */ 645static inline void end_write(struct hfs_cat_entry *dir) 646{ 647 if (!(--dir->u.dir.writers)) { 648 hfs_wake_up(&dir->u.dir.read_wait); 649 } 650} 651 652/* 653 * Remove a reader from dir, possibly admitting writers. 654 */ 655static inline void end_read(struct hfs_cat_entry *dir) 656{ 657 if (!(--dir->u.dir.readers)) { 658 hfs_wake_up(&dir->u.dir.write_wait); 659 } 660} 661 662/* 663 * create_entry() 664 * 665 * Add a new file or directory to the catalog B-tree and 666 * return a (struct hfs_cat_entry) for it in '*result'. 667 */ 668static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key, 669 const struct hfs_cat_rec *record, int is_dir, 670 hfs_u32 cnid, struct hfs_cat_entry **result) 671{ 672 struct hfs_mdb *mdb = parent->mdb; 673 struct hfs_cat_entry *entry; 674 struct hfs_cat_key thd_key; 675 struct hfs_cat_rec thd_rec; 676 int error, has_thread; 677 678 if (result) { 679 *result = NULL; 680 } 681 682 /* keep readers from getting confused by changing dir size */ 683 start_write(parent); 684 685 /* create a locked entry in the cache */ 686 entry = get_entry(mdb, key, 0); 687 if (!entry) { 688 /* The entry exists but can't be read */ 689 error = -EIO; 690 goto done; 691 } 692 693 if (entry->cnid) { 694 /* The (unlocked) entry exists in the cache */ 695 error = -EEXIST; 696 goto bail2; 697 } 698 699 /* limit directory valence to signed 16-bit integer */ 700 if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) { 701 error = -ENOSPC; 702 goto bail1; 703 } 704 705 has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD); 706 707 if (has_thread) { 708 /* init some fields for the thread record */ 709 memset(&thd_rec, 0, sizeof(thd_rec)); 710 thd_rec.cdrType = is_dir ? HFS_CDR_THD : HFS_CDR_FTH; 711 memcpy(&thd_rec.u.thd.ParID, &key->ParID, 712 sizeof(hfs_u32) + sizeof(struct hfs_name)); 713 714 /* insert the thread record */ 715 hfs_cat_build_key(cnid, NULL, &thd_key); 716 error = hfs_binsert(mdb->cat_tree, HFS_BKEY(&thd_key), 717 &thd_rec, 2 + sizeof(THD_REC)); 718 if (error) { 719 goto bail1; 720 } 721 } 722 723 /* insert the record */ 724 error = hfs_binsert(mdb->cat_tree, HFS_BKEY(key), record, 725 is_dir ? 2 + sizeof(DIR_REC) : 726 2 + sizeof(FIL_REC)); 727 if (error) { 728 if (has_thread && (error != -EIO)) { 729 /* at least TRY to remove the thread record */ 730 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key)); 731 } 732 goto bail1; 733 } 734 735 /* update the parent directory */ 736 update_dir(mdb, parent, is_dir, 1); 737 738 /* complete the cache entry and return success */ 739 __read_entry(entry, record); 740 unlock_entry(entry); 741 742 if (result) { 743 *result = entry; 744 } else { 745 hfs_cat_put(entry); 746 } 747 goto done; 748 749bail1: 750 /* entry really didn't exist, so we don't need to really delete it. 751 * we do need to remove it from the hash, though. */ 752 entry->state |= HFS_DELETED; 753 remove_hash(entry); 754 unlock_entry(entry); 755bail2: 756 hfs_cat_put(entry); 757done: 758 end_write(parent); 759 return error; 760} 761 762/*================ Global functions ================*/ 763 764/* 765 * hfs_cat_put() 766 * 767 * Release an entry we aren't using anymore. 768 * 769 * nothing in hfs_cat_put goes to sleep now except on the initial entry. 770 */ 771void hfs_cat_put(struct hfs_cat_entry * entry) 772{ 773 if (entry) { 774 wait_on_entry(entry); 775 776 /* just in case. this should never happen. */ 777 if (!entry->count) { 778 hfs_warn("hfs_cat_put: trying to free free entry: %p\n", 779 entry); 780 return; 781 } 782 783#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 784 hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n", 785 entry, entry->count, entry->type, entry->state); 786#endif 787 spin_lock(&entry_lock); 788 if (!--entry->count) { 789 if ((entry->state & HFS_DELETED)) 790 goto entry_deleted; 791 792 if ((entry->type == HFS_CDR_FIL)) { 793 /* clear out any cached extents */ 794 if (entry->u.file.data_fork.first.next) { 795 hfs_extent_free(&entry->u.file.data_fork); 796 } 797 if (entry->u.file.rsrc_fork.first.next) { 798 hfs_extent_free(&entry->u.file.rsrc_fork); 799 } 800 } 801 802 /* if we put a dirty entry, write it out. */ 803 if ((entry->state & HFS_DIRTY)) { 804 entry->state ^= HFS_DIRTY | HFS_LOCK; 805 write_entry(entry); 806 entry->state &= ~HFS_LOCK; 807 } 808 809 list_del(&entry->hash); 810entry_deleted: /* deleted entries have already been removed 811 * from the hash list. */ 812 list_del(&entry->list); 813 if (entries_stat.nr_free_entries > CCACHE_MAX) { 814 HFS_DELETE(entry); 815 entries_stat.nr_entries--; 816 } else { 817 init_entry(entry); 818 list_add(&entry->list, &entry_unused); 819 entries_stat.nr_free_entries++; 820 } 821 } 822 spin_unlock(&entry_lock); 823 } 824} 825 826/* 827 * hfs_cat_get() 828 * 829 * Wrapper for get_entry() which always calls with ('read'==1). 830 * Used for access to get_entry() from outside this file. 831 */ 832struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb, 833 const struct hfs_cat_key *key) 834{ 835 return get_entry(mdb, key, 1); 836} 837 838/* invalidate all entries for a device */ 839static void invalidate_list(struct list_head *head, struct hfs_mdb *mdb, 840 struct list_head *dispose) 841{ 842 struct list_head *next; 843 844 next = head->next; 845 for (;;) { 846 struct list_head *tmp = next; 847 struct hfs_cat_entry * entry; 848 849 next = next->next; 850 if (tmp == head) 851 break; 852 entry = list_entry(tmp, struct hfs_cat_entry, list); 853 if (entry->mdb != mdb) { 854 continue; 855 } 856 857 if (!entry->count) { 858 list_del(&entry->hash); 859 INIT_LIST_HEAD(&entry->hash); 860 list_del(&entry->list); 861 list_add(&entry->list, dispose); 862 continue; 863 } 864 865 hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n", 866 entry, entry->count, 867 hfs_mdb_name(entry->mdb->sys_mdb)); 868 } 869} 870 871/* delete entries from a list */ 872static void delete_list(struct list_head *head) 873{ 874 struct list_head *next = head->next; 875 struct hfs_cat_entry *entry; 876 877 for (;;) { 878 struct list_head * tmp = next; 879 880 next = next->next; 881 if (tmp == head) { 882 break; 883 } 884 entry = list_entry(tmp, struct hfs_cat_entry, list); 885 HFS_DELETE(entry); 886 } 887} 888 889/* 890 * hfs_cat_invalidate() 891 * 892 * Called by hfs_mdb_put() to remove all the entries 893 * in the cache that are associated with a given MDB. 894 */ 895void hfs_cat_invalidate(struct hfs_mdb *mdb) 896{ 897 LIST_HEAD(throw_away); 898 899 spin_lock(&entry_lock); 900 invalidate_list(&entry_in_use, mdb, &throw_away); 901 invalidate_list(&mdb->entry_dirty, mdb, &throw_away); 902 spin_unlock(&entry_lock); 903 904 delete_list(&throw_away); 905#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 906 hfs_warn("hfs_cat_invalidate: free=%d total=%d\n", 907 entries_stat.nr_free_entries, 908 entries_stat.nr_entries); 909#endif 910} 911 912/* 913 * hfs_cat_commit() 914 * 915 * Called by hfs_mdb_commit() to write dirty entries to the disk buffers. 916 */ 917void hfs_cat_commit(struct hfs_mdb *mdb) 918{ 919 struct list_head *tmp, *head = &mdb->entry_dirty; 920 struct hfs_cat_entry *entry; 921 922 spin_lock(&entry_lock); 923 while ((tmp = head->prev) != head) { 924 entry = list_entry(tmp, struct hfs_cat_entry, list); 925 926 if ((entry->state & HFS_LOCK)) { 927 spin_unlock(&entry_lock); 928 wait_on_entry(entry); 929 spin_lock(&entry_lock); 930 } else { 931 struct list_head *insert = &entry_in_use; 932 933 if (!entry->count) 934 insert = entry_in_use.prev; 935 936 /* add to in_use list */ 937 list_del(&entry->list); 938 list_add(&entry->list, insert); 939 940 /* reset DIRTY, set LOCK */ 941 entry->state ^= HFS_DIRTY | HFS_LOCK; 942 spin_unlock(&entry_lock); 943 write_entry(entry); 944 spin_lock(&entry_lock); 945 entry->state &= ~HFS_LOCK; 946 hfs_wake_up(&entry->wait); 947 } 948 } 949 spin_unlock(&entry_lock); 950} 951 952/* 953 * hfs_cat_free() 954 * 955 * Releases all the memory allocated in grow_entries(). 956 * Must call hfs_cat_invalidate() on all MDBs before calling this. 957 * This only gets rid of the unused pool of entries. all the other 958 * entry references should have either been freed by cat_invalidate 959 * or moved onto the unused list. 960 */ 961void hfs_cat_free(void) 962{ 963 delete_list(&entry_unused); 964} 965 966/* 967 * hfs_cat_compare() 968 * 969 * Description: 970 * This is the comparison function used for the catalog B-tree. In 971 * comparing catalog B-tree entries, the parent id is the most 972 * significant field (compared as unsigned ints). The name field is 973 * the least significant (compared in "Macintosh lexical order", 974 * see hfs_strcmp() in string.c) 975 * Input Variable(s): 976 * struct hfs_cat_key *key1: pointer to the first key to compare 977 * struct hfs_cat_key *key2: pointer to the second key to compare 978 * Output Variable(s): 979 * NONE 980 * Returns: 981 * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2 982 * Preconditions: 983 * key1 and key2 point to "valid" (struct hfs_cat_key)s. 984 * Postconditions: 985 * This function has no side-effects 986 */ 987int hfs_cat_compare(const struct hfs_cat_key *key1, 988 const struct hfs_cat_key *key2) 989{ 990 unsigned int parents; 991 int retval; 992 993 parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID); 994 if (parents != 0) { 995 retval = (int)parents; 996 } else { 997 retval = hfs_strcmp(key1->CName.Name, key1->CName.Len, 998 key2->CName.Name, key2->CName.Len); 999 } 1000 return retval; 1001} 1002 1003/* 1004 * hfs_cat_build_key() 1005 * 1006 * Given the ID of the parent and the name build a search key. 1007 */ 1008void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname, 1009 struct hfs_cat_key *key) 1010{ 1011 hfs_put_nl(parent, key->ParID); 1012 1013 if (cname) { 1014 key->KeyLen = 6 + cname->Len; 1015 memcpy(&key->CName, cname, sizeof(*cname)); 1016 } else { 1017 key->KeyLen = 6; 1018 memset(&key->CName, 0, sizeof(*cname)); 1019 } 1020} 1021 1022/* 1023 * hfs_cat_open() 1024 * 1025 * Given a directory on an HFS filesystem get its thread and 1026 * lock the directory against insertions and deletions. 1027 * Return 0 on success or an error code on failure. 1028 */ 1029int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec) 1030{ 1031 struct hfs_cat_key key; 1032 int error; 1033 1034 if (dir->type != HFS_CDR_DIR) { 1035 return -EINVAL; 1036 } 1037 1038 /* Block writers */ 1039 start_read(dir); 1040 1041 /* Find the directory */ 1042 hfs_cat_build_key(dir->cnid, NULL, &key); 1043 error = hfs_bfind(brec, dir->mdb->cat_tree, 1044 HFS_BKEY(&key), HFS_BFIND_READ_EQ); 1045 1046 if (error) { 1047 end_read(dir); 1048 } 1049 1050 return error; 1051} 1052 1053/* 1054 * hfs_cat_next() 1055 * 1056 * Given a catalog brec structure, replace it with the count'th next brec 1057 * in the same directory. 1058 * Return an error code if there is a problem, 0 if OK. 1059 * Note that an error code of -ENOENT means there are no more entries 1060 * in this directory. 1061 * The directory is "closed" on an error. 1062 */ 1063int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec, 1064 hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type) 1065{ 1066 int error; 1067 1068 if (!dir || !brec) { 1069 return -EINVAL; 1070 } 1071 1072 /* Get the count'th next catalog tree entry */ 1073 error = hfs_bsucc(brec, count); 1074 if (!error) { 1075 struct hfs_cat_key *key = (struct hfs_cat_key *)brec->key; 1076 if (hfs_get_nl(key->ParID) != dir->cnid) { 1077 hfs_brec_relse(brec, NULL); 1078 error = -ENOENT; 1079 } 1080 } 1081 if (!error) { 1082 *type = ((struct hfs_cat_rec *)brec->data)->cdrType; 1083 *cnid = brec_to_id(brec); 1084 } else { 1085 end_read(dir); 1086 } 1087 return error; 1088} 1089 1090/* 1091 * hfs_cat_close() 1092 * 1093 * Given a catalog brec structure, replace it with the count'th next brec 1094 * in the same directory. 1095 * Return an error code if there is a problem, 0 if OK. 1096 * Note that an error code of -ENOENT means there are no more entries 1097 * in this directory. 1098 */ 1099void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec) 1100{ 1101 if (dir && brec) { 1102 hfs_brec_relse(brec, NULL); 1103 end_read(dir); 1104 } 1105} 1106 1107/* 1108 * hfs_cat_parent() 1109 * 1110 * Given a catalog entry, return the entry for its parent. 1111 * Uses catalog key for the entry to get its parent's ID 1112 * and then uses the parent's thread record to locate the 1113 * parent's actual catalog entry. 1114 */ 1115struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry) 1116{ 1117 struct hfs_cat_entry *retval = NULL; 1118 struct hfs_mdb *mdb = entry->mdb; 1119 struct hfs_brec brec; 1120 struct hfs_cat_key key; 1121 int error; 1122 1123 lock_entry(entry); 1124 if (!(entry->state & HFS_DELETED)) { 1125 hfs_cat_build_key(hfs_get_nl(entry->key.ParID), NULL, &key); 1126 error = hfs_bfind(&brec, mdb->cat_tree, 1127 HFS_BKEY(&key), HFS_BFIND_READ_EQ); 1128 if (!error) { 1129 /* convert thread record to key */ 1130 struct hfs_cat_rec *rec = brec.data; 1131 key.KeyLen = 6 + rec->u.thd.CName.Len; 1132 memcpy(&key.ParID, &rec->u.thd.ParID, 1133 sizeof(hfs_u32) + sizeof(struct hfs_name)); 1134 1135 hfs_brec_relse(&brec, NULL); 1136 1137 retval = hfs_cat_get(mdb, &key); 1138 } 1139 } 1140 unlock_entry(entry); 1141 return retval; 1142} 1143 1144/* 1145 * hfs_cat_create() 1146 * 1147 * Create a new file with the indicated name in the indicated directory. 1148 * The file will have the indicated flags, type and creator. 1149 * If successful an (struct hfs_cat_entry) is returned in '*result'. 1150 */ 1151int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key, 1152 hfs_u8 flags, hfs_u32 type, hfs_u32 creator, 1153 struct hfs_cat_entry **result) 1154{ 1155 struct hfs_cat_rec record; 1156 hfs_u32 id = new_cnid(parent->mdb); 1157 hfs_u32 mtime = hfs_time(); 1158 1159#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 1160 hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n", 1161 parent, key->CName.Name, flags, result); 1162#endif 1163 /* init some fields for the file record */ 1164 memset(&record, 0, sizeof(record)); 1165 record.cdrType = HFS_CDR_FIL; 1166 record.u.fil.Flags = flags | HFS_FIL_USED; 1167 hfs_put_nl(id, record.u.fil.FlNum); 1168 hfs_put_nl(mtime, record.u.fil.CrDat); 1169 hfs_put_nl(mtime, record.u.fil.MdDat); 1170 hfs_put_nl(0, record.u.fil.BkDat); 1171 hfs_put_nl(type, record.u.fil.UsrWds.fdType); 1172 hfs_put_nl(creator, record.u.fil.UsrWds.fdCreator); 1173 1174 return create_entry(parent, key, &record, 0, id, result); 1175} 1176 1177/* 1178 * hfs_cat_mkdir() 1179 * 1180 * Create a new directory with the indicated name in the indicated directory. 1181 * If successful an (struct hfs_cat_entry) is returned in '*result'. 1182 */ 1183int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key, 1184 struct hfs_cat_entry **result) 1185{ 1186 struct hfs_cat_rec record; 1187 hfs_u32 id = new_cnid(parent->mdb); 1188 hfs_u32 mtime = hfs_time(); 1189 1190#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 1191 hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name, 1192 result); 1193#endif 1194 1195 /* init some fields for the directory record */ 1196 memset(&record, 0, sizeof(record)); 1197 record.cdrType = HFS_CDR_DIR; 1198 hfs_put_nl(id, record.u.dir.DirID); 1199 hfs_put_nl(mtime, record.u.dir.CrDat); 1200 hfs_put_nl(mtime, record.u.dir.MdDat); 1201 hfs_put_nl(0, record.u.dir.BkDat); 1202 hfs_put_hs(0xff, record.u.dir.UsrInfo.frView); 1203 1204 return create_entry(parent, key, &record, 1, id, result); 1205} 1206 1207/* 1208 * hfs_cat_delete() 1209 * 1210 * Delete the indicated file or directory. 1211 * The associated thread is also removed unless ('with_thread'==0). 1212 */ 1213int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry, 1214 int with_thread) 1215{ 1216 struct hfs_cat_key key; 1217 struct hfs_mdb *mdb = parent->mdb; 1218 int is_dir, error = 0; 1219 1220#if defined(DEBUG_CATALOG) || defined(DEBUG_ALL) 1221 hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n", 1222 parent, entry, entry->type, entry->state, with_thread); 1223#endif 1224 if (parent->mdb != entry->mdb) { 1225 return -EINVAL; 1226 } 1227 1228 if (entry->type == HFS_CDR_FIL) { 1229 with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread; 1230 is_dir = 0; 1231 } else { 1232 is_dir = 1; 1233 } 1234 1235 /* keep readers from getting confused by changing dir size */ 1236 start_write(parent); 1237 1238 /* don't delete a busy directory */ 1239 if (entry->type == HFS_CDR_DIR) { 1240 start_read(entry); 1241 1242 error = -ENOTEMPTY; 1243 if (entry->u.dir.files || entry->u.dir.dirs) 1244 goto hfs_delete_end; 1245 } 1246 1247 /* try to delete the file or directory */ 1248 lock_entry(entry); 1249 error = -ENOENT; 1250 if ((entry->state & HFS_DELETED)) { 1251 /* somebody beat us to it. */ 1252 goto hfs_delete_unlock; 1253 } 1254 1255 /* delete the catalog record */ 1256 if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) { 1257 goto hfs_delete_unlock; 1258 } 1259 1260 /* Mark the entry deleted and remove it from the cache */ 1261 delete_entry(entry); 1262 1263 /* try to delete the thread entry if it exists */ 1264 if (with_thread) { 1265 hfs_cat_build_key(entry->cnid, NULL, &key); 1266 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key)); 1267 } 1268 1269 update_dir(mdb, parent, is_dir, -1); 1270 1271hfs_delete_unlock: 1272 unlock_entry(entry); 1273 1274hfs_delete_end: 1275 if (entry->type == HFS_CDR_DIR) { 1276 end_read(entry); 1277 } 1278 end_write(parent); 1279 return error; 1280} 1281 1282/* 1283 * hfs_cat_move() 1284 * 1285 * Rename a file or directory, possibly to a new directory. 1286 * If the destination exists it is removed and a 1287 * (struct hfs_cat_entry) for it is returned in '*result'. 1288 */ 1289int hfs_cat_move(struct hfs_cat_entry *old_dir, struct hfs_cat_entry *new_dir, 1290 struct hfs_cat_entry *entry, struct hfs_cat_key *new_key, 1291 struct hfs_cat_entry **removed) 1292{ 1293 struct hfs_cat_entry *dest; 1294 struct hfs_mdb *mdb; 1295 int error = 0; 1296 int is_dir, has_thread; 1297 1298 if (removed) { 1299 *removed = NULL; 1300 } 1301 1302 /* sanity checks */ 1303 if (!old_dir || !new_dir) { 1304 return -EINVAL; 1305 } 1306 mdb = old_dir->mdb; 1307 if (mdb != new_dir->mdb) { 1308 return -EXDEV; 1309 } 1310 1311 /* precompute a few things */ 1312 if (entry->type == HFS_CDR_DIR) { 1313 is_dir = 1; 1314 has_thread = 1; 1315 } else if (entry->type == HFS_CDR_FIL) { 1316 is_dir = 0; 1317 has_thread = entry->u.file.flags & HFS_FIL_THD; 1318 } else { 1319 return -EINVAL; 1320 } 1321 1322 while (mdb->rename_lock) { 1323 hfs_sleep_on(&mdb->rename_wait); 1324 } 1325 spin_lock(&entry_lock); 1326 mdb->rename_lock = 1; 1327 spin_unlock(&entry_lock); 1328 1329 /* keep readers from getting confused by changing dir size */ 1330 start_write(new_dir); 1331 if (old_dir != new_dir) { 1332 start_write(old_dir); 1333 } 1334 1335 /* Don't move a directory inside itself */ 1336 if (is_dir) { 1337 struct hfs_cat_key thd_key; 1338 struct hfs_brec brec; 1339 1340 hfs_u32 id = new_dir->cnid; 1341 while (id != htonl(HFS_ROOT_CNID)) { 1342 if (id == entry->cnid) { 1343 error = -EINVAL; 1344 } else { 1345 hfs_cat_build_key(id, NULL, &thd_key); 1346 error = hfs_bfind(&brec, mdb->cat_tree, 1347 HFS_BKEY(&thd_key), 1348 HFS_BFIND_READ_EQ); 1349 } 1350 if (error) { 1351 goto done; 1352 } else { 1353 struct hfs_cat_rec *rec = brec.data; 1354 id = hfs_get_nl(rec->u.thd.ParID); 1355 hfs_brec_relse(&brec, NULL); 1356 } 1357 } 1358 } 1359 1360restart: 1361 /* see if the destination exists, getting it if it does */ 1362 dest = hfs_cat_get(mdb, new_key); 1363 if (!dest) { 1364 /* destination doesn't exist, so create it */ 1365 struct hfs_cat_rec new_record; 1366 1367 /* create a locked entry in the cache */ 1368 dest = get_entry(mdb, new_key, 0); 1369 if (!dest) { 1370 error = -EIO; 1371 goto done; 1372 } 1373 if (dest->cnid) { 1374 /* The (unlocked) entry exists in the cache */ 1375 goto have_distinct; 1376 } 1377 1378 /* limit directory valence to signed 16-bit integer */ 1379 if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >= 1380 HFS_MAX_VALENCE) { 1381 error = -ENOSPC; 1382 goto bail3; 1383 } 1384 1385 /* build the new record. make sure to zero out the 1386 record. */ 1387 memset(&new_record, 0, sizeof(new_record)); 1388 new_record.cdrType = entry->type; 1389 __write_entry(entry, &new_record); 1390 1391 /* insert the new record */ 1392 error = hfs_binsert(mdb->cat_tree, HFS_BKEY(new_key), 1393 &new_record, is_dir ? 2 + sizeof(DIR_REC) : 1394 2 + sizeof(FIL_REC)); 1395 if (error == -EEXIST) { 1396 delete_entry(dest); 1397 unlock_entry(dest); 1398 hfs_cat_put(dest); 1399 goto restart; 1400 } else if (error) { 1401 goto bail3; 1402 } 1403 1404 /* update the destination directory */ 1405 update_dir(mdb, new_dir, is_dir, 1); 1406 } else if (entry != dest) { 1407have_distinct: 1408 /* The destination exists and is not same as source */ 1409 lock_entry(dest); 1410 if ((dest->state & HFS_DELETED)) { 1411 unlock_entry(dest); 1412 hfs_cat_put(dest); 1413 goto restart; 1414 } 1415 if (dest->type != entry->type) { 1416 /* can't move a file on top 1417 of a dir nor vice versa. */ 1418 error = is_dir ? -ENOTDIR : -EISDIR; 1419 } else if (is_dir && (dest->u.dir.dirs || dest->u.dir.files)) { 1420 /* directory to replace is not empty */ 1421 error = -ENOTEMPTY; 1422 } 1423 1424 if (error) { 1425 goto bail2; 1426 } 1427 } else { 1428 /* The destination exists but is same as source */ 1429 --entry->count; 1430 dest = NULL; 1431 } 1432 1433 /* lock the entry */ 1434 lock_entry(entry); 1435 if ((entry->state & HFS_DELETED)) { 1436 error = -ENOENT; 1437 goto bail1; 1438 } 1439 1440 if (dest) { 1441 /* remove the old entry */ 1442 error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)); 1443 1444 if (error) { 1445 /* We couldn't remove the entry for the 1446 original file, so nothing has changed. */ 1447 goto bail1; 1448 } 1449 update_dir(mdb, old_dir, is_dir, -1); 1450 } 1451 1452 /* update the thread of the dir/file we're moving */ 1453 if (has_thread) { 1454 struct hfs_cat_key thd_key; 1455 struct hfs_brec brec; 1456 1457 hfs_cat_build_key(entry->cnid, NULL, &thd_key); 1458 error = hfs_bfind(&brec, mdb->cat_tree, 1459 HFS_BKEY(&thd_key), HFS_BFIND_WRITE); 1460 if (error == -ENOENT) { 1461 if (is_dir) { 1462 /* directory w/o a thread! */ 1463 error = -EIO; 1464 } else { 1465 /* We were lied to! */ 1466 entry->u.file.flags &= ~HFS_FIL_THD; 1467 hfs_cat_mark_dirty(entry); 1468 } 1469 } 1470 if (!error) { 1471 struct hfs_cat_rec *rec = brec.data; 1472 memcpy(&rec->u.thd.ParID, &new_key->ParID, 1473 sizeof(hfs_u32) + sizeof(struct hfs_name)); 1474 hfs_brec_relse(&brec, NULL); 1475 } else if (error == -ENOENT) { 1476 error = 0; 1477 } else if (!dest) { 1478 /* Nothing was changed */ 1479 unlock_entry(entry); 1480 goto done; 1481 } else { 1482 /* Something went seriously wrong. 1483 The dir/file has been deleted. */ 1484 delete_entry(entry); 1485 goto bail1; 1486 } 1487 } 1488 1489 /* TRY to remove the thread for the pre-existing entry */ 1490 if (dest && dest->cnid && 1491 (is_dir || (dest->u.file.flags & HFS_FIL_THD))) { 1492 struct hfs_cat_key thd_key; 1493 1494 hfs_cat_build_key(dest->cnid, NULL, &thd_key); 1495 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key)); 1496 } 1497 1498 /* update directories */ 1499 new_dir->modify_date = hfs_time(); 1500 hfs_cat_mark_dirty(new_dir); 1501 1502 /* update key */ 1503 remove_hash(entry); 1504 memcpy(&entry->key, new_key, sizeof(*new_key)); 1505 /* KEYDIRTY as case might differ */ 1506 entry->state |= HFS_KEYDIRTY; 1507 insert_hash(entry); 1508 hfs_cat_mark_dirty(entry); 1509 unlock_entry(entry); 1510 1511 /* delete any pre-existing or place-holder entry */ 1512 if (dest) { 1513 delete_entry(dest); 1514 unlock_entry(dest); 1515 if (removed && dest->cnid) { 1516 *removed = dest; 1517 } else { 1518 hfs_cat_put(dest); 1519 } 1520 } 1521 goto done; 1522 1523bail1: 1524 unlock_entry(entry); 1525bail2: 1526 if (dest) { 1527 if (!dest->cnid) { 1528 /* TRY to remove the new entry */ 1529 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(new_key)); 1530 update_dir(mdb, new_dir, is_dir, -1); 1531bail3: 1532 delete_entry(dest); 1533 } 1534 unlock_entry(dest); 1535 hfs_cat_put(dest); 1536 } 1537done: 1538 if (new_dir != old_dir) { 1539 end_write(old_dir); 1540 } 1541 end_write(new_dir); 1542 spin_lock(&entry_lock); 1543 mdb->rename_lock = 0; 1544 hfs_wake_up(&mdb->rename_wait); 1545 spin_unlock(&entry_lock); 1546 1547 return error; 1548} 1549 1550/* 1551 * Initialize the hash tables 1552 */ 1553void hfs_cat_init(void) 1554{ 1555 int i; 1556 struct list_head *head = hash_table; 1557 1558 i = C_HASHSIZE; 1559 do { 1560 INIT_LIST_HEAD(head); 1561 head++; 1562 i--; 1563 } while (i); 1564} 1565