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  */
brec_to_id(struct hfs_brec * brec)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  */
hashfn(const struct hfs_mdb * mdb,const struct hfs_cat_key * key)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  */
hash(struct hfs_mdb * mdb,const struct hfs_cat_key * key)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 
insert_hash(struct hfs_cat_entry * entry)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 
remove_hash(struct hfs_cat_entry * entry)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  */
wait_on_entry(struct hfs_cat_entry * entry)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  */
lock_entry(struct hfs_cat_entry * entry)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  */
unlock_entry(struct hfs_cat_entry * entry)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. */
hfs_cat_mark_dirty(struct hfs_cat_entry * entry)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. */
delete_entry(struct hfs_cat_entry * entry)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 
init_entry(struct hfs_cat_entry * entry)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  */
hfs_cat_alloc(void)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. */
grow_entries(void)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  */
__read_entry(struct hfs_cat_entry * entry,const struct hfs_cat_rec * cat)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  */
count_dir_entries(struct hfs_cat_entry * entry,struct hfs_brec * brec)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  */
read_entry(struct hfs_cat_entry * entry,struct hfs_brec * brec)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  */
__write_entry(const struct hfs_cat_entry * entry,struct hfs_cat_rec * cat)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  */
write_entry(struct hfs_cat_entry * entry)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. */
find_entry(struct hfs_mdb * mdb,const struct hfs_cat_key * key)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. */
get_new_entry(struct hfs_mdb * mdb,const struct hfs_cat_key * key,const int read)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  */
get_entry(struct hfs_mdb * mdb,const struct hfs_cat_key * key,const int read)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  */
new_cnid(struct hfs_mdb * mdb)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  */
update_dir(struct hfs_mdb * mdb,struct hfs_cat_entry * dir,int is_dir,int count)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  */
start_write(struct hfs_cat_entry * dir)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  */
start_read(struct hfs_cat_entry * dir)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  */
end_write(struct hfs_cat_entry * dir)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  */
end_read(struct hfs_cat_entry * dir)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  */
create_entry(struct hfs_cat_entry * parent,struct hfs_cat_key * key,const struct hfs_cat_rec * record,int is_dir,hfs_u32 cnid,struct hfs_cat_entry ** result)695 static int create_entry(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
696 			const struct hfs_cat_rec *record, int is_dir,
697 			hfs_u32 cnid, struct hfs_cat_entry **result)
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  */
hfs_cat_put(struct hfs_cat_entry * entry)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  */
hfs_cat_get(struct hfs_mdb * mdb,const struct hfs_cat_key * key)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 */
invalidate_list(struct list_head * head,struct hfs_mdb * mdb,struct list_head * dispose)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 */
delete_list(struct list_head * head)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  */
hfs_cat_invalidate(struct hfs_mdb * mdb)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  */
hfs_cat_commit(struct hfs_mdb * mdb)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  */
hfs_cat_free(void)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  */
hfs_cat_compare(const struct hfs_cat_key * key1,const struct hfs_cat_key * key2)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  */
hfs_cat_build_key(hfs_u32 parent,const struct hfs_name * cname,struct hfs_cat_key * key)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  */
hfs_cat_open(struct hfs_cat_entry * dir,struct hfs_brec * brec)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  */
hfs_cat_next(struct hfs_cat_entry * dir,struct hfs_brec * brec,hfs_u16 count,hfs_u32 * cnid,hfs_u8 * type)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  */
hfs_cat_close(struct hfs_cat_entry * dir,struct hfs_brec * brec)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  */
hfs_cat_parent(struct hfs_cat_entry * entry)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  */
hfs_cat_create(struct hfs_cat_entry * parent,struct hfs_cat_key * key,hfs_u8 flags,hfs_u32 type,hfs_u32 creator,struct hfs_cat_entry ** result)1178 int hfs_cat_create(struct hfs_cat_entry *parent, struct hfs_cat_key *key,
1179 		   hfs_u8 flags, hfs_u32 type, hfs_u32 creator,
1180 		   struct hfs_cat_entry **result)
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  */
hfs_cat_mkdir(struct hfs_cat_entry * parent,struct hfs_cat_key * key,struct hfs_cat_entry ** result)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  */
hfs_cat_delete(struct hfs_cat_entry * parent,struct hfs_cat_entry * entry,int with_thread)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  */
hfs_cat_move(struct hfs_cat_entry * old_dir,struct hfs_cat_entry * new_dir,struct hfs_cat_entry * entry,struct hfs_cat_key * new_key,struct hfs_cat_entry ** removed)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  */
hfs_cat_init(void)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 }
1593