import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / fs / hfs / catalog.c
1 /*
2  * linux/fs/hfs/catalog.c
3  *
4  * Copyright (C) 1995-1997  Paul H. Hargrove
5  * This file may be distributed under the terms of the GNU General Public License.
6  *
7  * This file contains the functions related to the catalog B-tree.
8  *
9  * "XXX" in a comment is a note to myself to consider changing something.
10  *
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
14  *
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.
18  *
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
22  */
23
24 #include "hfs.h"
25
26 /*================ Variable-like macros ================*/
27
28 /* Number of hash table slots */
29 #define C_HASHBITS  10
30 #define C_HASHSIZE  (1UL << C_HASHBITS)
31 #define C_HASHMASK  (C_HASHSIZE - 1)
32
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)
37
38 /*================ File-local data types ================*/
39
40 /* The catalog record for a file */
41 typedef struct {
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
59                                            for the data fork */
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;
64
65 /* the catalog record for a directory */
66 typedef struct {
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;
78
79 /* the catalog record for a thread */
80 typedef struct {
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;
85
86 /* A catalog tree record */
87 struct hfs_cat_rec {
88         hfs_byte_t              cdrType;        /* The type of entry */
89         hfs_byte_t              cdrResrv2;      /* padding */
90         union {
91                 FIL_REC fil;
92                 DIR_REC dir;
93                 THD_REC thd;
94         } u;
95 } __attribute__((packed));
96
97 /*================ File-local variables ================*/
98  
99 static LIST_HEAD(entry_in_use);
100 static LIST_HEAD(entry_unused);
101 static struct list_head hash_table[C_HASHSIZE];
102
103 static spinlock_t entry_lock = SPIN_LOCK_UNLOCKED;
104
105 static struct {
106         int nr_entries;
107         int nr_free_entries;
108 } entries_stat;
109
110 /*================ File-local functions ================*/
111
112 /*
113  * brec_to_id
114  *
115  * Get the CNID from a brec
116  */
117 static inline hfs_u32 brec_to_id(struct hfs_brec *brec)
118 {
119         struct hfs_cat_rec *rec = brec->data;
120
121         return hfs_get_nl((rec->cdrType==HFS_CDR_FIL) ?
122                                 rec->u.fil.FlNum : rec->u.dir.DirID);
123 }
124
125 /*
126  * hashfn()
127  *
128  * hash an (struct mdb *) and a (struct hfs_cat_key *) to an integer.
129  */
130 static inline unsigned int hashfn(const struct hfs_mdb *mdb,
131                                   const struct hfs_cat_key *key)
132 {
133         unsigned int hash;
134         
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;
139 }
140
141 /*
142  * hash()
143  *
144  * hash an (struct mdb *) and a (struct hfs_cat_key *)
145  * to a pointer to a slot in the hash table.
146  */
147 static inline struct list_head *hash(struct hfs_mdb *mdb,
148                                      const struct hfs_cat_key *key)
149 {
150         return hash_table + hashfn(mdb, key);
151 }
152
153 static inline void insert_hash(struct hfs_cat_entry *entry)
154 {
155         struct list_head *head = hash(entry->mdb, &entry->key);
156         list_add(&entry->hash, head);
157 }
158
159 static inline void remove_hash(struct hfs_cat_entry *entry)
160 {
161         list_del(&entry->hash);
162         INIT_LIST_HEAD(&entry->hash);
163 }
164
165 /*
166  * wait_on_entry()
167  *
168  * Sleep until a locked entry is unlocked.
169  */
170 static inline void wait_on_entry(struct hfs_cat_entry * entry)
171 {
172         while ((entry->state & HFS_LOCK)) {
173                 hfs_sleep_on(&entry->wait);
174         }
175 }
176
177 /*
178  * lock_entry()
179  *
180  * Obtain an exclusive lock on an entry.
181  */
182 static void lock_entry(struct hfs_cat_entry * entry)
183 {
184         wait_on_entry(entry);
185         spin_lock(&entry_lock);
186         entry->state |= HFS_LOCK;
187         spin_unlock(&entry_lock);
188 }
189
190 /*
191  * lock_entry()
192  *
193  * Relinquish an exclusive lock on an entry.
194  */
195 static void unlock_entry(struct hfs_cat_entry * entry)
196 {
197         spin_lock(&entry_lock);
198         entry->state &= ~HFS_LOCK;
199         spin_unlock(&entry_lock);
200         hfs_wake_up(&entry->wait);
201 }
202
203 /* put entry on mdb dirty list. */
204 void hfs_cat_mark_dirty(struct hfs_cat_entry *entry)
205 {
206         struct hfs_mdb *mdb = entry->mdb;
207
208         spin_lock(&entry_lock);
209         if (!(entry->state & HFS_DIRTY)) {
210                 entry->state |= HFS_DIRTY;
211
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);
216                 }
217         }
218         spin_unlock(&entry_lock);
219 }
220
221 /* delete an entry and remove it from the hash table. */
222 static void delete_entry(struct hfs_cat_entry *entry)
223 {
224         if (!(entry->state & HFS_DELETED)) {
225                 entry->state |= HFS_DELETED;
226                 list_del(&entry->hash);
227                 INIT_LIST_HEAD(&entry->hash);
228
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);
235                 }
236         }
237 }
238
239
240 static inline void init_entry(struct hfs_cat_entry *entry)
241 {
242         memset(entry, 0, sizeof(*entry));
243         hfs_init_waitqueue(&entry->wait);
244         INIT_LIST_HEAD(&entry->hash);
245         INIT_LIST_HEAD(&entry->list);
246 }
247
248 /*
249  * hfs_cat_alloc()
250  *
251  * Try to allocate another entry. 
252  */
253 static inline struct hfs_cat_entry *hfs_cat_alloc(void)
254 {
255         struct hfs_cat_entry *entry;
256
257         if (!HFS_NEW(entry))
258                 return NULL;
259
260         init_entry(entry);
261         return entry;
262 }
263
264 /* this gets called with the spinlock held. */
265 static int grow_entries(void)
266 {
267         struct hfs_cat_entry *entry;
268         int i;
269         
270         for (i = 0; i < CCACHE_INC; i++) {
271                 if (!(entry = hfs_cat_alloc()))
272                         break;
273                 list_add(&entry->list, &entry_unused);
274         }
275
276         entries_stat.nr_entries += i;
277         entries_stat.nr_free_entries += i;
278                 
279         return i;
280 }
281
282 /*
283  * __read_entry()
284  *
285  * Convert a (struct hfs_cat_rec) to a (struct hfs_cat_entry).
286  */
287 static void __read_entry(struct hfs_cat_entry *entry,
288                          const struct hfs_cat_rec *cat)
289 {
290         entry->type = cat->cdrType;
291
292         if (cat->cdrType == HFS_CDR_DIR) {
293                 struct hfs_dir *dir = &entry->u.dir;
294
295                 entry->cnid = hfs_get_nl(cat->u.dir.DirID);
296
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;
309
310                 entry->cnid = hfs_get_nl(cat->u.fil.FlNum);
311
312                 fil->magic = HFS_FILE_MAGIC;
313
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);
320
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);
327
328                 memcpy(&entry->info.file.finfo, &cat->u.fil.UsrWds, 16);
329                 memcpy(&entry->info.file.fxinfo, &cat->u.fil.FndrInfo, 16);
330
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;
338         } else {
339                 hfs_warn("hfs_fs: entry is neither file nor directory!\n");
340         }
341 }
342
343 /*
344  * count_dir_entries()
345  *
346  * Count the number of files and directories in a given directory.
347  */
348 static inline void count_dir_entries(struct hfs_cat_entry *entry,
349                                      struct hfs_brec *brec)
350 {
351         int error = 0;
352         hfs_u32 cnid;
353         hfs_u8 type;
354
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) {
360                                 ++entry->u.dir.dirs;
361                         }
362                 } /* -ENOENT is normal termination */
363         }
364         if (error != -ENOENT) {
365                 entry->cnid = 0;
366         }
367 }
368
369 /*
370  * read_entry()
371  *
372  * Convert a (struct hfs_brec) to a (struct hfs_cat_entry).
373  */
374 static inline void read_entry(struct hfs_cat_entry *entry,
375                               struct hfs_brec *brec)
376 {
377         int need_count;
378         struct hfs_cat_rec *rec = brec->data;
379
380         __read_entry(entry, rec);
381
382         need_count = (rec->cdrType == HFS_CDR_DIR) && rec->u.dir.Val;
383
384         hfs_brec_relse(brec, NULL);
385
386         if (need_count) {
387                 count_dir_entries(entry, brec);
388         }
389 }
390
391 /*
392  * __write_entry()
393  *
394  * Convert a (struct hfs_cat_entry) to a (struct hfs_cat_rec).
395  */
396 static void __write_entry(const struct hfs_cat_entry *entry,
397                           struct hfs_cat_rec *cat)
398 {
399         if (entry->type == HFS_CDR_DIR) {
400                 const struct hfs_dir *dir = &entry->u.dir;
401
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;
412
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,
418                                                         cat->u.fil.PyLen);
419                 hfs_put_hl(fil->rsrc_fork.lsize, cat->u.fil.RLgLen);
420                 hfs_put_hl(fil->rsrc_fork.psize << HFS_SECTOR_SIZE_BITS,
421                                                         cat->u.fil.RPyLen);
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);
430         } else {
431                 hfs_warn("__write_entry: invalid entry\n");
432         }
433 }
434
435 /*
436  * write_entry()
437  *
438  * Write a modified entry back to the catalog B-tree. this gets called
439  * with the entry locked.
440  */
441 static void write_entry(struct hfs_cat_entry * entry)
442 {
443         struct hfs_brec brec;
444         int error;
445
446         if (!(entry->state & HFS_DELETED)) {
447                 error = hfs_bfind(&brec, entry->mdb->cat_tree,
448                                   HFS_BKEY(&entry->key), HFS_BFIND_WRITE);
449                 if (!error) {
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 "
455                                                  "changed!\n");
456                                         error = 1;
457                                 } else {
458                                         memcpy(brec.key, &entry->key,
459                                                entry->key.KeyLen);
460                                 }
461                         } else if (entry->cnid != brec_to_id(&brec)) {
462                                 hfs_warn("hfs_write_entry: CNID "
463                                          "changed unexpectedly!\n");
464                                 error = 1;
465                         }
466                         if (!error) {
467                                 __write_entry(entry, brec.data);
468                         }
469                         hfs_brec_relse(&brec, NULL);
470                 }
471                 if (error) {
472                         hfs_warn("hfs_write_entry: unable to write "
473                                  "entry %08x\n", entry->cnid);
474                 }
475         }
476 }
477
478
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)
482 {
483         struct list_head *tmp, *head = hash(mdb, key);
484         struct hfs_cat_entry * entry;
485
486         tmp = head;
487         for (;;) {
488                 tmp = tmp->next;
489                 entry = NULL;
490                 if (tmp == head)
491                         break;
492                 entry = list_entry(tmp, struct hfs_cat_entry, hash);
493                 if (entry->mdb != mdb)
494                         continue;
495                 if (hfs_cat_compare(&entry->key, key)) {
496                         continue;
497                 }
498                 entry->count++;
499                 break;
500         }
501
502         return entry;
503 }
504
505
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,
509                                            const int read)
510 {
511         struct hfs_cat_entry *entry;
512         struct list_head *head = hash(mdb, key);
513         struct list_head *tmp;
514
515 add_new_entry:
516         tmp = entry_unused.next;
517         if ((tmp != &entry_unused) ) {
518                 list_del(tmp);
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);
523                 entry->mdb = mdb;
524                 entry->count = 1;
525                 memcpy(&entry->key, key, sizeof(*key));
526                 entry->state = HFS_LOCK;
527                 spin_unlock(&entry_lock);
528
529                 if (read) {
530                    struct hfs_brec brec;
531
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. */
536                         goto read_fail;
537                    }
538
539                    read_entry(entry, &brec);
540                    
541                    /* error */
542                    if (!entry->cnid) {
543                         goto read_fail;
544                    }
545
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);
551                 }
552
553                 return entry;
554         }
555
556
557         /* try to allocate more entries. grow_entries() doesn't release
558          * the spinlock. */
559         if (grow_entries())
560                 goto add_new_entry;
561
562         spin_unlock(&entry_lock);
563         return NULL;
564
565 read_fail: 
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);
570         init_entry(entry);
571         list_add(&entry->list, &entry_unused);
572         entries_stat.nr_free_entries++;
573         spin_unlock(&entry_lock);
574         return NULL;
575 }
576
577 /*
578  * get_entry()
579  *
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.
583  */
584 static struct hfs_cat_entry *get_entry(struct hfs_mdb *mdb,
585                                        const struct hfs_cat_key *key,
586                                        const int read)
587 {
588         struct hfs_cat_entry * entry;
589
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);
593 #endif
594
595         spin_lock(&entry_lock);
596         entry = find_entry(mdb, key);
597         if (!entry) {
598                 return get_new_entry(mdb, key, read);
599         }
600         spin_unlock(&entry_lock);
601         wait_on_entry(entry);
602         return entry;
603 }
604
605 /* 
606  * new_cnid()
607  *
608  * Allocate a CNID to use for a new file or directory.
609  */
610 static inline hfs_u32 new_cnid(struct hfs_mdb *mdb)
611 {
612         /* If the create succeeds then the mdb will get dirtied */
613         return htonl(mdb->next_id++);
614 }
615
616 /*
617  * update_dir()
618  *
619  * Update counts, times and dirt on a changed directory
620  */
621 static void update_dir(struct hfs_mdb *mdb, struct hfs_cat_entry *dir,
622                        int is_dir, int count)
623 {
624         /* update counts */
625         if (is_dir) {
626                 mdb->dir_count += count;
627                 dir->u.dir.dirs += count;
628                 if (dir->cnid == htonl(HFS_ROOT_CNID)) {
629                         mdb->root_dirs += count;
630                 }
631         } else {
632                 mdb->file_count += count;
633                 dir->u.dir.files += count;
634                 if (dir->cnid == htonl(HFS_ROOT_CNID)) {
635                         mdb->root_files += count;
636                 }
637         }
638         
639         /* update times and dirt */
640         dir->modify_date = hfs_time();
641         hfs_cat_mark_dirty(dir);
642 }
643
644 /*
645  * Add a writer to dir, excluding readers.
646  *
647  * XXX: this is wrong. it allows a move to occur when a directory
648  *      is being written to. 
649  */
650 static inline void start_write(struct hfs_cat_entry *dir)
651 {
652         if (dir->u.dir.readers || waitqueue_active(&dir->u.dir.read_wait)) {
653                 hfs_sleep_on(&dir->u.dir.write_wait);
654         }
655         ++dir->u.dir.writers;
656 }
657
658 /*
659  * Add a reader to dir, excluding writers.
660  */
661 static inline void start_read(struct hfs_cat_entry *dir)
662 {
663         if (dir->u.dir.writers || waitqueue_active(&dir->u.dir.write_wait)) {
664                 hfs_sleep_on(&dir->u.dir.read_wait);
665         }
666         ++dir->u.dir.readers;
667 }
668
669 /*
670  * Remove a writer from dir, possibly admitting readers.
671  */
672 static inline void end_write(struct hfs_cat_entry *dir)
673 {
674         if (!(--dir->u.dir.writers)) {
675                 hfs_wake_up(&dir->u.dir.read_wait);
676         }
677 }
678
679 /*
680  * Remove a reader from dir, possibly admitting writers.
681  */
682 static inline void end_read(struct hfs_cat_entry *dir)
683 {
684         if (!(--dir->u.dir.readers)) {
685                 hfs_wake_up(&dir->u.dir.write_wait);
686         }
687 }
688
689 /*
690  * create_entry()
691  *
692  * Add a new file or directory to the catalog B-tree and
693  * return a (struct hfs_cat_entry) for it in '*result'.
694  */
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)
698 {
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;
704
705         if (result) {
706                 *result = NULL;
707         }
708
709         /* keep readers from getting confused by changing dir size */
710         start_write(parent);
711
712         /* create a locked entry in the cache */
713         entry = get_entry(mdb, key, 0);
714         if (!entry) {
715                 /* The entry exists but can't be read */
716                 error = -EIO;
717                 goto done;
718         }
719
720         if (entry->cnid) {
721                 /* The (unlocked) entry exists in the cache */
722                 error = -EEXIST;
723                 goto bail2;
724         }
725
726         /* limit directory valence to signed 16-bit integer */
727         if ((parent->u.dir.dirs + parent->u.dir.files) >= HFS_MAX_VALENCE) {
728                 error = -ENOSPC;
729                 goto bail1;
730         }
731
732         has_thread = is_dir || (record->u.fil.Flags & HFS_FIL_THD);
733
734         if (has_thread) {
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));
740
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));
745                 if (error) {
746                         goto bail1;
747                 }
748         }
749
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));
754         if (error) {
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));
758                 }
759                 goto bail1;
760         }
761
762         /* update the parent directory */
763         update_dir(mdb, parent, is_dir, 1);
764
765         /* complete the cache entry and return success */
766         __read_entry(entry, record);
767         unlock_entry(entry);
768
769         if (result) {
770                 *result = entry;
771         } else {
772                 hfs_cat_put(entry);
773         }
774         goto done;
775
776 bail1:
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;
780         remove_hash(entry);
781         unlock_entry(entry);
782 bail2:
783         hfs_cat_put(entry);
784 done:
785         end_write(parent);
786         return error;
787 }
788
789 /*================ Global functions ================*/
790
791 /* 
792  * hfs_cat_put()
793  *
794  * Release an entry we aren't using anymore.
795  *
796  * nothing in hfs_cat_put goes to sleep now except on the initial entry.  
797  */
798 void hfs_cat_put(struct hfs_cat_entry * entry)
799 {
800         if (entry) {
801                 wait_on_entry(entry);
802
803                 /* just in case. this should never happen. */
804                 if (!entry->count) { 
805                   hfs_warn("hfs_cat_put: trying to free free entry: %p\n",
806                            entry);
807                   return;
808                 }
809
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);
813 #endif
814                 spin_lock(&entry_lock);
815                 if (!--entry->count) {
816                         if ((entry->state & HFS_DELETED))
817                                 goto entry_deleted;
818
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);
823                                 }
824                                 if (entry->u.file.rsrc_fork.first.next) {
825                                   hfs_extent_free(&entry->u.file.rsrc_fork);
826                                 }
827                         }
828
829                         /* if we put a dirty entry, write it out. */
830                         if ((entry->state & HFS_DIRTY)) {
831                                 entry->state ^= HFS_DIRTY | HFS_LOCK;
832                                 write_entry(entry);
833                                 entry->state &= ~HFS_LOCK;
834                         }
835
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) {
841                                 HFS_DELETE(entry);
842                                 entries_stat.nr_entries--;
843                         } else {
844                                 init_entry(entry);
845                                 list_add(&entry->list, &entry_unused);
846                                 entries_stat.nr_free_entries++;
847                         }
848                 }
849                 spin_unlock(&entry_lock);
850         }
851 }
852
853 /* 
854  * hfs_cat_get()
855  *
856  * Wrapper for get_entry() which always calls with ('read'==1).
857  * Used for access to get_entry() from outside this file.
858  */
859 struct hfs_cat_entry *hfs_cat_get(struct hfs_mdb *mdb,
860                                   const struct hfs_cat_key *key)
861 {
862         return get_entry(mdb, key, 1);
863 }
864
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)
868 {
869         struct list_head *next;
870
871         next = head->next;
872         for (;;) {
873                 struct list_head *tmp = next;
874                 struct hfs_cat_entry * entry;
875                 
876                 next = next->next;
877                 if (tmp == head)
878                         break;
879                 entry = list_entry(tmp, struct hfs_cat_entry, list);
880                 if (entry->mdb != mdb) {
881                         continue;
882                 }
883
884                 if (!entry->count) {
885                         list_del(&entry->hash);
886                         INIT_LIST_HEAD(&entry->hash);
887                         list_del(&entry->list);
888                         list_add(&entry->list, dispose);
889                         continue;
890                 }
891                 
892                 hfs_warn("hfs_fs: entry %p(%u) busy on removed device %s.\n",
893                          entry, entry->count, 
894                          hfs_mdb_name(entry->mdb->sys_mdb));
895         }
896 }
897
898 /* delete entries from a list */
899 static void delete_list(struct list_head *head) 
900 {
901         struct list_head *next = head->next;
902         struct hfs_cat_entry *entry;
903         
904         for (;;) {
905                 struct list_head * tmp = next;
906
907                 next = next->next;
908                 if (tmp == head) {
909                         break;
910                 }
911                 entry = list_entry(tmp, struct hfs_cat_entry, list);
912                 HFS_DELETE(entry);
913         }
914 }
915
916 /* 
917  * hfs_cat_invalidate()
918  *
919  * Called by hfs_mdb_put() to remove all the entries
920  * in the cache that are associated with a given MDB.
921  */
922 void hfs_cat_invalidate(struct hfs_mdb *mdb)
923 {
924         LIST_HEAD(throw_away);
925
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);
930
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);
936 #endif
937 }
938
939 /*
940  * hfs_cat_commit()
941  *
942  * Called by hfs_mdb_commit() to write dirty entries to the disk buffers.
943  */
944 void hfs_cat_commit(struct hfs_mdb *mdb)
945 {
946         struct list_head *tmp, *head = &mdb->entry_dirty;
947         struct hfs_cat_entry *entry;
948
949         spin_lock(&entry_lock);
950         while ((tmp = head->prev) != head) {
951                 entry = list_entry(tmp, struct hfs_cat_entry, list);
952                   
953                 if ((entry->state & HFS_LOCK)) {
954                         spin_unlock(&entry_lock);
955                         wait_on_entry(entry);
956                         spin_lock(&entry_lock);
957                 } else {
958                        struct list_head *insert = &entry_in_use;
959
960                        if (!entry->count)
961                                 insert = entry_in_use.prev;
962
963                        /* add to in_use list */
964                        list_del(&entry->list);
965                        list_add(&entry->list, insert);
966
967                        /* reset DIRTY, set LOCK */
968                        entry->state ^= HFS_DIRTY | HFS_LOCK;
969                        spin_unlock(&entry_lock);
970                        write_entry(entry);
971                        spin_lock(&entry_lock);
972                        entry->state &= ~HFS_LOCK;
973                        hfs_wake_up(&entry->wait);
974                 }
975         }
976         spin_unlock(&entry_lock);
977 }
978
979 /*
980  * hfs_cat_free()
981  *
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.
987  */
988 void hfs_cat_free(void)
989 {
990         delete_list(&entry_unused);
991 }
992
993 /*
994  * hfs_cat_compare()
995  *
996  * Description:
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):
1006  *   NONE
1007  * Returns:
1008  *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
1009  * Preconditions:
1010  *   key1 and key2 point to "valid" (struct hfs_cat_key)s.
1011  * Postconditions:
1012  *   This function has no side-effects
1013  */
1014 int hfs_cat_compare(const struct hfs_cat_key *key1,
1015                     const struct hfs_cat_key *key2)
1016 {
1017         unsigned int parents;
1018         int retval;
1019
1020         parents = hfs_get_hl(key1->ParID) - hfs_get_hl(key2->ParID);
1021         if (parents != 0) {
1022                 retval = (int)parents;
1023         } else {
1024                 retval = hfs_strcmp(key1->CName.Name, key1->CName.Len,
1025                                     key2->CName.Name, key2->CName.Len);
1026         }
1027         return retval;
1028 }
1029
1030 /*
1031  * hfs_cat_build_key()
1032  *
1033  * Given the ID of the parent and the name build a search key.
1034  */
1035 void hfs_cat_build_key(hfs_u32 parent, const struct hfs_name *cname,
1036                        struct hfs_cat_key *key)
1037 {
1038         hfs_put_nl(parent, key->ParID);
1039
1040         if (cname) {
1041                 key->KeyLen = 6 + cname->Len;
1042                 memcpy(&key->CName, cname, sizeof(*cname));
1043         } else {
1044                 key->KeyLen = 6;
1045                 memset(&key->CName, 0, sizeof(*cname));
1046         }
1047 }
1048
1049 /*
1050  * hfs_cat_open()
1051  *
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.
1055  */
1056 int hfs_cat_open(struct hfs_cat_entry *dir, struct hfs_brec *brec)
1057 {
1058         struct hfs_cat_key key;
1059         int error;
1060
1061         if (dir->type != HFS_CDR_DIR) {
1062                 return -EINVAL;
1063         }
1064         
1065         /* Block writers */
1066         start_read(dir);
1067
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);
1072
1073         if (error) {
1074                 end_read(dir);
1075         }
1076
1077         return error;
1078 }
1079
1080 /*
1081  * hfs_cat_next()
1082  *
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.
1089  */
1090 int hfs_cat_next(struct hfs_cat_entry *dir, struct hfs_brec *brec,
1091                  hfs_u16 count, hfs_u32 *cnid, hfs_u8 *type)
1092 {
1093         int error;
1094
1095         if (!dir || !brec) {
1096                 return -EINVAL;
1097         }
1098
1099         /* Get the count'th next catalog tree entry */
1100         error = hfs_bsucc(brec, count);
1101         if (!error) {
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);
1105                         error = -ENOENT;
1106                 }
1107         }
1108         if (!error) {
1109                 *type = ((struct hfs_cat_rec *)brec->data)->cdrType;
1110                 *cnid = brec_to_id(brec);
1111         } else {
1112                 end_read(dir);
1113         }
1114         return error;
1115 }
1116
1117 /*
1118  * hfs_cat_close()
1119  *
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.
1125  */
1126 void hfs_cat_close(struct hfs_cat_entry *dir, struct hfs_brec *brec)
1127 {
1128         if (dir && brec) {
1129                 hfs_brec_relse(brec, NULL);
1130                 end_read(dir);
1131         }
1132 }
1133
1134 /*
1135  * hfs_cat_parent()
1136  *
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.
1141  */
1142 struct hfs_cat_entry *hfs_cat_parent(struct hfs_cat_entry *entry)
1143 {
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;
1148         int error;
1149
1150         lock_entry(entry);
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);
1155                 if (!error) {
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));
1161
1162                         hfs_brec_relse(&brec, NULL);
1163
1164                         retval = hfs_cat_get(mdb, &key);
1165                 }
1166         }
1167         unlock_entry(entry);
1168         return retval;
1169 }
1170         
1171 /*
1172  * hfs_cat_create()
1173  *
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'.
1177  */
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)
1181 {
1182         struct hfs_cat_rec record;
1183         hfs_u32 id = new_cnid(parent->mdb);
1184         hfs_u32 mtime = hfs_time();
1185
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);
1189 #endif
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);
1200
1201         return create_entry(parent, key, &record, 0, id, result);
1202 }
1203
1204 /*
1205  * hfs_cat_mkdir()
1206  *
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'.
1209  */
1210 int hfs_cat_mkdir(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
1211                   struct hfs_cat_entry **result)
1212 {
1213         struct hfs_cat_rec record;
1214         hfs_u32 id = new_cnid(parent->mdb);
1215         hfs_u32 mtime = hfs_time();
1216
1217 #if defined(DEBUG_CATALOG) || defined(DEBUG_ALL)
1218         hfs_warn("hfs_cat_mkdir: %p/%s res=%p\n", parent, key->CName.Name,
1219                  result);
1220 #endif
1221
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);
1230
1231         return create_entry(parent, key, &record, 1, id, result);
1232 }
1233
1234 /*
1235  * hfs_cat_delete()
1236  *
1237  * Delete the indicated file or directory.
1238  * The associated thread is also removed unless ('with_thread'==0).
1239  */
1240 int hfs_cat_delete(struct hfs_cat_entry *parent, struct hfs_cat_entry *entry,
1241                    int with_thread)
1242 {
1243         struct hfs_cat_key key;
1244         struct hfs_mdb *mdb = parent->mdb;
1245         int is_dir, error = 0;
1246
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);
1250 #endif
1251         if (parent->mdb != entry->mdb) {
1252                 return -EINVAL;
1253         }
1254
1255         if (entry->type == HFS_CDR_FIL) {
1256                 with_thread = (entry->u.file.flags&HFS_FIL_THD) && with_thread;
1257                 is_dir = 0;
1258         } else {
1259                 is_dir = 1;
1260         }
1261
1262         /* keep readers from getting confused by changing dir size */
1263         start_write(parent);
1264
1265         /* don't delete a busy directory */
1266         if (entry->type == HFS_CDR_DIR) {
1267                 start_read(entry);
1268
1269                 error = -ENOTEMPTY;
1270                 if (entry->u.dir.files || entry->u.dir.dirs) 
1271                         goto hfs_delete_end;
1272         }
1273
1274         /* try to delete the file or directory */
1275         lock_entry(entry);
1276         error = -ENOENT;
1277         if ((entry->state & HFS_DELETED)) {
1278                 /* somebody beat us to it. */
1279                 goto hfs_delete_unlock;
1280         }
1281                 
1282         /* delete the catalog record */
1283         if ((error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key)))) {
1284                 goto hfs_delete_unlock;
1285         }
1286
1287         /* Mark the entry deleted and remove it from the cache */
1288         delete_entry(entry);
1289
1290         /* try to delete the thread entry if it exists */
1291         if (with_thread) {
1292                 hfs_cat_build_key(entry->cnid, NULL, &key);
1293                 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&key));
1294         }
1295         
1296         update_dir(mdb, parent, is_dir, -1);
1297
1298 hfs_delete_unlock:
1299         unlock_entry(entry);
1300
1301 hfs_delete_end:
1302         if (entry->type == HFS_CDR_DIR) {
1303                 end_read(entry);
1304         }
1305         end_write(parent);
1306         return error;
1307 }
1308
1309 /*
1310  * hfs_cat_move()
1311  *
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'.
1315  */
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)
1319 {
1320         struct hfs_cat_entry *dest;
1321         struct hfs_mdb *mdb;
1322         int error = 0;
1323         int is_dir, has_thread;
1324
1325         if (removed) {
1326                 *removed = NULL;
1327         }
1328
1329         /* sanity checks */
1330         if (!old_dir || !new_dir) {
1331                 return -EINVAL;
1332         }
1333         mdb = old_dir->mdb;
1334         if (mdb != new_dir->mdb) {
1335                 return -EXDEV;
1336         }
1337
1338         /* precompute a few things */
1339         if (entry->type == HFS_CDR_DIR) {
1340                 is_dir = 1;
1341                 has_thread = 1;
1342         } else if (entry->type == HFS_CDR_FIL) {
1343                 is_dir = 0;
1344                 has_thread = entry->u.file.flags & HFS_FIL_THD;
1345         } else {
1346                 return -EINVAL;
1347         }
1348
1349         while (mdb->rename_lock) {
1350                 hfs_sleep_on(&mdb->rename_wait);
1351         }
1352         spin_lock(&entry_lock);
1353         mdb->rename_lock = 1; /* XXX: should be atomic_inc */
1354         spin_unlock(&entry_lock);
1355
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);
1360         }
1361
1362         /* Don't move a directory inside itself */
1363         if (is_dir) {
1364                 struct hfs_cat_key thd_key;
1365                 struct hfs_brec brec;
1366
1367                 hfs_u32 id = new_dir->cnid;
1368                 while (id != htonl(HFS_ROOT_CNID)) {
1369                         if (id == entry->cnid) {
1370                                 error = -EINVAL;
1371                         } else {
1372                                 hfs_cat_build_key(id, NULL, &thd_key);
1373                                 error = hfs_bfind(&brec, mdb->cat_tree,
1374                                                   HFS_BKEY(&thd_key),
1375                                                   HFS_BFIND_READ_EQ);
1376                         }
1377                         if (error) {
1378                                 goto done;
1379                         } else {
1380                                 struct hfs_cat_rec *rec = brec.data;
1381                                 id = hfs_get_nl(rec->u.thd.ParID);
1382                                 hfs_brec_relse(&brec, NULL);
1383                         }
1384                 }
1385         }
1386
1387 restart:
1388         /* see if the destination exists, getting it if it does */
1389         dest = hfs_cat_get(mdb, new_key);
1390         if (!dest) {
1391                 /* destination doesn't exist, so create it */
1392                 struct hfs_cat_rec new_record;
1393
1394                 /* create a locked entry in the cache */
1395                 dest = get_entry(mdb, new_key, 0);
1396                 if (!dest) {
1397                         error = -EIO;
1398                         goto done;
1399                 }
1400                 if (dest->cnid) {
1401                         /* The (unlocked) entry exists in the cache */
1402                         goto have_distinct;
1403                 }
1404
1405                 /* limit directory valence to signed 16-bit integer */
1406                 if ((new_dir->u.dir.dirs + new_dir->u.dir.files) >=
1407                                                         HFS_MAX_VALENCE) {
1408                         error = -ENOSPC;
1409                         goto bail3;
1410                 }
1411
1412                 /* build the new record. make sure to zero out the
1413                    record. */
1414                 memset(&new_record, 0, sizeof(new_record));
1415                 new_record.cdrType = entry->type;
1416                 __write_entry(entry, &new_record);
1417
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) {
1423                         delete_entry(dest);
1424                         unlock_entry(dest);
1425                         hfs_cat_put(dest);
1426                         goto restart;
1427                 } else if (error) {
1428                         goto bail3;
1429                 }
1430
1431                 /* update the destination directory */
1432                 update_dir(mdb, new_dir, is_dir, 1);
1433         } else if (entry != dest) {
1434 have_distinct:
1435                 /* The destination exists and is not same as source */
1436                 lock_entry(dest);
1437                 if ((dest->state & HFS_DELETED)) {
1438                         unlock_entry(dest);
1439                         hfs_cat_put(dest);
1440                         goto restart;
1441                 }
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 */
1448                         error = -ENOTEMPTY;
1449                 }
1450
1451                 if (error) {
1452                         goto bail2;
1453                 }
1454         } else {
1455                 /* The destination exists but is same as source */
1456                 --entry->count;
1457                 dest = NULL;
1458         }
1459
1460         /* lock the entry */
1461         lock_entry(entry);
1462         if ((entry->state & HFS_DELETED)) {
1463                 error = -ENOENT;
1464                 goto bail1;
1465         }
1466
1467         if (dest) {
1468                 /* remove the old entry */
1469                 error = hfs_bdelete(mdb->cat_tree, HFS_BKEY(&entry->key));
1470
1471                 if (error) {
1472                         /* We couldn't remove the entry for the
1473                            original file, so nothing has changed. */
1474                         goto bail1;
1475                 }
1476                 update_dir(mdb, old_dir, is_dir, -1);
1477         }
1478
1479         /* update the thread of the dir/file we're moving */
1480         if (has_thread) {
1481                 struct hfs_cat_key thd_key;
1482                 struct hfs_brec brec;
1483
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) {
1488                         if (is_dir) {
1489                                 /* directory w/o a thread! */
1490                                 error = -EIO;
1491                         } else {
1492                                 /* We were lied to! */
1493                                 entry->u.file.flags &= ~HFS_FIL_THD;
1494                                 hfs_cat_mark_dirty(entry);
1495                         }
1496                 }
1497                 if (!error) {
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) {
1503                         error = 0;
1504                 } else if (!dest) {
1505                         /* Nothing was changed */
1506                         unlock_entry(entry);
1507                         goto done;
1508                 } else {
1509                         /* Something went seriously wrong.
1510                            The dir/file has been deleted. */
1511                         /* XXX try some recovery? */
1512                         delete_entry(entry);
1513                         goto bail1;
1514                 }
1515         }
1516
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;
1521
1522                 hfs_cat_build_key(dest->cnid, NULL, &thd_key);
1523                 (void)hfs_bdelete(mdb->cat_tree, HFS_BKEY(&thd_key));
1524         }
1525
1526         /* update directories */
1527         new_dir->modify_date = hfs_time();
1528         hfs_cat_mark_dirty(new_dir);
1529
1530         /* update key */
1531         remove_hash(entry);
1532         memcpy(&entry->key, new_key, sizeof(*new_key));
1533         /* KEYDIRTY as case might differ */
1534         entry->state |= HFS_KEYDIRTY;
1535         insert_hash(entry);
1536         hfs_cat_mark_dirty(entry);
1537         unlock_entry(entry);
1538
1539         /* delete any pre-existing or place-holder entry */
1540         if (dest) {
1541                 delete_entry(dest);
1542                 unlock_entry(dest);
1543                 if (removed && dest->cnid) {
1544                         *removed = dest;
1545                 } else {
1546                         hfs_cat_put(dest);
1547                 }
1548         }
1549         goto done;
1550
1551 bail1:
1552         unlock_entry(entry);
1553 bail2:
1554         if (dest) {
1555                 if (!dest->cnid) {
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);
1559 bail3:
1560                         delete_entry(dest);
1561                 }
1562                 unlock_entry(dest);
1563                 hfs_cat_put(dest);
1564         }
1565 done:
1566         if (new_dir != old_dir) {
1567                 end_write(old_dir);
1568         }
1569         end_write(new_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);
1574
1575         return error;
1576 }
1577
1578 /*
1579  * Initialize the hash tables
1580  */
1581 void hfs_cat_init(void)
1582 {
1583         int i;
1584         struct list_head *head = hash_table;
1585
1586         i = C_HASHSIZE;
1587         do {
1588                 INIT_LIST_HEAD(head);
1589                 head++;
1590                 i--;
1591         } while (i);
1592 }