OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [fs/] [hfs/] [catalog.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.