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