2 * linux/fs/hfs/catalog.c
4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU Public License.
7 * This file contains the functions related to the catalog B-tree.
9 * "XXX" in a comment is a note to myself to consider changing something.
11 * Cache code shamelessly stolen from
12 * linux/fs/inode.c Copyright (C) 1991, 1992 Linus Torvalds
13 * re-shamelessly stolen Copyright (C) 1997 Linus Torvalds
15 * In function preconditions the term "valid" applied to a pointer to
16 * a structure means that the pointer is non-NULL and the structure it
17 * points to has all fields initialized to consistent values.
19 * The code in this file initializes some structures by calling
20 * memset(&foo, 0, sizeof(foo)). This produces the desired behavior
21 * only due to the non-ANSI assumption that the machine representation
26 /*================ Variable-like macros ================*/
28 /* Number of hash table slots */
30 #define C_HASHSIZE (1UL << C_HASHBITS)
31 #define C_HASHMASK (C_HASHSIZE - 1)
33 /* Number of entries to fit in a single page on an i386.
34 * Actually, now it's used to increment the free entry pool. */
35 #define CCACHE_INC (PAGE_SIZE/sizeof(struct hfs_cat_entry))
36 #define CCACHE_MAX (CCACHE_INC * 8)
38 /*================ File-local data types ================*/
40 /* The catalog record for a file */
42 hfs_byte_t Flags
; /* Flags such as read-only */
43 hfs_byte_t Typ
; /* file version number = 0 */
44 hfs_finfo_t UsrWds
; /* data used by the Finder */
45 hfs_lword_t FlNum
; /* The CNID */
46 hfs_word_t StBlk
; /* obsolete */
47 hfs_lword_t LgLen
; /* The logical EOF of the data fork*/
48 hfs_lword_t PyLen
; /* The physical EOF of the data fork */
49 hfs_word_t RStBlk
; /* obsolete */
50 hfs_lword_t RLgLen
; /* The logical EOF of the rsrc fork */
51 hfs_lword_t RPyLen
; /* The physical EOF of the rsrc fork */
52 hfs_lword_t CrDat
; /* The creation date */
53 hfs_lword_t MdDat
; /* The modified date */
54 hfs_lword_t BkDat
; /* The last backup date */
55 hfs_fxinfo_t FndrInfo
; /* more data for the Finder */
56 hfs_word_t ClpSize
; /* number of bytes to allocate
57 when extending files */
58 hfs_byte_t ExtRec
[12]; /* first extent record
60 hfs_byte_t RExtRec
[12]; /* first extent record
61 for the resource fork */
62 hfs_lword_t Resrv
; /* reserved by Apple */
63 } __attribute__((packed
)) FIL_REC
;
65 /* the catalog record for a directory */
67 hfs_word_t Flags
; /* flags */
68 hfs_word_t Val
; /* Valence: number of files and
69 dirs in the directory */
70 hfs_lword_t DirID
; /* The CNID */
71 hfs_lword_t CrDat
; /* The creation date */
72 hfs_lword_t MdDat
; /* The modification date */
73 hfs_lword_t BkDat
; /* The last backup date */
74 hfs_dinfo_t UsrInfo
; /* data used by the Finder */
75 hfs_dxinfo_t FndrInfo
; /* more data used by Finder */
76 hfs_byte_t Resrv
[16]; /* reserved by Apple */
77 } __attribute__((packed
)) DIR_REC
;
79 /* the catalog record for a thread */
81 hfs_byte_t Reserv
[8]; /* reserved by Apple */
82 hfs_lword_t ParID
; /* CNID of parent directory */
83 struct hfs_name CName
; /* The name of this entry */
84 } __attribute__((packed
)) THD_REC
;
86 /* A catalog tree record */
88 hfs_byte_t cdrType
; /* The type of entry */
89 hfs_byte_t cdrResrv2
; /* padding */
95 } __attribute__((packed
));
97 /*================ File-local variables ================*/
99 static LIST_HEAD(entry_in_use
);
100 static LIST_HEAD(entry_unused
);
101 static struct list_head hash_table
[C_HASHSIZE
];
103 spinlock_t entry_lock
= SPIN_LOCK_UNLOCKED
;
110 /*================ File-local functions ================*/
115 * Get the CNID from a brec
117 static inline hfs_u32
brec_to_id(struct hfs_brec
*brec
)
119 struct hfs_cat_rec
*rec
= brec
->data
;
121 return hfs_get_nl((rec
->cdrType
==HFS_CDR_FIL
) ?
122 rec
->u
.fil
.FlNum
: rec
->u
.dir
.DirID
);
128 * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
130 static inline unsigned int hashfn(const struct hfs_mdb
*mdb
,
131 const struct hfs_cat_key
*key
)
135 hash
= (unsigned long) mdb
| (unsigned long) key
->ParID
[3] |
136 hfs_strhash(key
->CName
.Name
, key
->CName
.Len
);
137 hash
= hash
^ (hash
>> C_HASHBITS
) ^ (hash
>> C_HASHBITS
*2);
138 return hash
& C_HASHMASK
;
144 * hash an (struct mdb *) and a (struct hfs_cat_key *)
145 * to a pointer to a slot in the hash table.
147 static inline struct list_head
*hash(struct hfs_mdb
*mdb
,
148 const struct hfs_cat_key
*key
)
150 return hash_table
+ hashfn(mdb
, key
);
153 static inline void insert_hash(struct hfs_cat_entry
*entry
)
155 struct list_head
*head
= hash(entry
->mdb
, &entry
->key
);
156 list_add(&entry
->hash
, head
);
159 static inline void remove_hash(struct hfs_cat_entry
*entry
)
161 list_del(&entry
->hash
);
162 INIT_LIST_HEAD(&entry
->hash
);
168 * Sleep until a locked entry is unlocked.
170 static inline void wait_on_entry(struct hfs_cat_entry
* entry
)
172 while ((entry
->state
& HFS_LOCK
)) {
173 hfs_sleep_on(&entry
->wait
);
180 * Obtain an exclusive lock on an entry.
182 static void lock_entry(struct hfs_cat_entry
* entry
)
184 wait_on_entry(entry
);
185 spin_lock(&entry_lock
);
186 entry
->state
|= HFS_LOCK
;
187 spin_unlock(&entry_lock
);
193 * Relinquish an exclusive lock on an entry.
195 static void unlock_entry(struct hfs_cat_entry
* entry
)
197 spin_lock(&entry_lock
);
198 entry
->state
&= ~HFS_LOCK
;
199 spin_unlock(&entry_lock
);
200 hfs_wake_up(&entry
->wait
);
203 /* put entry on mdb dirty list. */
204 void hfs_cat_mark_dirty(struct hfs_cat_entry
*entry
)
206 struct hfs_mdb
*mdb
= entry
->mdb
;
208 spin_lock(&entry_lock
);
209 if (!(entry
->state
& HFS_DIRTY
)) {
210 entry
->state
|= HFS_DIRTY
;
212 /* Only add valid (ie hashed) entries to the dirty list. */
213 if (!list_empty(&entry
->hash
)) {
214 list_del(&entry
->list
);
215 list_add(&entry
->list
, &mdb
->entry_dirty
);
218 spin_unlock(&entry_lock
);
221 /* delete an entry and remove it from the hash table. */
222 static void delete_entry(struct hfs_cat_entry
*entry
)
224 if (!(entry
->state
& HFS_DELETED
)) {
225 entry
->state
|= HFS_DELETED
;
226 list_del(&entry
->hash
);
227 INIT_LIST_HEAD(&entry
->hash
);
229 if (entry
->type
== HFS_CDR_FIL
) {
230 /* free all extents */
231 entry
->u
.file
.data_fork
.lsize
= 0;
232 hfs_extent_adj(&entry
->u
.file
.data_fork
);
233 entry
->u
.file
.rsrc_fork
.lsize
= 0;
234 hfs_extent_adj(&entry
->u
.file
.rsrc_fork
);
240 static inline void init_entry(struct hfs_cat_entry
*entry
)
242 memset(entry
, 0, sizeof(*entry
));
243 hfs_init_waitqueue(&entry
->wait
);
244 INIT_LIST_HEAD(&entry
->hash
);
245 INIT_LIST_HEAD(&entry
->list
);
251 * Try to allocate another entry.
253 static inline struct hfs_cat_entry
*hfs_cat_alloc(void)
255 struct hfs_cat_entry
*entry
;
264 /* this gets called with the spinlock held. */
265 static int grow_entries(void)
267 struct hfs_cat_entry
*entry
;
270 for (i
= 0; i
< CCACHE_INC
; i
++) {
271 if (!(entry
= hfs_cat_alloc()))
273 list_add(&entry
->list
, &entry_unused
);
276 entries_stat
.nr_entries
+= i
;
277 entries_stat
.nr_free_entries
+= i
;
285 * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
287 static void __read_entry(struct hfs_cat_entry
*entry
,
288 const struct hfs_cat_rec
*cat
)
290 entry
->type
= cat
->cdrType
;
292 if (cat
->cdrType
== HFS_CDR_DIR
) {
293 struct hfs_dir
*dir
= &entry
->u
.dir
;
295 entry
->cnid
= hfs_get_nl(cat
->u
.dir
.DirID
);
297 dir
->magic
= HFS_DIR_MAGIC
;
298 dir
->flags
= hfs_get_ns(cat
->u
.dir
.Flags
);
299 memcpy(&entry
->info
.dir
.dinfo
, &cat
->u
.dir
.UsrInfo
, 16);
300 memcpy(&entry
->info
.dir
.dxinfo
, &cat
->u
.dir
.FndrInfo
, 16);
301 entry
->create_date
= hfs_get_nl(cat
->u
.dir
.CrDat
);
302 entry
->modify_date
= hfs_get_nl(cat
->u
.dir
.MdDat
);
303 entry
->backup_date
= hfs_get_nl(cat
->u
.dir
.BkDat
);
304 dir
->dirs
= dir
->files
= 0;
305 hfs_init_waitqueue(&dir
->read_wait
);
306 hfs_init_waitqueue(&dir
->write_wait
);
307 } else if (cat
->cdrType
== HFS_CDR_FIL
) {
308 struct hfs_file
*fil
= &entry
->u
.file
;
310 entry
->cnid
= hfs_get_nl(cat
->u
.fil
.FlNum
);
312 fil
->magic
= HFS_FILE_MAGIC
;
314 fil
->data_fork
.fork
= HFS_FK_DATA
;
315 fil
->data_fork
.entry
= entry
;
316 fil
->data_fork
.lsize
= hfs_get_hl(cat
->u
.fil
.LgLen
);
317 fil
->data_fork
.psize
= hfs_get_hl(cat
->u
.fil
.PyLen
) >>
318 HFS_SECTOR_SIZE_BITS
;
319 hfs_extent_in(&fil
->data_fork
, cat
->u
.fil
.ExtRec
);
321 fil
->rsrc_fork
.fork
= HFS_FK_RSRC
;
322 fil
->rsrc_fork
.entry
= entry
;
323 fil
->rsrc_fork
.lsize
= hfs_get_hl(cat
->u
.fil
.RLgLen
);
324 fil
->rsrc_fork
.psize
= hfs_get_hl(cat
->u
.fil
.RPyLen
) >>
325 HFS_SECTOR_SIZE_BITS
;
326 hfs_extent_in(&fil
->rsrc_fork
, cat
->u
.fil
.RExtRec
);
328 memcpy(&entry
->info
.file
.finfo
, &cat
->u
.fil
.UsrWds
, 16);
329 memcpy(&entry
->info
.file
.fxinfo
, &cat
->u
.fil
.FndrInfo
, 16);
331 entry
->create_date
= hfs_get_nl(cat
->u
.fil
.CrDat
);
332 entry
->modify_date
= hfs_get_nl(cat
->u
.fil
.MdDat
);
333 entry
->backup_date
= hfs_get_nl(cat
->u
.fil
.BkDat
);
334 fil
->clumpablks
= (hfs_get_hs(cat
->u
.fil
.ClpSize
)
335 / entry
->mdb
->alloc_blksz
)
336 >> HFS_SECTOR_SIZE_BITS
;
337 fil
->flags
= cat
->u
.fil
.Flags
;
339 hfs_warn("hfs_fs: entry is neither file nor directory!\n");
344 * count_dir_entries()
346 * Count the number of files and directories in a given directory.
348 static inline void count_dir_entries(struct hfs_cat_entry
*entry
,
349 struct hfs_brec
*brec
)
355 if (!hfs_cat_open(entry
, brec
)) {
356 while (!(error
= hfs_cat_next(entry
, brec
, 1, &cnid
, &type
))) {
357 if (type
== HFS_CDR_FIL
) {
358 ++entry
->u
.dir
.files
;
359 } else if (type
== HFS_CDR_DIR
) {
362 } /* -ENOENT is normal termination */
364 if (error
!= -ENOENT
) {
372 * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
374 static inline void read_entry(struct hfs_cat_entry
*entry
,
375 struct hfs_brec
*brec
)
378 struct hfs_cat_rec
*rec
= brec
->data
;
380 __read_entry(entry
, rec
);
382 need_count
= (rec
->cdrType
== HFS_CDR_DIR
) && rec
->u
.dir
.Val
;
384 hfs_brec_relse(brec
, NULL
);
387 count_dir_entries(entry
, brec
);
394 * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
396 static void __write_entry(const struct hfs_cat_entry
*entry
,
397 struct hfs_cat_rec
*cat
)
399 if (entry
->type
== HFS_CDR_DIR
) {
400 const struct hfs_dir
*dir
= &entry
->u
.dir
;
402 hfs_put_ns(dir
->flags
, cat
->u
.dir
.Flags
);
403 hfs_put_hs(dir
->dirs
+ dir
->files
, cat
->u
.dir
.Val
);
404 hfs_put_nl(entry
->cnid
, cat
->u
.dir
.DirID
);
405 hfs_put_nl(entry
->create_date
, cat
->u
.dir
.CrDat
);
406 hfs_put_nl(entry
->modify_date
, cat
->u
.dir
.MdDat
);
407 hfs_put_nl(entry
->backup_date
, cat
->u
.dir
.BkDat
);
408 memcpy(&cat
->u
.dir
.UsrInfo
, &entry
->info
.dir
.dinfo
, 16);
409 memcpy(&cat
->u
.dir
.FndrInfo
, &entry
->info
.dir
.dxinfo
, 16);
410 } else if (entry
->type
== HFS_CDR_FIL
) {
411 const struct hfs_file
*fil
= &entry
->u
.file
;
413 cat
->u
.fil
.Flags
= fil
->flags
;
414 hfs_put_nl(entry
->cnid
, cat
->u
.fil
.FlNum
);
415 memcpy(&cat
->u
.fil
.UsrWds
, &entry
->info
.file
.finfo
, 16);
416 hfs_put_hl(fil
->data_fork
.lsize
, cat
->u
.fil
.LgLen
);
417 hfs_put_hl(fil
->data_fork
.psize
<< HFS_SECTOR_SIZE_BITS
,
419 hfs_put_hl(fil
->rsrc_fork
.lsize
, cat
->u
.fil
.RLgLen
);
420 hfs_put_hl(fil
->rsrc_fork
.psize
<< HFS_SECTOR_SIZE_BITS
,
422 hfs_put_nl(entry
->create_date
, cat
->u
.fil
.CrDat
);
423 hfs_put_nl(entry
->modify_date
, cat
->u
.fil
.MdDat
);
424 hfs_put_nl(entry
->backup_date
, cat
->u
.fil
.BkDat
);
425 memcpy(&cat
->u
.fil
.FndrInfo
, &entry
->info
.file
.fxinfo
, 16);
426 hfs_put_hs((fil
->clumpablks
* entry
->mdb
->alloc_blksz
)
427 << HFS_SECTOR_SIZE_BITS
, cat
->u
.fil
.ClpSize
);
428 hfs_extent_out(&fil
->data_fork
, cat
->u
.fil
.ExtRec
);
429 hfs_extent_out(&fil
->rsrc_fork
, cat
->u
.fil
.RExtRec
);
431 hfs_warn("__write_entry: invalid entry\n");
438 * Write a modified entry back to the catalog B-tree. this gets called
439 * with the entry locked.
441 static void write_entry(struct hfs_cat_entry
* entry
)
443 struct hfs_brec brec
;
446 if (!(entry
->state
& HFS_DELETED
)) {
447 error
= hfs_bfind(&brec
, entry
->mdb
->cat_tree
,
448 HFS_BKEY(&entry
->key
), HFS_BFIND_WRITE
);
450 if ((entry
->state
& HFS_KEYDIRTY
)) {
451 /* key may have changed case due to a rename */
452 entry
->state
&= ~HFS_KEYDIRTY
;
453 if (brec
.key
->KeyLen
!= entry
->key
.KeyLen
) {
454 hfs_warn("hfs_write_entry: key length "
458 memcpy(brec
.key
, &entry
->key
,
461 } else if (entry
->cnid
!= brec_to_id(&brec
)) {
462 hfs_warn("hfs_write_entry: CNID "
463 "changed unexpectedly!\n");
467 __write_entry(entry
, brec
.data
);
469 hfs_brec_relse(&brec
, NULL
);
472 hfs_warn("hfs_write_entry: unable to write "
473 "entry %08x\n", entry
->cnid
);
479 /* this gets called with the spinlock held. */
480 static struct hfs_cat_entry
*find_entry(struct hfs_mdb
*mdb
,
481 const struct hfs_cat_key
*key
)
483 struct list_head
*tmp
, *head
= hash(mdb
, key
);
484 struct hfs_cat_entry
* entry
;
492 entry
= list_entry(tmp
, struct hfs_cat_entry
, hash
);
493 if (entry
->mdb
!= mdb
)
495 if (hfs_cat_compare(&entry
->key
, key
)) {
506 /* be careful. this gets called with the spinlock held. */
507 static struct hfs_cat_entry
*get_new_entry(struct hfs_mdb
*mdb
,
508 const struct hfs_cat_key
*key
,
511 struct hfs_cat_entry
*entry
;
512 struct list_head
*head
= hash(mdb
, key
);
513 struct list_head
*tmp
;
516 tmp
= entry_unused
.next
;
517 if ((tmp
!= &entry_unused
) ) {
519 entries_stat
.nr_free_entries
--;
520 entry
= list_entry(tmp
, struct hfs_cat_entry
, list
);
521 list_add(&entry
->list
, &entry_in_use
);
522 list_add(&entry
->hash
, head
);
525 memcpy(&entry
->key
, key
, sizeof(*key
));
526 entry
->state
= HFS_LOCK
;
527 spin_unlock(&entry_lock
);
530 struct hfs_brec brec
;
532 if (hfs_bfind(&brec
, mdb
->cat_tree
,
533 HFS_BKEY(key
), HFS_BFIND_READ_EQ
)) {
534 /* uh oh. we failed to read the record.
535 * the entry doesn't actually exist. */
539 read_entry(entry
, &brec
);
546 /* we don't have to acquire a spinlock here or
547 * below for the unlocking bits as we're the first
548 * user of this entry. */
549 entry
->state
&= ~HFS_LOCK
;
550 hfs_wake_up(&entry
->wait
);
557 /* try to allocate more entries. grow_entries() doesn't release
562 spin_unlock(&entry_lock
);
566 /* short-cut hfs_cat_put by doing everything here. */
567 spin_lock(&entry_lock
);
568 list_del(&entry
->hash
);
569 list_del(&entry
->list
);
571 list_add(&entry
->list
, &entry_unused
);
572 entries_stat
.nr_free_entries
++;
573 spin_unlock(&entry_lock
);
580 * Try to return an entry for the indicated file or directory.
581 * If ('read' == 0) then no attempt will be made to read it from disk
582 * and a locked, but uninitialized, entry is returned.
584 static struct hfs_cat_entry
*get_entry(struct hfs_mdb
*mdb
,
585 const struct hfs_cat_key
*key
,
588 struct hfs_cat_entry
* entry
;
590 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
591 hfs_warn("hfs_get_entry: mdb=%p key=%s read=%d\n",
592 mdb
, key
->CName
.Name
, read
);
595 spin_lock(&entry_lock
);
596 entry
= find_entry(mdb
, key
);
598 return get_new_entry(mdb
, key
, read
);
600 spin_unlock(&entry_lock
);
601 wait_on_entry(entry
);
608 * Allocate a CNID to use for a new file or directory.
610 static inline hfs_u32
new_cnid(struct hfs_mdb
*mdb
)
612 /* If the create succeeds then the mdb will get dirtied */
613 return htonl(mdb
->next_id
++);
619 * Update counts, times and dirt on a changed directory
621 static void update_dir(struct hfs_mdb
*mdb
, struct hfs_cat_entry
*dir
,
622 int is_dir
, int count
)
626 mdb
->dir_count
+= count
;
627 dir
->u
.dir
.dirs
+= count
;
628 if (dir
->cnid
== htonl(HFS_ROOT_CNID
)) {
629 mdb
->root_dirs
+= count
;
632 mdb
->file_count
+= count
;
633 dir
->u
.dir
.files
+= count
;
634 if (dir
->cnid
== htonl(HFS_ROOT_CNID
)) {
635 mdb
->root_files
+= count
;
639 /* update times and dirt */
640 dir
->modify_date
= hfs_time();
641 hfs_cat_mark_dirty(dir
);
645 * Add a writer to dir, excluding readers.
647 * XXX: this is wrong. it allows a move to occur when a directory
648 * is being written to.
650 static inline void start_write(struct hfs_cat_entry
*dir
)
652 if (dir
->u
.dir
.readers
|| waitqueue_active(&dir
->u
.dir
.read_wait
)) {
653 hfs_sleep_on(&dir
->u
.dir
.write_wait
);
655 ++dir
->u
.dir
.writers
;
659 * Add a reader to dir, excluding writers.
661 static inline void start_read(struct hfs_cat_entry
*dir
)
663 if (dir
->u
.dir
.writers
|| waitqueue_active(&dir
->u
.dir
.write_wait
)) {
664 hfs_sleep_on(&dir
->u
.dir
.read_wait
);
666 ++dir
->u
.dir
.readers
;
670 * Remove a writer from dir, possibly admitting readers.
672 static inline void end_write(struct hfs_cat_entry
*dir
)
674 if (!(--dir
->u
.dir
.writers
)) {
675 hfs_wake_up(&dir
->u
.dir
.read_wait
);
680 * Remove a reader from dir, possibly admitting writers.
682 static inline void end_read(struct hfs_cat_entry
*dir
)
684 if (!(--dir
->u
.dir
.readers
)) {
685 hfs_wake_up(&dir
->u
.dir
.write_wait
);
692 * Add a new file or directory to the catalog B-tree and
693 * return a (struct hfs_cat_entry) for it in '*result'.
695 static int create_entry(struct hfs_cat_entry
*parent
, struct hfs_cat_key
*key
,
696 const struct hfs_cat_rec
*record
, int is_dir
,
697 hfs_u32 cnid
, struct hfs_cat_entry
**result
)
699 struct hfs_mdb
*mdb
= parent
->mdb
;
700 struct hfs_cat_entry
*entry
;
701 struct hfs_cat_key thd_key
;
702 struct hfs_cat_rec thd_rec
;
703 int error
, has_thread
;
709 /* keep readers from getting confused by changing dir size */
712 /* create a locked entry in the cache */
713 entry
= get_entry(mdb
, key
, 0);
715 /* The entry exists but can't be read */
721 /* The (unlocked) entry exists in the cache */
726 /* limit directory valence to signed 16-bit integer */
727 if ((parent
->u
.dir
.dirs
+ parent
->u
.dir
.files
) >= HFS_MAX_VALENCE
) {
732 has_thread
= is_dir
|| (record
->u
.fil
.Flags
& HFS_FIL_THD
);
735 /* init some fields for the thread record */
736 memset(&thd_rec
, 0, sizeof(thd_rec
));
737 thd_rec
.cdrType
= is_dir
? HFS_CDR_THD
: HFS_CDR_FTH
;
738 memcpy(&thd_rec
.u
.thd
.ParID
, &key
->ParID
,
739 sizeof(hfs_u32
) + sizeof(struct hfs_name
));
741 /* insert the thread record */
742 hfs_cat_build_key(cnid
, NULL
, &thd_key
);
743 error
= hfs_binsert(mdb
->cat_tree
, HFS_BKEY(&thd_key
),
744 &thd_rec
, 2 + sizeof(THD_REC
));
750 /* insert the record */
751 error
= hfs_binsert(mdb
->cat_tree
, HFS_BKEY(key
), record
,
752 is_dir
? 2 + sizeof(DIR_REC
) :
753 2 + sizeof(FIL_REC
));
755 if (has_thread
&& (error
!= -EIO
)) {
756 /* at least TRY to remove the thread record */
757 (void)hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(&thd_key
));
762 /* update the parent directory */
763 update_dir(mdb
, parent
, is_dir
, 1);
765 /* complete the cache entry and return success */
766 __read_entry(entry
, record
);
777 /* entry really didn't exist, so we don't need to really delete it.
778 * we do need to remove it from the hash, though. */
779 entry
->state
|= HFS_DELETED
;
789 /*================ Global functions ================*/
794 * Release an entry we aren't using anymore.
796 * nothing in hfs_cat_put goes to sleep now except on the initial entry.
798 void hfs_cat_put(struct hfs_cat_entry
* entry
)
801 wait_on_entry(entry
);
803 /* just in case. this should never happen. */
805 hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
810 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
811 hfs_warn("hfs_cat_put: %p(%u) type=%d state=%lu\n",
812 entry
, entry
->count
, entry
->type
, entry
->state
);
814 spin_lock(&entry_lock
);
815 if (!--entry
->count
) {
816 if ((entry
->state
& HFS_DELETED
))
819 if ((entry
->type
== HFS_CDR_FIL
)) {
820 /* clear out any cached extents */
821 if (entry
->u
.file
.data_fork
.first
.next
) {
822 hfs_extent_free(&entry
->u
.file
.data_fork
);
824 if (entry
->u
.file
.rsrc_fork
.first
.next
) {
825 hfs_extent_free(&entry
->u
.file
.rsrc_fork
);
829 /* if we put a dirty entry, write it out. */
830 if ((entry
->state
& HFS_DIRTY
)) {
831 entry
->state
^= HFS_DIRTY
| HFS_LOCK
;
833 entry
->state
&= ~HFS_LOCK
;
836 list_del(&entry
->hash
);
837 entry_deleted
: /* deleted entries have already been removed
838 * from the hash list. */
839 list_del(&entry
->list
);
840 if (entries_stat
.nr_free_entries
> CCACHE_MAX
) {
842 entries_stat
.nr_entries
--;
845 list_add(&entry
->list
, &entry_unused
);
846 entries_stat
.nr_free_entries
++;
849 spin_unlock(&entry_lock
);
856 * Wrapper for get_entry() which always calls with ('read'==1).
857 * Used for access to get_entry() from outside this file.
859 struct hfs_cat_entry
*hfs_cat_get(struct hfs_mdb
*mdb
,
860 const struct hfs_cat_key
*key
)
862 return get_entry(mdb
, key
, 1);
865 /* invalidate all entries for a device */
866 static void invalidate_list(struct list_head
*head
, struct hfs_mdb
*mdb
,
867 struct list_head
*dispose
)
869 struct list_head
*next
;
873 struct list_head
*tmp
= next
;
874 struct hfs_cat_entry
* entry
;
879 entry
= list_entry(tmp
, struct hfs_cat_entry
, list
);
880 if (entry
->mdb
!= mdb
) {
885 list_del(&entry
->hash
);
886 INIT_LIST_HEAD(&entry
->hash
);
887 list_del(&entry
->list
);
888 list_add(&entry
->list
, dispose
);
892 hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n",
894 hfs_mdb_name(entry
->mdb
->sys_mdb
));
898 /* delete entries from a list */
899 static void delete_list(struct list_head
*head
)
901 struct list_head
*next
= head
->next
;
902 struct hfs_cat_entry
*entry
;
905 struct list_head
* tmp
= next
;
911 entry
= list_entry(tmp
, struct hfs_cat_entry
, list
);
917 * hfs_cat_invalidate()
919 * Called by hfs_mdb_put() to remove all the entries
920 * in the cache that are associated with a given MDB.
922 void hfs_cat_invalidate(struct hfs_mdb
*mdb
)
924 LIST_HEAD(throw_away
);
926 spin_lock(&entry_lock
);
927 invalidate_list(&entry_in_use
, mdb
, &throw_away
);
928 invalidate_list(&mdb
->entry_dirty
, mdb
, &throw_away
);
929 spin_unlock(&entry_lock
);
931 delete_list(&throw_away
);
932 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
933 hfs_warn("hfs_cat_invalidate: free=%d total=%d\n",
934 entries_stat
.nr_free_entries
,
935 entries_stat
.nr_entries
);
942 * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
944 void hfs_cat_commit(struct hfs_mdb
*mdb
)
946 struct list_head
*tmp
, *head
= &mdb
->entry_dirty
;
947 struct hfs_cat_entry
*entry
;
949 spin_lock(&entry_lock
);
950 while ((tmp
= head
->prev
) != head
) {
951 entry
= list_entry(tmp
, struct hfs_cat_entry
, list
);
953 if ((entry
->state
& HFS_LOCK
)) {
954 spin_unlock(&entry_lock
);
955 wait_on_entry(entry
);
956 spin_lock(&entry_lock
);
958 struct list_head
*insert
= &entry_in_use
;
961 insert
= entry_in_use
.prev
;
963 /* add to in_use list */
964 list_del(&entry
->list
);
965 list_add(&entry
->list
, insert
);
967 /* reset DIRTY, set LOCK */
968 entry
->state
^= HFS_DIRTY
| HFS_LOCK
;
969 spin_unlock(&entry_lock
);
971 spin_lock(&entry_lock
);
972 entry
->state
&= ~HFS_LOCK
;
973 hfs_wake_up(&entry
->wait
);
976 spin_unlock(&entry_lock
);
982 * Releases all the memory allocated in grow_entries().
983 * Must call hfs_cat_invalidate() on all MDBs before calling this.
984 * This only gets rid of the unused pool of entries. all the other
985 * entry references should have either been freed by cat_invalidate
986 * or moved onto the unused list.
988 void hfs_cat_free(void)
990 delete_list(&entry_unused
);
997 * This is the comparison function used for the catalog B-tree. In
998 * comparing catalog B-tree entries, the parent id is the most
999 * significant field (compared as unsigned ints). The name field is
1000 * the least significant (compared in "Macintosh lexical order",
1001 * see hfs_strcmp() in string.c)
1002 * Input Variable(s):
1003 * struct hfs_cat_key *key1: pointer to the first key to compare
1004 * struct hfs_cat_key *key2: pointer to the second key to compare
1005 * Output Variable(s):
1008 * int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
1010 * key1 and key2 point to "valid" (struct hfs_cat_key)s.
1012 * This function has no side-effects
1014 int hfs_cat_compare(const struct hfs_cat_key
*key1
,
1015 const struct hfs_cat_key
*key2
)
1017 unsigned int parents
;
1020 parents
= hfs_get_hl(key1
->ParID
) - hfs_get_hl(key2
->ParID
);
1022 retval
= (int)parents
;
1024 retval
= hfs_strcmp(key1
->CName
.Name
, key1
->CName
.Len
,
1025 key2
->CName
.Name
, key2
->CName
.Len
);
1031 * hfs_cat_build_key()
1033 * Given the ID of the parent and the name build a search key.
1035 void hfs_cat_build_key(hfs_u32 parent
, const struct hfs_name
*cname
,
1036 struct hfs_cat_key
*key
)
1038 hfs_put_nl(parent
, key
->ParID
);
1041 key
->KeyLen
= 6 + cname
->Len
;
1042 memcpy(&key
->CName
, cname
, sizeof(*cname
));
1045 memset(&key
->CName
, 0, sizeof(*cname
));
1052 * Given a directory on an HFS filesystem get its thread and
1053 * lock the directory against insertions and deletions.
1054 * Return 0 on success or an error code on failure.
1056 int hfs_cat_open(struct hfs_cat_entry
*dir
, struct hfs_brec
*brec
)
1058 struct hfs_cat_key key
;
1061 if (dir
->type
!= HFS_CDR_DIR
) {
1068 /* Find the directory */
1069 hfs_cat_build_key(dir
->cnid
, NULL
, &key
);
1070 error
= hfs_bfind(brec
, dir
->mdb
->cat_tree
,
1071 HFS_BKEY(&key
), HFS_BFIND_READ_EQ
);
1083 * Given a catalog brec structure, replace it with the count'th next brec
1084 * in the same directory.
1085 * Return an error code if there is a problem, 0 if OK.
1086 * Note that an error code of -ENOENT means there are no more entries
1087 * in this directory.
1088 * The directory is "closed" on an error.
1090 int hfs_cat_next(struct hfs_cat_entry
*dir
, struct hfs_brec
*brec
,
1091 hfs_u16 count
, hfs_u32
*cnid
, hfs_u8
*type
)
1095 if (!dir
|| !brec
) {
1099 /* Get the count'th next catalog tree entry */
1100 error
= hfs_bsucc(brec
, count
);
1102 struct hfs_cat_key
*key
= (struct hfs_cat_key
*)brec
->key
;
1103 if (hfs_get_nl(key
->ParID
) != dir
->cnid
) {
1104 hfs_brec_relse(brec
, NULL
);
1109 *type
= ((struct hfs_cat_rec
*)brec
->data
)->cdrType
;
1110 *cnid
= brec_to_id(brec
);
1120 * Given a catalog brec structure, replace it with the count'th next brec
1121 * in the same directory.
1122 * Return an error code if there is a problem, 0 if OK.
1123 * Note that an error code of -ENOENT means there are no more entries
1124 * in this directory.
1126 void hfs_cat_close(struct hfs_cat_entry
*dir
, struct hfs_brec
*brec
)
1129 hfs_brec_relse(brec
, NULL
);
1137 * Given a catalog entry, return the entry for its parent.
1138 * Uses catalog key for the entry to get its parent's ID
1139 * and then uses the parent's thread record to locate the
1140 * parent's actual catalog entry.
1142 struct hfs_cat_entry
*hfs_cat_parent(struct hfs_cat_entry
*entry
)
1144 struct hfs_cat_entry
*retval
= NULL
;
1145 struct hfs_mdb
*mdb
= entry
->mdb
;
1146 struct hfs_brec brec
;
1147 struct hfs_cat_key key
;
1151 if (!(entry
->state
& HFS_DELETED
)) {
1152 hfs_cat_build_key(hfs_get_nl(entry
->key
.ParID
), NULL
, &key
);
1153 error
= hfs_bfind(&brec
, mdb
->cat_tree
,
1154 HFS_BKEY(&key
), HFS_BFIND_READ_EQ
);
1156 /* convert thread record to key */
1157 struct hfs_cat_rec
*rec
= brec
.data
;
1158 key
.KeyLen
= 6 + rec
->u
.thd
.CName
.Len
;
1159 memcpy(&key
.ParID
, &rec
->u
.thd
.ParID
,
1160 sizeof(hfs_u32
) + sizeof(struct hfs_name
));
1162 hfs_brec_relse(&brec
, NULL
);
1164 retval
= hfs_cat_get(mdb
, &key
);
1167 unlock_entry(entry
);
1174 * Create a new file with the indicated name in the indicated directory.
1175 * The file will have the indicated flags, type and creator.
1176 * If successful an (struct hfs_cat_entry) is returned in '*result'.
1178 int hfs_cat_create(struct hfs_cat_entry
*parent
, struct hfs_cat_key
*key
,
1179 hfs_u8 flags
, hfs_u32 type
, hfs_u32 creator
,
1180 struct hfs_cat_entry
**result
)
1182 struct hfs_cat_rec record
;
1183 hfs_u32 id
= new_cnid(parent
->mdb
);
1184 hfs_u32 mtime
= hfs_time();
1186 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
1187 hfs_warn("hfs_cat_create: %p/%s flags=%d res=%p\n",
1188 parent
, key
->CName
.Name
, flags
, result
);
1190 /* init some fields for the file record */
1191 memset(&record
, 0, sizeof(record
));
1192 record
.cdrType
= HFS_CDR_FIL
;
1193 record
.u
.fil
.Flags
= flags
| HFS_FIL_USED
;
1194 hfs_put_nl(id
, record
.u
.fil
.FlNum
);
1195 hfs_put_nl(mtime
, record
.u
.fil
.CrDat
);
1196 hfs_put_nl(mtime
, record
.u
.fil
.MdDat
);
1197 hfs_put_nl(0, record
.u
.fil
.BkDat
);
1198 hfs_put_nl(type
, record
.u
.fil
.UsrWds
.fdType
);
1199 hfs_put_nl(creator
, record
.u
.fil
.UsrWds
.fdCreator
);
1201 return create_entry(parent
, key
, &record
, 0, id
, result
);
1207 * Create a new directory with the indicated name in the indicated directory.
1208 * If successful an (struct hfs_cat_entry) is returned in '*result'.
1210 int hfs_cat_mkdir(struct hfs_cat_entry
*parent
, struct hfs_cat_key
*key
,
1211 struct hfs_cat_entry
**result
)
1213 struct hfs_cat_rec record
;
1214 hfs_u32 id
= new_cnid(parent
->mdb
);
1215 hfs_u32 mtime
= hfs_time();
1217 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
1218 hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent
, key
->CName
.Name
,
1222 /* init some fields for the directory record */
1223 memset(&record
, 0, sizeof(record
));
1224 record
.cdrType
= HFS_CDR_DIR
;
1225 hfs_put_nl(id
, record
.u
.dir
.DirID
);
1226 hfs_put_nl(mtime
, record
.u
.dir
.CrDat
);
1227 hfs_put_nl(mtime
, record
.u
.dir
.MdDat
);
1228 hfs_put_nl(0, record
.u
.dir
.BkDat
);
1229 hfs_put_hs(0xff, record
.u
.dir
.UsrInfo
.frView
);
1231 return create_entry(parent
, key
, &record
, 1, id
, result
);
1237 * Delete the indicated file or directory.
1238 * The associated thread is also removed unless ('with_thread'==0).
1240 int hfs_cat_delete(struct hfs_cat_entry
*parent
, struct hfs_cat_entry
*entry
,
1243 struct hfs_cat_key key
;
1244 struct hfs_mdb
*mdb
= parent
->mdb
;
1245 int is_dir
, error
= 0;
1247 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
1248 hfs_warn("hfs_cat_delete: %p/%p type=%d state=%lu, thread=%d\n",
1249 parent
, entry
, entry
->type
, entry
->state
, with_thread
);
1251 if (parent
->mdb
!= entry
->mdb
) {
1255 if (entry
->type
== HFS_CDR_FIL
) {
1256 with_thread
= (entry
->u
.file
.flags
&HFS_FIL_THD
) && with_thread
;
1262 /* keep readers from getting confused by changing dir size */
1263 start_write(parent
);
1265 /* don't delete a busy directory */
1266 if (entry
->type
== HFS_CDR_DIR
) {
1270 if (entry
->u
.dir
.files
|| entry
->u
.dir
.dirs
)
1271 goto hfs_delete_end
;
1274 /* try to delete the file or directory */
1277 if ((entry
->state
& HFS_DELETED
)) {
1278 /* somebody beat us to it. */
1279 goto hfs_delete_unlock
;
1282 /* delete the catalog record */
1283 if ((error
= hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(&entry
->key
)))) {
1284 goto hfs_delete_unlock
;
1287 /* Mark the entry deleted and remove it from the cache */
1288 delete_entry(entry
);
1290 /* try to delete the thread entry if it exists */
1292 hfs_cat_build_key(entry
->cnid
, NULL
, &key
);
1293 (void)hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(&key
));
1296 update_dir(mdb
, parent
, is_dir
, -1);
1299 unlock_entry(entry
);
1302 if (entry
->type
== HFS_CDR_DIR
) {
1312 * Rename a file or directory, possibly to a new directory.
1313 * If the destination exists it is removed and a
1314 * (struct hfs_cat_entry) for it is returned in '*result'.
1316 int hfs_cat_move(struct hfs_cat_entry
*old_dir
, struct hfs_cat_entry
*new_dir
,
1317 struct hfs_cat_entry
*entry
, struct hfs_cat_key
*new_key
,
1318 struct hfs_cat_entry
**removed
)
1320 struct hfs_cat_entry
*dest
;
1321 struct hfs_mdb
*mdb
;
1323 int is_dir
, has_thread
;
1330 if (!old_dir
|| !new_dir
) {
1334 if (mdb
!= new_dir
->mdb
) {
1338 /* precompute a few things */
1339 if (entry
->type
== HFS_CDR_DIR
) {
1342 } else if (entry
->type
== HFS_CDR_FIL
) {
1344 has_thread
= entry
->u
.file
.flags
& HFS_FIL_THD
;
1349 while (mdb
->rename_lock
) {
1350 hfs_sleep_on(&mdb
->rename_wait
);
1352 spin_lock(&entry_lock
);
1353 mdb
->rename_lock
= 1; /* XXX: should be atomic_inc */
1354 spin_unlock(&entry_lock
);
1356 /* keep readers from getting confused by changing dir size */
1357 start_write(new_dir
);
1358 if (old_dir
!= new_dir
) {
1359 start_write(old_dir
);
1362 /* Don't move a directory inside itself */
1364 struct hfs_cat_key thd_key
;
1365 struct hfs_brec brec
;
1367 hfs_u32 id
= new_dir
->cnid
;
1368 while (id
!= htonl(HFS_ROOT_CNID
)) {
1369 if (id
== entry
->cnid
) {
1372 hfs_cat_build_key(id
, NULL
, &thd_key
);
1373 error
= hfs_bfind(&brec
, mdb
->cat_tree
,
1380 struct hfs_cat_rec
*rec
= brec
.data
;
1381 id
= hfs_get_nl(rec
->u
.thd
.ParID
);
1382 hfs_brec_relse(&brec
, NULL
);
1388 /* see if the destination exists, getting it if it does */
1389 dest
= hfs_cat_get(mdb
, new_key
);
1391 /* destination doesn't exist, so create it */
1392 struct hfs_cat_rec new_record
;
1394 /* create a locked entry in the cache */
1395 dest
= get_entry(mdb
, new_key
, 0);
1401 /* The (unlocked) entry exists in the cache */
1405 /* limit directory valence to signed 16-bit integer */
1406 if ((new_dir
->u
.dir
.dirs
+ new_dir
->u
.dir
.files
) >=
1412 /* build the new record. make sure to zero out the
1414 memset(&new_record
, 0, sizeof(new_record
));
1415 new_record
.cdrType
= entry
->type
;
1416 __write_entry(entry
, &new_record
);
1418 /* insert the new record */
1419 error
= hfs_binsert(mdb
->cat_tree
, HFS_BKEY(new_key
),
1420 &new_record
, is_dir
? 2 + sizeof(DIR_REC
) :
1421 2 + sizeof(FIL_REC
));
1422 if (error
== -EEXIST
) {
1431 /* update the destination directory */
1432 update_dir(mdb
, new_dir
, is_dir
, 1);
1433 } else if (entry
!= dest
) {
1435 /* The destination exists and is not same as source */
1437 if ((dest
->state
& HFS_DELETED
)) {
1442 if (dest
->type
!= entry
->type
) {
1443 /* can't move a file on top
1444 of a dir nor vice versa. */
1445 error
= is_dir
? -ENOTDIR
: -EISDIR
;
1446 } else if (is_dir
&& (dest
->u
.dir
.dirs
|| dest
->u
.dir
.files
)) {
1447 /* directory to replace is not empty */
1455 /* The destination exists but is same as source */
1460 /* lock the entry */
1462 if ((entry
->state
& HFS_DELETED
)) {
1468 /* remove the old entry */
1469 error
= hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(&entry
->key
));
1472 /* We couldn't remove the entry for the
1473 original file, so nothing has changed. */
1476 update_dir(mdb
, old_dir
, is_dir
, -1);
1479 /* update the thread of the dir/file we're moving */
1481 struct hfs_cat_key thd_key
;
1482 struct hfs_brec brec
;
1484 hfs_cat_build_key(entry
->cnid
, NULL
, &thd_key
);
1485 error
= hfs_bfind(&brec
, mdb
->cat_tree
,
1486 HFS_BKEY(&thd_key
), HFS_BFIND_WRITE
);
1487 if (error
== -ENOENT
) {
1489 /* directory w/o a thread! */
1492 /* We were lied to! */
1493 entry
->u
.file
.flags
&= ~HFS_FIL_THD
;
1494 hfs_cat_mark_dirty(entry
);
1498 struct hfs_cat_rec
*rec
= brec
.data
;
1499 memcpy(&rec
->u
.thd
.ParID
, &new_key
->ParID
,
1500 sizeof(hfs_u32
) + sizeof(struct hfs_name
));
1501 hfs_brec_relse(&brec
, NULL
);
1502 } else if (error
== -ENOENT
) {
1505 /* Nothing was changed */
1506 unlock_entry(entry
);
1509 /* Something went seriously wrong.
1510 The dir/file has been deleted. */
1511 /* XXX try some recovery? */
1512 delete_entry(entry
);
1517 /* TRY to remove the thread for the pre-existing entry */
1518 if (dest
&& dest
->cnid
&&
1519 (is_dir
|| (dest
->u
.file
.flags
& HFS_FIL_THD
))) {
1520 struct hfs_cat_key thd_key
;
1522 hfs_cat_build_key(dest
->cnid
, NULL
, &thd_key
);
1523 (void)hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(&thd_key
));
1526 /* update directories */
1527 new_dir
->modify_date
= hfs_time();
1528 hfs_cat_mark_dirty(new_dir
);
1532 memcpy(&entry
->key
, new_key
, sizeof(*new_key
));
1533 /* KEYDIRTY as case might differ */
1534 entry
->state
|= HFS_KEYDIRTY
;
1536 hfs_cat_mark_dirty(entry
);
1537 unlock_entry(entry
);
1539 /* delete any pre-existing or place-holder entry */
1543 if (removed
&& dest
->cnid
) {
1552 unlock_entry(entry
);
1556 /* TRY to remove the new entry */
1557 (void)hfs_bdelete(mdb
->cat_tree
, HFS_BKEY(new_key
));
1558 update_dir(mdb
, new_dir
, is_dir
, -1);
1566 if (new_dir
!= old_dir
) {
1570 spin_lock(&entry_lock
);
1571 mdb
->rename_lock
= 0; /* XXX: should use atomic_dec */
1572 hfs_wake_up(&mdb
->rename_wait
);
1573 spin_unlock(&entry_lock
);
1579 * Initialize the hash tables
1581 void hfs_cat_init(void)
1584 struct list_head
*head
= hash_table
;
1588 INIT_LIST_HEAD(head
);