]> git.saurik.com Git - apple/xnu.git/blame - bsd/hfs/hfs_catalog.c
xnu-2782.40.9.tar.gz
[apple/xnu.git] / bsd / hfs / hfs_catalog.c
CommitLineData
9bccf70c 1/*
fe8ab488 2 * Copyright (c) 2000-2014 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
9bccf70c 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
9bccf70c
A
27 */
28
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/malloc.h>
32#include <sys/stat.h>
33#include <sys/mount.h>
34#include <sys/vnode.h>
9bccf70c
A
35#include <sys/dirent.h>
36#include <vfs/vfs_support.h>
37#include <libkern/libkern.h>
38
39#include <sys/utfconv.h>
40
41#include "hfs.h"
42#include "hfs_catalog.h"
43#include "hfs_format.h"
44#include "hfs_endian.h"
45
46#include "hfscommon/headers/BTreesInternal.h"
2d21ac55 47#include "hfscommon/headers/BTreesPrivate.h"
9bccf70c
A
48#include "hfscommon/headers/HFSUnicodeWrappers.h"
49
9bccf70c
A
50
51/*
52 * Initialization of an FSBufferDescriptor structure.
53 */
54#define BDINIT(bd, addr) { \
55 (bd).bufferAddress = (addr); \
56 (bd).itemSize = sizeof(*(addr)); \
57 (bd).itemCount = 1; \
58}
59
60
61struct btobj {
62 BTreeIterator iterator;
63 HFSPlusCatalogKey key;
64 CatalogRecord data;
65};
66
67struct update_state {
68 struct cat_desc * s_desc;
69 struct cat_attr * s_attr;
fe8ab488
A
70 const struct cat_fork * s_datafork;
71 const struct cat_fork * s_rsrcfork;
9bccf70c
A
72 struct hfsmount * s_hfsmp;
73};
74
91447636
A
75struct position_state {
76 int error;
77 u_int32_t count;
78 u_int32_t index;
79 u_int32_t parentID;
80 struct hfsmount *hfsmp;
81};
82
83/* Map file mode type to directory entry types */
84u_char modetodirtype[16] = {
85 DT_REG, DT_FIFO, DT_CHR, DT_UNKNOWN,
86 DT_DIR, DT_UNKNOWN, DT_BLK, DT_UNKNOWN,
87 DT_REG, DT_UNKNOWN, DT_LNK, DT_UNKNOWN,
88 DT_SOCK, DT_UNKNOWN, DT_WHT, DT_UNKNOWN
89};
90#define MODE_TO_DT(mode) (modetodirtype[((mode) & S_IFMT) >> 12])
91
9bccf70c 92
6d2010ae
A
93#define HFS_LOOKUP_SYSFILE 0x1 /* If set, allow lookup of system files */
94#define HFS_LOOKUP_HARDLINK 0x2 /* If set, allow lookup of hard link records and not resolve the hard links */
39236c6e 95#define HFS_LOOKUP_CASESENSITIVE 0x4 /* If set, verify results of a file/directory record match input case */
6d2010ae 96static int cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
91447636 97 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid);
9bccf70c 98
6d2010ae 99int cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
9bccf70c
A
100 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp);
101
9bccf70c
A
102/* Internal catalog support routines */
103
91447636
A
104static int cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
105 struct position_state *state);
9bccf70c 106
b0d623f7 107static int resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino);
55e303ae 108
9bccf70c
A
109static int getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key);
110
111static int buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
112 HFSPlusCatalogKey *key, int retry);
113
114static void buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key);
115
2d21ac55 116static void buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding, CatalogRecord *crp, u_int32_t *recordSize);
9bccf70c 117
91447636 118static int catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state);
9bccf70c 119
b0d623f7 120static int builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
9bccf70c
A
121 int isdir, struct cat_desc *descp);
122
123static void getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp);
124
39236c6e 125#if CONFIG_HFS_STD
b0d623f7 126static void promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey, HFSPlusCatalogKey *keyp, u_int32_t *encoding);
9bccf70c
A
127static void promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *file, int resource, struct cat_fork * forkp);
128static void promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp);
39236c6e 129#endif
9bccf70c
A
130
131static cnid_t getcnid(const CatalogRecord *crp);
b0d623f7 132static u_int32_t getencoding(const CatalogRecord *crp);
9bccf70c
A
133static cnid_t getparentcnid(const CatalogRecord *recp);
134
135static int isadir(const CatalogRecord *crp);
136
137static int buildthread(void *keyp, void *recp, int std_hfs, int directory);
138
2d21ac55
A
139static int cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp);
140
6d2010ae 141static int cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
fe8ab488 142 const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp);
9bccf70c 143
39236c6e
A
144
145
146/* HFS ID Hashtable Functions */
147#define IDHASH(hfsmp, inum) (&hfsmp->hfs_idhashtbl[(inum) & hfsmp->hfs_idhash])
148
149/* Initialize the HFS ID hash table */
150void
151hfs_idhash_init (struct hfsmount *hfsmp) {
152 /* secured by catalog lock so no lock init needed */
153 hfsmp->hfs_idhashtbl = hashinit(HFS_IDHASH_DEFAULT, M_HFSMNT, &hfsmp->hfs_idhash);
154}
155
156/* Free the HFS ID hash table */
157void
158hfs_idhash_destroy (struct hfsmount *hfsmp) {
159 /* during failed mounts & unmounts */
160 FREE(hfsmp->hfs_idhashtbl, M_HFSMNT);
161}
162
163/*
164from hfs_catalog.h:
165typedef struct cat_preflightid {
166 cnid_t fileid;
167 LIST_ENTRY(cat_preflightid) id_hash;
168} cat_preflightid_t;
169
170from hfs.h:
171 u_long hfs_idhash; / size of cnid/fileid hash table -1 /
172 LIST_HEAD(idhashhead, cat_preflightid) *hfs_idhashtbl; / base of ID hash /
173*/
174
175/*
176 * Check the run-time ID hashtable.
177 *
178 * The catalog lock must be held (like other functions in this file).
179 *
180 * Returns:
181 * 1 if the ID is in the hash table.
182 * 0 if the ID is not in the hash table
183 */
184int cat_check_idhash (struct hfsmount *hfsmp, cnid_t test_fileid) {
185
186 cat_preflightid_t *preflight;
187 int found = 0;
188
189 for (preflight = IDHASH(hfsmp, test_fileid)->lh_first; preflight ; preflight = preflight->id_hash.le_next) {
190 if (preflight->fileid == test_fileid) {
191 found = 1;
192 break;
193 }
194 }
195
196 return found;
197}
198
199/* Insert the supplied preflight into the ID hash table */
200int cat_insert_idhash (struct hfsmount *hfsmp, cat_preflightid_t *preflight) {
201
202 if (preflight) {
203 LIST_INSERT_HEAD(IDHASH(hfsmp, (preflight->fileid)), preflight, id_hash);
204 return 0;
205 }
206 return -1;
207}
208
209
210/* Remove the data structure with the specified ID from the hashtable */
211int cat_remove_idhash (cat_preflightid_t *preflight) {
212
213 if ((preflight) && ((preflight->id_hash.le_next || preflight->id_hash.le_prev))) {
214 LIST_REMOVE (preflight, id_hash);
215 preflight->id_hash.le_next = NULL;
216 preflight->id_hash.le_prev = NULL;
217
218 return 0;
219 }
220
221 return -1;
222}
223
224/*
225 * Acquire a new CNID for use.
226 *
227 * This is slightly more complicated than just pulling the value from the
228 * hfsmount data structure. We need to validate that the ID is not in-use
229 * even if we've not wrapped around and that there are not any lingering
230 * or orphaned fileIDs for this ID.
231 *
232 * Also validate that there are not any pending insertions into the
233 * catalog by checking the ID hash table.
234 */
235int
fe8ab488
A
236cat_acquire_cnid (struct hfsmount *hfsmp, cnid_t *new_cnid)
237{
39236c6e
A
238 uint32_t nextCNID;
239 struct BTreeIterator *iterator;
240 FSBufferDescriptor btdata;
241 uint16_t datasize;
242 CatalogRecord *recp;
243 int result = 0;
244 int std_hfs;
245 int wrapped = 0;
246
247 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
248 /*
249 * Get the next CNID. We can change it since we hold the catalog lock.
250 */
251nextid:
252 nextCNID = hfsmp->vcbNxtCNID;
253 if (nextCNID == 0xFFFFFFFF) {
254 if (std_hfs) {
255 return (ENOSPC);
256 } else {
257 wrapped++;
258 if (wrapped > 1) {
259 /* don't allow more than one wrap-around */
260 return ENOSPC;
261 }
262 hfs_lock_mount (hfsmp);
263 hfsmp->vcbNxtCNID = kHFSFirstUserCatalogNodeID;
264 hfsmp->vcbAtrb |= kHFSCatalogNodeIDsReusedMask;
265 hfs_unlock_mount (hfsmp);
266 }
267 } else {
268 hfsmp->vcbNxtCNID++;
269 }
270 MarkVCBDirty(hfsmp);
271
272 /* First check that there are not any entries pending in the hash table with this ID */
273 if (cat_check_idhash (hfsmp, nextCNID)) {
274 /* Someone wants to insert this into the catalog but hasn't done so yet. Skip it */
275 goto nextid;
276 }
277
278 /* Check to see if a thread record exists for the target ID we just got */
279 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
280 bzero(iterator, sizeof(*iterator));
281 buildthreadkey(nextCNID, std_hfs, (CatalogKey *)&iterator->key);
282
283 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
284 BDINIT(btdata, recp);
285
286 result = BTSearchRecord(hfsmp->hfs_catalog_cp->c_datafork, iterator, &btdata, &datasize, iterator);
287 FREE (recp, M_TEMP);
288 FREE (iterator, M_TEMP);
289
290 if (result == btNotFound) {
291 /* Good. File ID was not in use. Move on to checking EA B-Tree */
292 result = file_attribute_exist (hfsmp, nextCNID);
293 if (result == EEXIST) {
294 /* This CNID has orphaned EAs. Skip it and move on to the next one */
295 result = 0;
296 goto nextid;
297 }
298 if (result) {
299 /* For any other error, return the result */
300 return result;
301 }
302
303 /*
304 * Now validate that there are no lingering cnodes with this ID. If a cnode
305 * has been removed on-disk (marked C_NOEXISTS), but has not yet been reclaimed,
306 * then it will still have an entry in the cnode hash table. This means that
307 * a subsequent lookup will find THAT entry and believe this one has been deleted
308 * prematurely. If there is a lingering cnode, then just skip this entry and move on.
309 *
310 * Note that we pass (existence_only == 1) argument to hfs_chash_snoop.
311 */
312 if (!std_hfs && (hfsmp->vcbAtrb & kHFSCatalogNodeIDsReusedMask)) {
313 if (hfs_chash_snoop (hfsmp, nextCNID, 1, NULL, NULL) == 0) {
314 goto nextid;
315 }
316 }
317
318 /*
319 * If we get here, then we didn't see any thread records, orphaned EAs,
320 * or stale cnodes. This ID is safe to vend out.
321 */
322 *new_cnid = nextCNID;
323 }
324 else if (result == noErr) {
325 /* move on to the next ID */
326 goto nextid;
327 }
328 else {
329 /* For any other situation, just bail out */
330 return EIO;
331 }
332
333 return 0;
334
335}
336
55e303ae 337int
2d21ac55 338cat_preflight(struct hfsmount *hfsmp, catops_t ops, cat_cookie_t *cookie, __unused proc_t p)
55e303ae 339{
2d21ac55 340 int lockflags = 0;
55e303ae
A
341 int result;
342
2d21ac55
A
343 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
344 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
55e303ae 345
2d21ac55
A
346 result = BTReserveSpace(hfsmp->hfs_catalog_cp->c_datafork, ops, (void*)cookie);
347
348 if (lockflags)
349 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae 350
91447636 351 return MacToVFSError(result);
55e303ae
A
352}
353
55e303ae 354void
2d21ac55 355cat_postflight(struct hfsmount *hfsmp, cat_cookie_t *cookie, __unused proc_t p)
55e303ae 356{
2d21ac55 357 int lockflags = 0;
55e303ae 358
2d21ac55
A
359 if (hfsmp->hfs_catalog_cp->c_lockowner != current_thread())
360 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
91447636 361
2d21ac55 362 (void) BTReleaseReserve(hfsmp->hfs_catalog_cp->c_datafork, (void*)cookie);
55e303ae 363
2d21ac55
A
364 if (lockflags)
365 hfs_systemfile_unlock(hfsmp, lockflags);
55e303ae
A
366}
367
6d2010ae 368__private_extern__
9bccf70c
A
369void
370cat_convertattr(
371 struct hfsmount *hfsmp,
372 CatalogRecord * recp,
373 struct cat_attr *attrp,
374 struct cat_fork *datafp,
375 struct cat_fork *rsrcfp)
376{
377 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
378
39236c6e
A
379 if (std_hfs == 0) {
380 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
381 }
382#if CONFIG_HFS_STD
383 else {
9bccf70c
A
384 struct HFSPlusCatalogFile cnoderec;
385
386 promoteattr(hfsmp, recp, &cnoderec);
387 getbsdattr(hfsmp, &cnoderec, attrp);
39236c6e
A
388 }
389#endif
9bccf70c 390
39236c6e 391 if (isadir(recp)) {
9bccf70c 392 bzero(datafp, sizeof(*datafp));
39236c6e
A
393 }
394#if CONFIG_HFS_STD
9bccf70c
A
395 else if (std_hfs) {
396 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 0, datafp);
397 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, 1, rsrcfp);
39236c6e
A
398 }
399#endif
400 else {
55e303ae
A
401 /* Convert the data fork. */
402 datafp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
593a1d5f 403 datafp->cf_new_size = 0;
55e303ae
A
404 datafp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
405 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
406 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
407 datafp->cf_bytesread =
408 recp->hfsPlusFile.dataFork.clumpSize *
409 HFSTOVCB(hfsmp)->blockSize;
410 } else {
411 datafp->cf_bytesread = 0;
412 }
413 datafp->cf_vblocks = 0;
414 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
415 &datafp->cf_extents[0], sizeof(HFSPlusExtentRecord));
416
417 /* Convert the resource fork. */
418 rsrcfp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
593a1d5f 419 rsrcfp->cf_new_size = 0;
55e303ae
A
420 rsrcfp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
421 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
422 (attrp->ca_atime >= hfsmp->hfc_timebase)) {
423 datafp->cf_bytesread =
424 recp->hfsPlusFile.resourceFork.clumpSize *
425 HFSTOVCB(hfsmp)->blockSize;
426 } else {
427 datafp->cf_bytesread = 0;
428 }
429 rsrcfp->cf_vblocks = 0;
430 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
431 &rsrcfp->cf_extents[0], sizeof(HFSPlusExtentRecord));
9bccf70c
A
432 }
433}
434
4a3eedf9
A
435/*
436 * Convert a raw catalog key and record into an in-core catalog descriptor.
437 *
438 * Note: The caller is responsible for releasing the catalog descriptor.
439 */
55e303ae 440__private_extern__
9bccf70c
A
441int
442cat_convertkey(
443 struct hfsmount *hfsmp,
444 CatalogKey *key,
445 CatalogRecord * recp,
446 struct cat_desc *descp)
447{
448 int std_hfs = HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord;
449 HFSPlusCatalogKey * pluskey = NULL;
b0d623f7 450 u_int32_t encoding;
39236c6e
A
451 cnid_t cnid = 0;
452 int err = 0;
9bccf70c 453
39236c6e 454 if (std_hfs == 0) {
9bccf70c
A
455 pluskey = (HFSPlusCatalogKey *)key;
456 encoding = getencoding(recp);
457 }
39236c6e
A
458#if CONFIG_HFS_STD
459 else {
460 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
461 promotekey(hfsmp, (HFSCatalogKey *)key, pluskey, &encoding);
462 }
463#endif
464
465 /* Get the CNID before calling builddesc. Need to error check it. */
466 cnid = getcnid(recp);
467 if (cnid == 0) {
468 /* If ths CNID == 0, it's invalid. Mark as corrupt */
fe8ab488 469 hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
39236c6e
A
470 err = EINVAL;
471 }
472 else {
473 builddesc(pluskey, cnid, 0, encoding, isadir(recp), descp);
474 }
9bccf70c 475
39236c6e 476#if CONFIG_HFS_STD
9bccf70c
A
477 if (std_hfs) {
478 FREE(pluskey, M_TEMP);
479 }
39236c6e
A
480#endif
481
482 return err;
9bccf70c
A
483}
484
485
486/*
487 * cat_releasedesc
488 */
55e303ae 489__private_extern__
9bccf70c
A
490void
491cat_releasedesc(struct cat_desc *descp)
492{
2d21ac55 493 const u_int8_t * name;
9bccf70c
A
494
495 if (descp == NULL)
496 return;
497
498 if ((descp->cd_flags & CD_HASBUF) &&
499 (descp->cd_nameptr != NULL)) {
500 name = descp->cd_nameptr;
501 descp->cd_nameptr = NULL;
502 descp->cd_namelen = 0;
2d21ac55 503 vfs_removename((const char *)name);
9bccf70c
A
504 }
505 descp->cd_nameptr = NULL;
506 descp->cd_namelen = 0;
2d21ac55 507 descp->cd_flags &= ~CD_HASBUF;
9bccf70c
A
508}
509
510/*
511 * These Catalog functions allow access to the HFS Catalog (database).
935ed37a 512 * The catalog b-tree lock must be acquired before calling any of these routines.
9bccf70c
A
513 */
514
515/*
935ed37a 516 * cat_lookup - lookup a catalog node using a cnode descriptor
4a3eedf9
A
517 *
518 * Note: The caller is responsible for releasing the output
519 * catalog descriptor (when supplied outdescp is non-null).
9bccf70c
A
520 */
521int
39236c6e 522cat_lookup(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc, int force_casesensitive_lookup,
9bccf70c 523 struct cat_desc *outdescp, struct cat_attr *attrp,
91447636 524 struct cat_fork *forkp, cnid_t *desc_cnid)
9bccf70c
A
525{
526 CatalogKey * keyp;
527 int std_hfs;
528 int result;
39236c6e 529 int flags;
9bccf70c
A
530
531 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
39236c6e 532 flags = force_casesensitive_lookup ? HFS_LOOKUP_CASESENSITIVE : 0;
9bccf70c
A
533
534 MALLOC(keyp, CatalogKey *, sizeof(CatalogKey), M_TEMP, M_WAITOK);
535
536 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)keyp, 1);
537 if (result)
538 goto exit;
539
39236c6e 540 result = cat_lookupbykey(hfsmp, keyp, flags, descp->cd_hint, wantrsrc, outdescp, attrp, forkp, desc_cnid);
9bccf70c
A
541
542 if (result == ENOENT) {
543 if (!std_hfs) {
91447636
A
544 struct cat_desc temp_desc;
545 if (outdescp == NULL) {
546 bzero(&temp_desc, sizeof(temp_desc));
547 outdescp = &temp_desc;
548 }
9bccf70c 549 result = cat_lookupmangled(hfsmp, descp, wantrsrc, outdescp, attrp, forkp);
91447636
A
550 if (desc_cnid) {
551 *desc_cnid = outdescp->cd_cnid;
552 }
553 if (outdescp == &temp_desc) {
554 /* Release the local copy of desc */
555 cat_releasedesc(outdescp);
556 }
9bccf70c
A
557 } else if (hfsmp->hfs_encoding != kTextEncodingMacRoman) {
558 // make MacRoman key from utf-8
559 // result = cat_lookupbykey(hfsmp, keyp, descp->cd_hint, attrp, forkp);
560 // update desc text encoding so that other catalog ops succeed
561 }
562 }
563exit:
564 FREE(keyp, M_TEMP);
565
566 return (result);
567}
568
569int
570cat_insertfilethread(struct hfsmount *hfsmp, struct cat_desc *descp)
571{
572 struct BTreeIterator *iterator;
573 struct FSBufferDescriptor file_data;
574 struct HFSCatalogFile file_rec;
2d21ac55 575 u_int16_t datasize;
9bccf70c
A
576 FCB *fcb;
577 int result;
578
579 if (HFSTOVCB(hfsmp)->vcbSigWord != kHFSSigWord)
580 return (EINVAL);
581
582 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
583
584 MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
585 bzero(&iterator[0], 2* sizeof(*iterator));
586 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator[0].key, 0);
587 if (result)
588 goto exit;
589
590 BDINIT(file_data, &file_rec);
591 result = BTSearchRecord(fcb, &iterator[0], &file_data, &datasize, &iterator[0]);
592 if (result)
593 goto exit;
594
595 if (file_rec.recordType != kHFSFileRecord) {
596 result = EISDIR;
597 goto exit;
598 }
599
600 if ((file_rec.flags & kHFSThreadExistsMask) == 0) {
601 struct FSBufferDescriptor thread_data;
602 struct HFSCatalogThread thread_rec;
603
604 file_rec.flags |= kHFSThreadExistsMask;
605 BDINIT(thread_data, &thread_rec);
606 thread_data.itemSize = buildthread(&iterator[0].key, &thread_rec, 1, 0);
607 buildthreadkey(file_rec.fileID, 1, (CatalogKey *)&iterator[1].key);
608
609 result = BTInsertRecord(fcb, &iterator[1], &thread_data, thread_data.itemSize);
610 if (result)
611 goto exit;
612
613 (void) BTReplaceRecord(fcb, &iterator[0], &file_data, datasize);
614 (void) BTFlushPath(fcb);
615 }
616exit:
b4c24cb9 617 (void) BTFlushPath(fcb);
9bccf70c
A
618 FREE(iterator, M_TEMP);
619
620 return MacToVFSError(result);
621}
622
623
91447636
A
624/*
625 * cat_findname - obtain a descriptor from cnid
626 *
627 * Only a thread lookup is performed.
4a3eedf9
A
628 *
629 * Note: The caller is responsible for releasing the output
630 * catalog descriptor (when supplied outdescp is non-null).
631
91447636 632 */
91447636
A
633int
634cat_findname(struct hfsmount *hfsmp, cnid_t cnid, struct cat_desc *outdescp)
635{
636 struct BTreeIterator * iterator;
637 FSBufferDescriptor btdata;
638 CatalogKey * keyp;
639 CatalogRecord * recp;
640 int isdir;
641 int result;
642 int std_hfs;
643
644 isdir = 0;
645 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
646
647 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
648 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
649 iterator->hint.nodeNum = 0;
650
651 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
652 BDINIT(btdata, recp);
653
654 result = BTSearchRecord(VTOF(hfsmp->hfs_catalog_vp), iterator, &btdata, NULL, NULL);
655 if (result)
656 goto exit;
657
658 /* Turn thread record into a cnode key (in place). */
659 switch (recp->recordType) {
91447636 660
39236c6e
A
661#if CONFIG_HFS_STD
662 case kHFSFolderThreadRecord:
663 isdir = 1;
664 /* fall through */
665 case kHFSFileThreadRecord:
666 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
667 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
668 break;
669#endif
670
671 case kHFSPlusFolderThreadRecord:
672 isdir = 1;
673 /* fall through */
674 case kHFSPlusFileThreadRecord:
675 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
676 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
677 (keyp->hfsPlus.nodeName.length * 2);
678 break;
679 default:
680 result = ENOENT;
681 goto exit;
91447636 682 }
39236c6e
A
683
684 if (std_hfs == 0) {
685 builddesc((HFSPlusCatalogKey *)keyp, cnid, 0, 0, isdir, outdescp);
686 }
687#if CONFIG_HFS_STD
688 else {
91447636 689 HFSPlusCatalogKey * pluskey = NULL;
b0d623f7 690 u_int32_t encoding;
91447636
A
691
692 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
693 promotekey(hfsmp, &keyp->hfs, pluskey, &encoding);
694 builddesc(pluskey, cnid, 0, encoding, isdir, outdescp);
695 FREE(pluskey, M_TEMP);
91447636 696 }
39236c6e
A
697#endif
698
91447636
A
699exit:
700 FREE(recp, M_TEMP);
701 FREE(iterator, M_TEMP);
702
703 return MacToVFSError(result);
704}
705
9bccf70c
A
706/*
707 * cat_idlookup - lookup a catalog node using a cnode id
4a3eedf9
A
708 *
709 * Note: The caller is responsible for releasing the output
710 * catalog descriptor (when supplied outdescp is non-null).
9bccf70c
A
711 */
712int
db609669 713cat_idlookup(struct hfsmount *hfsmp, cnid_t cnid, int allow_system_files, int wantrsrc,
2d21ac55 714 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
9bccf70c
A
715{
716 struct BTreeIterator * iterator;
717 FSBufferDescriptor btdata;
2d21ac55 718 u_int16_t datasize;
9bccf70c
A
719 CatalogKey * keyp;
720 CatalogRecord * recp;
721 int result;
722 int std_hfs;
723
724 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
725
726 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
727 bzero(iterator, sizeof(*iterator));
728 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
729
730 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
731 BDINIT(btdata, recp);
732
733 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
734 &btdata, &datasize, iterator);
735 if (result)
736 goto exit;
737
738 /* Turn thread record into a cnode key (in place) */
739 switch (recp->recordType) {
b0d623f7 740
39236c6e
A
741#if CONFIG_HFS_STD
742 case kHFSFileThreadRecord:
743 case kHFSFolderThreadRecord:
744 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
b0d623f7 745
39236c6e
A
746 /* check for NULL name */
747 if (keyp->hfs.nodeName[0] == 0) {
748 result = ENOENT;
749 goto exit;
750 }
9bccf70c 751
39236c6e
A
752 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
753 break;
754#endif
b0d623f7 755
39236c6e
A
756 case kHFSPlusFileThreadRecord:
757 case kHFSPlusFolderThreadRecord:
758 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
b0d623f7 759
39236c6e
A
760 /* check for NULL name */
761 if (keyp->hfsPlus.nodeName.length == 0) {
762 result = ENOENT;
763 goto exit;
764 }
9bccf70c 765
39236c6e
A
766 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
767 (keyp->hfsPlus.nodeName.length * 2);
768 break;
769
770 default:
771 result = ENOENT;
772 goto exit;
9bccf70c
A
773 }
774
6d2010ae
A
775 result = cat_lookupbykey(hfsmp, keyp,
776 ((allow_system_files != 0) ? HFS_LOOKUP_SYSFILE : 0),
db609669 777 0, wantrsrc, outdescp, attrp, forkp, NULL);
2d21ac55
A
778 /* No corresponding file/folder record found for a thread record,
779 * mark the volume inconsistent.
780 */
781 if (result == 0 && outdescp) {
8f6c56a5
A
782 cnid_t dcnid = outdescp->cd_cnid;
783 /*
2d21ac55 784 * Just for sanity's case, let's make sure that
8f6c56a5
A
785 * the key in the thread matches the key in the record.
786 */
787 if (cnid != dcnid) {
b0d623f7 788 printf("hfs: cat_idlookup: Requested cnid (%d / %08x) != dcnid (%d / %08x)\n", cnid, cnid, dcnid, dcnid);
8f6c56a5
A
789 result = ENOENT;
790 }
791 }
9bccf70c
A
792exit:
793 FREE(recp, M_TEMP);
794 FREE(iterator, M_TEMP);
795
796 return MacToVFSError(result);
797}
798
799
800/*
801 * cat_lookupmangled - lookup a catalog node using a mangled name
802 */
6d2010ae 803int
9bccf70c
A
804cat_lookupmangled(struct hfsmount *hfsmp, struct cat_desc *descp, int wantrsrc,
805 struct cat_desc *outdescp, struct cat_attr *attrp, struct cat_fork *forkp)
806{
807 cnid_t fileID;
2d21ac55 808 u_int32_t prefixlen;
9bccf70c 809 int result;
316670eb
A
810 u_int8_t utf8[NAME_MAX + 1];
811 u_int32_t utf8len;
812 u_int16_t unicode[kHFSPlusMaxFileNameChars + 1];
813 size_t unicodelen;
9bccf70c
A
814
815 if (wantrsrc)
816 return (ENOENT);
817
818 fileID = GetEmbeddedFileID(descp->cd_nameptr, descp->cd_namelen, &prefixlen);
0c530ab8
A
819 if (fileID < (cnid_t)kHFSFirstUserCatalogNodeID)
820 return (ENOENT);
6601e61a 821
2d21ac55
A
822 if (fileID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
823 fileID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid ||
6601e61a
A
824 fileID == hfsmp->hfs_jnlfileid ||
825 fileID == hfsmp->hfs_jnlinfoblkid) {
9bccf70c 826 return (ENOENT);
6601e61a 827 }
9bccf70c 828
39236c6e 829 result = cat_idlookup(hfsmp, fileID, 0, 0, outdescp, attrp, forkp);
9bccf70c
A
830 if (result)
831 return (ENOENT);
9bccf70c
A
832 /* It must be in the correct directory */
833 if (descp->cd_parentcnid != outdescp->cd_parentcnid)
834 goto falsematch;
835
316670eb
A
836 /*
837 * Compare the mangled version of file name looked up from the
838 * disk with the mangled name provided by the user. Note that
839 * this comparison is case-sensitive, which should be fine
840 * since we're trying to prevent user space from constructing
841 * a mangled name that differs from the one they'd get from the
842 * file system.
843 */
844 result = utf8_decodestr(outdescp->cd_nameptr, outdescp->cd_namelen,
845 unicode, &unicodelen, sizeof(unicode), ':', 0);
846 if (result) {
b0d623f7 847 goto falsematch;
316670eb
A
848 }
849 result = ConvertUnicodeToUTF8Mangled(unicodelen, unicode,
850 sizeof(utf8), &utf8len, utf8, fileID);
851 if ((result != 0) ||
852 ((u_int16_t)descp->cd_namelen != utf8len) ||
853 (bcmp(descp->cd_nameptr, utf8, utf8len) != 0)) {
b0d623f7 854 goto falsematch;
316670eb 855 }
b0d623f7 856
9bccf70c
A
857 return (0);
858
859falsematch:
860 cat_releasedesc(outdescp);
861 return (ENOENT);
862}
863
864
865/*
866 * cat_lookupbykey - lookup a catalog node using a cnode key
867 */
868static int
6d2010ae 869cat_lookupbykey(struct hfsmount *hfsmp, CatalogKey *keyp, int flags, u_int32_t hint, int wantrsrc,
91447636 870 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp, cnid_t *desc_cnid)
9bccf70c
A
871{
872 struct BTreeIterator * iterator;
873 FSBufferDescriptor btdata;
874 CatalogRecord * recp;
2d21ac55 875 u_int16_t datasize;
9bccf70c
A
876 int result;
877 int std_hfs;
b0d623f7 878 u_int32_t ilink = 0;
9bccf70c 879 cnid_t cnid = 0;
b0d623f7 880 u_int32_t encoding = 0;
6d2010ae 881 cnid_t parentid = 0;
9bccf70c
A
882
883 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
884
885 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
886 BDINIT(btdata, recp);
887 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
888 bzero(iterator, sizeof(*iterator));
889 iterator->hint.nodeNum = hint;
890 bcopy(keyp, &iterator->key, sizeof(CatalogKey));
891
892 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
893 &btdata, &datasize, iterator);
894 if (result)
895 goto exit;
896
6d2010ae 897 /* Save the cnid, parentid, and encoding now in case there's a hard link or inode */
9bccf70c 898 cnid = getcnid(recp);
39236c6e
A
899 if (cnid == 0) {
900 /* CNID of 0 is invalid. Mark as corrupt */
fe8ab488 901 hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
39236c6e
A
902 result = EINVAL;
903 goto exit;
904 }
905
906 if (std_hfs == 0) {
6d2010ae
A
907 parentid = keyp->hfsPlus.parentID;
908 }
316670eb 909
9bccf70c
A
910 encoding = getencoding(recp);
911 hint = iterator->hint.nodeNum;
912
b4c24cb9 913 /* Hide the journal files (if any) */
743b1565 914 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
2d21ac55 915 ((cnid == hfsmp->hfs_jnlfileid) || (cnid == hfsmp->hfs_jnlinfoblkid)) &&
6d2010ae 916 !(flags & HFS_LOOKUP_SYSFILE)) {
39236c6e 917 result = ERESERVEDNAME;
b4c24cb9
A
918 goto exit;
919 }
39236c6e
A
920
921 if (!std_hfs && !(hfsmp->hfs_flags & HFS_CASE_SENSITIVE)) {
922 /* Make sure the case of the file was correct if requested */
923 if (flags & HFS_LOOKUP_CASESENSITIVE) {
924 if (0 != cat_binarykeycompare(&keyp->hfsPlus, (HFSPlusCatalogKey *)&iterator->key)) {
925 result = ERESERVEDNAME;
926 goto exit;
927 }
928 }
929 }
2d21ac55 930
9bccf70c 931 /*
2d21ac55
A
932 * When a hardlink link is encountered, auto resolve it.
933 *
934 * The catalog record will change, and possibly its type.
9bccf70c
A
935 */
936 if (!std_hfs
937 && (attrp || forkp)
938 && (recp->recordType == kHFSPlusFileRecord)
6d2010ae 939 && ((to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_itime) ||
91447636 940 (to_bsd_time(recp->hfsPlusFile.createDate) == (time_t)hfsmp->hfs_metadata_createdate))) {
2d21ac55
A
941 int isdirlink = 0;
942 int isfilelink = 0;
943
944 if ((SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
945 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
946 isfilelink = 1;
947 } else if ((recp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
948 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
949 (SWAP_BE32(recp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
950 isdirlink = 1;
951 }
6d2010ae 952 if ((isfilelink || isdirlink) && !(flags & HFS_LOOKUP_HARDLINK)) {
2d21ac55
A
953 ilink = recp->hfsPlusFile.hl_linkReference;
954 (void) cat_resolvelink(hfsmp, ilink, isdirlink, (struct HFSPlusCatalogFile *)recp);
955 }
9bccf70c
A
956 }
957
39236c6e
A
958 if (attrp != NULL) {
959 if (std_hfs == 0) {
9bccf70c 960 getbsdattr(hfsmp, (struct HFSPlusCatalogFile *)recp, attrp);
6d2010ae
A
961 if (ilink) {
962 /* Update the inode number for this hard link */
2d21ac55 963 attrp->ca_linkref = ilink;
6d2010ae 964 }
316670eb 965
6d2010ae
A
966 /*
967 * Set kHFSHasLinkChainBit for hard links, and reset it for all
968 * other items. Also set linkCount to 1 for regular files.
969 *
970 * Due to some bug (rdar://8505977), some regular files can have
971 * kHFSHasLinkChainBit set and linkCount more than 1 even if they
972 * are not really hard links. The runtime code should not consider
973 * these files has hard links. Therefore we reset the kHFSHasLinkChainBit
974 * and linkCount for regular file before we vend it out. This might
975 * also result in repairing the bad files on disk, if the corresponding
976 * file is modified and updated on disk.
977 */
978 if (ilink) {
979 /* This is a hard link and the link count bit was not set */
980 if (!(attrp->ca_recflags & kHFSHasLinkChainMask)) {
981 printf ("hfs: set hardlink bit on vol=%s cnid=%u inoid=%u\n", hfsmp->vcbVN, cnid, ilink);
982 attrp->ca_recflags |= kHFSHasLinkChainMask;
983 }
984 } else {
985 /* Make sure that this non-hard link (regular) record is not
316670eb
A
986 * an inode record that was looked up and we do not end up
987 * reseting the hard link bit on it.
6d2010ae 988 */
316670eb 989 if ((parentid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
6d2010ae
A
990 (parentid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
991 /* This is not a hard link or inode and the link count bit was set */
992 if (attrp->ca_recflags & kHFSHasLinkChainMask) {
993 printf ("hfs: clear hardlink bit on vol=%s cnid=%u\n", hfsmp->vcbVN, cnid);
994 attrp->ca_recflags &= ~kHFSHasLinkChainMask;
995 }
996 /* This is a regular file and the link count was more than 1 */
997 if (S_ISREG(attrp->ca_mode) && (attrp->ca_linkcount > 1)) {
998 printf ("hfs: set linkcount=1 on vol=%s cnid=%u old=%u\n", hfsmp->vcbVN, cnid, attrp->ca_linkcount);
999 attrp->ca_linkcount = 1;
1000 }
1001 }
1002 }
9bccf70c 1003 }
39236c6e
A
1004#if CONFIG_HFS_STD
1005 else {
1006 struct HFSPlusCatalogFile cnoderec;
1007
1008 promoteattr(hfsmp, recp, &cnoderec);
1009 getbsdattr(hfsmp, &cnoderec, attrp);
1010 }
1011#endif
9bccf70c
A
1012 }
1013 if (forkp != NULL) {
55e303ae 1014 if (isadir(recp)) {
9bccf70c 1015 bzero(forkp, sizeof(*forkp));
39236c6e
A
1016 }
1017#if CONFIG_HFS_STD
1018 else if (std_hfs) {
9bccf70c 1019 promotefork(hfsmp, (HFSCatalogFile *)&recp->hfsFile, wantrsrc, forkp);
39236c6e
A
1020 }
1021#endif
1022 else if (wantrsrc) {
55e303ae
A
1023 /* Convert the resource fork. */
1024 forkp->cf_size = recp->hfsPlusFile.resourceFork.logicalSize;
593a1d5f 1025 forkp->cf_new_size = 0;
55e303ae
A
1026 forkp->cf_blocks = recp->hfsPlusFile.resourceFork.totalBlocks;
1027 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
1028 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
1029 forkp->cf_bytesread =
1030 recp->hfsPlusFile.resourceFork.clumpSize *
1031 HFSTOVCB(hfsmp)->blockSize;
1032 } else {
1033 forkp->cf_bytesread = 0;
1034 }
1035 forkp->cf_vblocks = 0;
1036 bcopy(&recp->hfsPlusFile.resourceFork.extents[0],
1037 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
1038 } else {
5d5c5d0d
A
1039 int i;
1040 u_int32_t validblks;
1041
55e303ae
A
1042 /* Convert the data fork. */
1043 forkp->cf_size = recp->hfsPlusFile.dataFork.logicalSize;
593a1d5f 1044 forkp->cf_new_size = 0;
55e303ae
A
1045 forkp->cf_blocks = recp->hfsPlusFile.dataFork.totalBlocks;
1046 if ((hfsmp->hfc_stage == HFC_RECORDING) &&
1047 (to_bsd_time(recp->hfsPlusFile.accessDate) >= hfsmp->hfc_timebase)) {
1048 forkp->cf_bytesread =
1049 recp->hfsPlusFile.dataFork.clumpSize *
1050 HFSTOVCB(hfsmp)->blockSize;
1051 } else {
1052 forkp->cf_bytesread = 0;
1053 }
1054 forkp->cf_vblocks = 0;
1055 bcopy(&recp->hfsPlusFile.dataFork.extents[0],
1056 &forkp->cf_extents[0], sizeof(HFSPlusExtentRecord));
5d5c5d0d
A
1057
1058 /* Validate the fork's resident extents. */
1059 validblks = 0;
1060 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
1061 if (forkp->cf_extents[i].startBlock + forkp->cf_extents[i].blockCount >= hfsmp->totalBlocks) {
1062 /* Suppress any bad extents so a remove can succeed. */
1063 forkp->cf_extents[i].startBlock = 0;
1064 forkp->cf_extents[i].blockCount = 0;
1065 /* Disable writes */
1066 if (attrp != NULL) {
1067 attrp->ca_mode &= S_IFMT | S_IRUSR | S_IRGRP | S_IROTH;
1068 }
1069 } else {
1070 validblks += forkp->cf_extents[i].blockCount;
1071 }
1072 }
1073 /* Adjust for any missing blocks. */
1074 if ((validblks < forkp->cf_blocks) && (forkp->cf_extents[7].blockCount == 0)) {
2d21ac55 1075 off_t psize;
5d5c5d0d 1076
6d2010ae
A
1077 /*
1078 * This is technically a volume corruption.
1079 * If the total number of blocks calculated by iterating + summing
1080 * the extents in the resident extent records, is less than that
1081 * which is reported in the catalog entry, we should force a fsck.
1082 * Only modifying ca_blocks here is not guaranteed to make it out
1083 * to disk; it is a runtime-only field.
1084 *
1085 * Note that we could have gotten into this state if we had invalid ranges
1086 * that existed in borrowed blocks that somehow made it out to disk.
1087 * The cnode's on disk block count should never be greater
1088 * than that which is in its extent records.
1089 */
1090
fe8ab488 1091 (void) hfs_mark_inconsistent (hfsmp, HFS_INCONSISTENCY_DETECTED);
6d2010ae 1092
5d5c5d0d
A
1093 forkp->cf_blocks = validblks;
1094 if (attrp != NULL) {
1095 attrp->ca_blocks = validblks + recp->hfsPlusFile.resourceFork.totalBlocks;
1096 }
2d21ac55 1097 psize = (off_t)validblks * (off_t)hfsmp->blockSize;
5d5c5d0d
A
1098 if (psize < forkp->cf_size) {
1099 forkp->cf_size = psize;
1100 }
1101
1102 }
55e303ae 1103 }
9bccf70c
A
1104 }
1105 if (descp != NULL) {
1106 HFSPlusCatalogKey * pluskey = NULL;
1107
39236c6e
A
1108 if (std_hfs == 0) {
1109 pluskey = (HFSPlusCatalogKey *)&iterator->key;
1110 }
1111#if CONFIG_HFS_STD
1112 else {
9bccf70c
A
1113 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1114 promotekey(hfsmp, (HFSCatalogKey *)&iterator->key, pluskey, &encoding);
1115
2d21ac55 1116 }
39236c6e
A
1117#endif
1118
9bccf70c 1119 builddesc(pluskey, cnid, hint, encoding, isadir(recp), descp);
39236c6e
A
1120
1121#if CONFIG_HFS_STD
9bccf70c
A
1122 if (std_hfs) {
1123 FREE(pluskey, M_TEMP);
1124 }
39236c6e
A
1125#endif
1126
9bccf70c 1127 }
91447636
A
1128
1129 if (desc_cnid != NULL) {
1130 *desc_cnid = cnid;
1131 }
9bccf70c
A
1132exit:
1133 FREE(iterator, M_TEMP);
1134 FREE(recp, M_TEMP);
1135
1136 return MacToVFSError(result);
1137}
1138
1139
1140/*
1141 * cat_create - create a node in the catalog
2d21ac55
A
1142 *
1143 * NOTE: both the catalog file and attribute file locks must
1144 * be held before calling this function.
4a3eedf9
A
1145 *
1146 * The caller is responsible for releasing the output
1147 * catalog descriptor (when supplied outdescp is non-null).
9bccf70c
A
1148 */
1149int
39236c6e 1150cat_create(struct hfsmount *hfsmp, cnid_t new_fileid, struct cat_desc *descp, struct cat_attr *attrp,
9bccf70c
A
1151 struct cat_desc *out_descp)
1152{
9bccf70c
A
1153 FCB * fcb;
1154 struct btobj * bto;
1155 FSBufferDescriptor btdata;
9bccf70c
A
1156 u_int32_t datalen;
1157 int std_hfs;
91447636 1158 int result = 0;
b0d623f7 1159 u_int32_t encoding = kTextEncodingMacRoman;
9bccf70c
A
1160 int modeformat;
1161
1162 modeformat = attrp->ca_mode & S_IFMT;
1163
2d21ac55
A
1164 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1165 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
9bccf70c 1166
39236c6e
A
1167 /* The caller is expected to reserve a CNID before calling this function! */
1168
9bccf70c
A
1169 /* Get space for iterator, key and data */
1170 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
91447636 1171 bto->iterator.hint.nodeNum = 0;
9bccf70c
A
1172
1173 result = buildkey(hfsmp, descp, &bto->key, 0);
1174 if (result)
1175 goto exit;
1176
1177 if (!std_hfs) {
1178 encoding = hfs_pickencoding(bto->key.nodeName.unicode,
1179 bto->key.nodeName.length);
1180 hfs_setencodingbits(hfsmp, encoding);
1181 }
1182
1183 /*
1184 * Insert the thread record first
1185 */
1186 if (!std_hfs || (modeformat == S_IFDIR)) {
1187 datalen = buildthread((void*)&bto->key, &bto->data, std_hfs,
1188 S_ISDIR(attrp->ca_mode));
1189 btdata.bufferAddress = &bto->data;
1190 btdata.itemSize = datalen;
1191 btdata.itemCount = 1;
1192
39236c6e
A
1193 /* Caller asserts the following:
1194 * 1) this CNID is not in use by any orphaned EAs
1195 * 2) There are no lingering cnodes (removed on-disk but still in-core) with this CNID
1196 * 3) There are no thread or catalog records for this ID
1197 */
1198 buildthreadkey(new_fileid, std_hfs, (CatalogKey *) &bto->iterator.key);
1199 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
1200 if (result) {
1201 goto exit;
91447636 1202 }
91447636 1203 }
9bccf70c
A
1204
1205 /*
1206 * Now insert the file/directory record
1207 */
39236c6e 1208 buildrecord(attrp, new_fileid, std_hfs, encoding, &bto->data, &datalen);
9bccf70c
A
1209 btdata.bufferAddress = &bto->data;
1210 btdata.itemSize = datalen;
1211 btdata.itemCount = 1;
1212
1213 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
1214
1215 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
1216 if (result) {
1217 if (result == btExists)
1218 result = EEXIST;
1219
1220 /* Back out the thread record */
1221 if (!std_hfs || S_ISDIR(attrp->ca_mode)) {
39236c6e 1222 buildthreadkey(new_fileid, std_hfs, (CatalogKey *)&bto->iterator.key);
2d21ac55
A
1223 if (BTDeleteRecord(fcb, &bto->iterator)) {
1224 /* Error on deleting extra thread record, mark
1225 * volume inconsistent
1226 */
39236c6e 1227 printf ("hfs: cat_create() failed to delete thread record id=%u on vol=%s\n", new_fileid, hfsmp->vcbVN);
fe8ab488 1228 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
2d21ac55 1229 }
9bccf70c
A
1230 }
1231 goto exit;
1232 }
1233
1234 /*
2d21ac55 1235 * Insert was successful, update name, parent and volume
9bccf70c 1236 */
9bccf70c
A
1237 if (out_descp != NULL) {
1238 HFSPlusCatalogKey * pluskey = NULL;
1239
39236c6e
A
1240 if (std_hfs == 0) {
1241 pluskey = (HFSPlusCatalogKey *)&bto->iterator.key;
1242 }
1243#if CONFIG_HFS_STD
1244 else {
9bccf70c
A
1245 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1246 promotekey(hfsmp, (HFSCatalogKey *)&bto->iterator.key, pluskey, &encoding);
39236c6e
A
1247 }
1248#endif
9bccf70c 1249
39236c6e 1250 builddesc(pluskey, new_fileid, bto->iterator.hint.nodeNum,
9bccf70c 1251 encoding, S_ISDIR(attrp->ca_mode), out_descp);
39236c6e 1252#if CONFIG_HFS_STD
9bccf70c
A
1253 if (std_hfs) {
1254 FREE(pluskey, M_TEMP);
1255 }
39236c6e
A
1256#endif
1257
9bccf70c 1258 }
39236c6e 1259 attrp->ca_fileid = new_fileid;
9bccf70c 1260
9bccf70c 1261exit:
b4c24cb9 1262 (void) BTFlushPath(fcb);
9bccf70c
A
1263 FREE(bto, M_TEMP);
1264
1265 return MacToVFSError(result);
1266}
1267
1268
1269/*
1270 * cnode_rename - rename a catalog node
1271 *
1272 * Assumes that the target's directory exists.
1273 *
1274 * Order of B-tree operations:
1275 * 1. BTSearchRecord(from_cnode, &data);
1276 * 2. BTInsertRecord(to_cnode, &data);
1277 * 3. BTDeleteRecord(from_cnode);
1278 * 4. BTDeleteRecord(from_thread);
1279 * 5. BTInsertRecord(to_thread);
4a3eedf9
A
1280 *
1281 * Note: The caller is responsible for releasing the output
1282 * catalog descriptor (when supplied out_cdp is non-null).
9bccf70c
A
1283 */
1284int
1285cat_rename (
1286 struct hfsmount * hfsmp,
1287 struct cat_desc * from_cdp,
1288 struct cat_desc * todir_cdp,
1289 struct cat_desc * to_cdp,
1290 struct cat_desc * out_cdp )
1291{
1292 struct BTreeIterator * to_iterator = NULL;
1293 struct BTreeIterator * from_iterator = NULL;
1294 FSBufferDescriptor btdata;
1295 CatalogRecord * recp = NULL;
1296 HFSPlusCatalogKey * to_key;
1297 ExtendedVCB * vcb;
1298 FCB * fcb;
2d21ac55 1299 u_int16_t datasize;
9bccf70c
A
1300 int result = 0;
1301 int sourcegone = 0;
1302 int skipthread = 0;
1303 int directory = from_cdp->cd_flags & CD_ISDIR;
2d21ac55 1304 int is_dirlink = 0;
9bccf70c 1305 int std_hfs;
b0d623f7 1306 u_int32_t encoding = 0;
9bccf70c
A
1307
1308 vcb = HFSTOVCB(hfsmp);
1309 fcb = GetFileControlBlock(vcb->catalogRefNum);
1310 std_hfs = (vcb->vcbSigWord == kHFSSigWord);
1311
1312 if (from_cdp->cd_namelen == 0 || to_cdp->cd_namelen == 0)
1313 return (EINVAL);
1314
1315 MALLOC(from_iterator, BTreeIterator *, sizeof(*from_iterator), M_TEMP, M_WAITOK);
1316 bzero(from_iterator, sizeof(*from_iterator));
1317 if ((result = buildkey(hfsmp, from_cdp, (HFSPlusCatalogKey *)&from_iterator->key, 0)))
1318 goto exit;
1319
1320 MALLOC(to_iterator, BTreeIterator *, sizeof(*to_iterator), M_TEMP, M_WAITOK);
1321 bzero(to_iterator, sizeof(*to_iterator));
1322 if ((result = buildkey(hfsmp, to_cdp, (HFSPlusCatalogKey *)&to_iterator->key, 0)))
1323 goto exit;
1324
1325 to_key = (HFSPlusCatalogKey *)&to_iterator->key;
1326 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
1327 BDINIT(btdata, recp);
1328
1329 /*
1330 * When moving a directory, make sure its a valid move.
1331 */
1332 if (directory && (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)) {
316670eb
A
1333 struct BTreeIterator *dir_iterator = NULL;
1334
9bccf70c
A
1335 cnid_t cnid = from_cdp->cd_cnid;
1336 cnid_t pathcnid = todir_cdp->cd_parentcnid;
316670eb 1337
9bccf70c
A
1338 /* First check the obvious ones */
1339 if (cnid == fsRtDirID ||
1340 cnid == to_cdp->cd_parentcnid ||
1341 cnid == pathcnid) {
1342 result = EINVAL;
1343 goto exit;
1344 }
316670eb
A
1345 /* now allocate the dir_iterator */
1346 MALLOC (dir_iterator, struct BTreeIterator*, sizeof(struct BTreeIterator), M_TEMP, M_WAITOK);
1347 if (dir_iterator == NULL) {
1348 return ENOMEM;
1349 }
1350 bzero(dir_iterator, sizeof(*dir_iterator));
1351
9bccf70c 1352 /*
2d21ac55 1353 * Traverse destination path all the way back to the root
9bccf70c
A
1354 * making sure that source directory is not encountered.
1355 *
1356 */
1357 while (pathcnid > fsRtDirID) {
316670eb
A
1358 buildthreadkey(pathcnid, std_hfs, (CatalogKey *)&dir_iterator->key);
1359 result = BTSearchRecord(fcb, dir_iterator, &btdata, &datasize, NULL);
1360 if (result) {
1361 FREE(dir_iterator, M_TEMP);
1362 goto exit;
1363 }
9bccf70c 1364 pathcnid = getparentcnid(recp);
2d21ac55 1365 if (pathcnid == cnid || pathcnid == 0) {
9bccf70c 1366 result = EINVAL;
316670eb 1367 FREE(dir_iterator, M_TEMP);
9bccf70c
A
1368 goto exit;
1369 }
1370 }
316670eb 1371 FREE(dir_iterator, M_TEMP);
9bccf70c
A
1372 }
1373
1374 /*
1375 * Step 1: Find cnode data at old location
1376 */
1377 result = BTSearchRecord(fcb, from_iterator, &btdata,
1378 &datasize, from_iterator);
91447636
A
1379 if (result) {
1380 if (std_hfs || (result != btNotFound))
1381 goto exit;
2d21ac55 1382
91447636
A
1383 struct cat_desc temp_desc;
1384
1385 /* Probably the node has mangled name */
1386 result = cat_lookupmangled(hfsmp, from_cdp, 0, &temp_desc, NULL, NULL);
2d21ac55 1387 if (result)
91447636 1388 goto exit;
2d21ac55 1389
91447636
A
1390 /* The file has mangled name. Search the cnode data using full name */
1391 bzero(from_iterator, sizeof(*from_iterator));
1392 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&from_iterator->key, 0);
1393 if (result) {
1394 cat_releasedesc(&temp_desc);
1395 goto exit;
1396 }
1397
1398 result = BTSearchRecord(fcb, from_iterator, &btdata, &datasize, from_iterator);
1399 if (result) {
1400 cat_releasedesc(&temp_desc);
1401 goto exit;
1402 }
2d21ac55 1403
91447636
A
1404 cat_releasedesc(&temp_desc);
1405 }
9bccf70c 1406
2d21ac55
A
1407 /* Check if the source is directory hard link. We do not change
1408 * directory flag because it is later used to initialize result descp
1409 */
1410 if ((!std_hfs) &&
1411 (directory) &&
1412 (recp->recordType == kHFSPlusFileRecord) &&
1413 (recp->hfsPlusFile.flags & kHFSHasLinkChainMask)) {
1414 is_dirlink = 1;
1415 }
1416
1417 /*
1418 * Update the text encoding (on disk and in descriptor).
1419 *
1420 * Note that hardlink inodes don't require a text encoding hint.
1421 */
1422 if (!std_hfs &&
1423 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid &&
1424 todir_cdp->cd_parentcnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1425 encoding = hfs_pickencoding(to_key->nodeName.unicode, to_key->nodeName.length);
9bccf70c
A
1426 hfs_setencodingbits(hfsmp, encoding);
1427 recp->hfsPlusFile.textEncoding = encoding;
1428 if (out_cdp)
1429 out_cdp->cd_encoding = encoding;
1430 }
2d21ac55 1431
39236c6e 1432#if CONFIG_HFS_STD
9bccf70c 1433 if (std_hfs && !directory &&
39236c6e 1434 !(recp->hfsFile.flags & kHFSThreadExistsMask)) {
9bccf70c 1435 skipthread = 1;
39236c6e
A
1436 }
1437#endif
1438
9bccf70c
A
1439#if 0
1440 /*
1441 * If the keys are identical then there's nothing left to do!
1442 *
1443 * update the hint and exit
1444 *
1445 */
1446 if (std_hfs && hfskeycompare(to_key, iter->key) == 0)
1447 goto exit;
1448 if (!std_hfs && hfspluskeycompare(to_key, iter->key) == 0)
1449 goto exit;
1450#endif
1451
9bccf70c
A
1452 /* Step 2: Insert cnode at new location */
1453 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1454 if (result == btExists) {
1455 int fromtype = recp->recordType;
39236c6e 1456 cnid_t cnid = 0;
9bccf70c
A
1457
1458 if (from_cdp->cd_parentcnid != to_cdp->cd_parentcnid)
1459 goto exit; /* EEXIST */
1460
1461 /* Find cnode data at new location */
1462 result = BTSearchRecord(fcb, to_iterator, &btdata, &datasize, NULL);
743b1565
A
1463 if (result)
1464 goto exit;
9bccf70c 1465
39236c6e
A
1466 /* Get the CNID after calling searchrecord */
1467 cnid = getcnid (recp);
1468 if (cnid == 0) {
fe8ab488 1469 hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
39236c6e
A
1470 result = EINVAL;
1471 goto exit;
1472 }
1473
9bccf70c 1474 if ((fromtype != recp->recordType) ||
39236c6e 1475 (from_cdp->cd_cnid != cnid)) {
743b1565 1476 result = EEXIST;
9bccf70c 1477 goto exit; /* EEXIST */
743b1565 1478 }
9bccf70c
A
1479 /* The old name is a case variant and must be removed */
1480 result = BTDeleteRecord(fcb, from_iterator);
1481 if (result)
1482 goto exit;
1483
1484 /* Insert cnode (now that case duplicate is gone) */
1485 result = BTInsertRecord(fcb, to_iterator, &btdata, datasize);
1486 if (result) {
1487 /* Try and restore original before leaving */
b4c24cb9
A
1488 // XXXdbg
1489 #if 1
1490 {
1491 int err;
1492 err = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
2d21ac55 1493 if (err) {
39236c6e 1494 printf("hfs: cat_create: could not undo (BTInsert = %d)\n", err);
fe8ab488 1495 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
2d21ac55
A
1496 result = err;
1497 goto exit;
1498 }
b4c24cb9
A
1499 }
1500 #else
9bccf70c 1501 (void) BTInsertRecord(fcb, from_iterator, &btdata, datasize);
b4c24cb9 1502 #endif
9bccf70c
A
1503 goto exit;
1504 }
1505 sourcegone = 1;
1506 }
1507 if (result)
1508 goto exit;
1509
1510 /* Step 3: Remove cnode from old location */
1511 if (!sourcegone) {
1512 result = BTDeleteRecord(fcb, from_iterator);
1513 if (result) {
1514 /* Try and delete new record before leaving */
b4c24cb9
A
1515 // XXXdbg
1516 #if 1
1517 {
1518 int err;
1519 err = BTDeleteRecord(fcb, to_iterator);
2d21ac55 1520 if (err) {
39236c6e 1521 printf("hfs: cat_create: could not undo (BTDelete = %d)\n", err);
fe8ab488 1522 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
2d21ac55
A
1523 result = err;
1524 goto exit;
1525 }
b4c24cb9
A
1526 }
1527 #else
9bccf70c 1528 (void) BTDeleteRecord(fcb, to_iterator);
b4c24cb9 1529 #endif
9bccf70c
A
1530 goto exit;
1531 }
1532 }
1533
1534 /* #### POINT OF NO RETURN #### */
1535
1536 /*
1537 * Step 4: Remove cnode's old thread record
1538 */
1539 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1540 (void) BTDeleteRecord(fcb, from_iterator);
1541
1542 /*
1543 * Step 5: Insert cnode's new thread record
1544 * (optional for HFS files)
1545 */
1546 if (!skipthread) {
2d21ac55
A
1547 /* For directory hard links, always create a file thread
1548 * record. For everything else, use the directory flag.
1549 */
1550 if (is_dirlink) {
1551 datasize = buildthread(&to_iterator->key, recp, std_hfs, false);
1552 } else {
1553 datasize = buildthread(&to_iterator->key, recp, std_hfs, directory);
1554 }
9bccf70c
A
1555 btdata.itemSize = datasize;
1556 buildthreadkey(from_cdp->cd_cnid, std_hfs, (CatalogKey *)&from_iterator->key);
1557 result = BTInsertRecord(fcb, from_iterator, &btdata, datasize);
1558 }
1559
1560 if (out_cdp) {
1561 HFSPlusCatalogKey * pluskey = NULL;
1562
39236c6e
A
1563 if (std_hfs == 0) {
1564 pluskey = (HFSPlusCatalogKey *)&to_iterator->key;
1565 }
1566#if CONFIG_HFS_STD
1567 else {
9bccf70c
A
1568 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
1569 promotekey(hfsmp, (HFSCatalogKey *)&to_iterator->key, pluskey, &encoding);
55e303ae
A
1570
1571 /* Save the real encoding hint in the Finder Info (field 4). */
1572 if (directory && from_cdp->cd_cnid == kHFSRootFolderID) {
b0d623f7 1573 u_int32_t realhint;
55e303ae
A
1574
1575 realhint = hfs_pickencoding(pluskey->nodeName.unicode, pluskey->nodeName.length);
1576 vcb->vcbFndrInfo[4] = SET_HFS_TEXT_ENCODING(realhint);
1577 }
39236c6e
A
1578 }
1579#endif
9bccf70c
A
1580
1581 builddesc(pluskey, from_cdp->cd_cnid, to_iterator->hint.nodeNum,
1582 encoding, directory, out_cdp);
39236c6e 1583#if CONFIG_HFS_STD
9bccf70c
A
1584 if (std_hfs) {
1585 FREE(pluskey, M_TEMP);
1586 }
39236c6e
A
1587#endif
1588
9bccf70c 1589 }
9bccf70c 1590exit:
b4c24cb9 1591 (void) BTFlushPath(fcb);
9bccf70c
A
1592 if (from_iterator)
1593 FREE(from_iterator, M_TEMP);
1594 if (to_iterator)
1595 FREE(to_iterator, M_TEMP);
1596 if (recp)
1597 FREE(recp, M_TEMP);
1598 return MacToVFSError(result);
1599}
1600
1601
1602/*
1603 * cat_delete - delete a node from the catalog
1604 *
1605 * Order of B-tree operations:
1606 * 1. BTDeleteRecord(cnode);
1607 * 2. BTDeleteRecord(thread);
1608 * 3. BTUpdateRecord(parent);
1609 */
1610int
1611cat_delete(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp)
1612{
9bccf70c
A
1613 FCB * fcb;
1614 BTreeIterator *iterator;
1615 cnid_t cnid;
1616 int std_hfs;
1617 int result;
1618
2d21ac55
A
1619 fcb = hfsmp->hfs_catalog_cp->c_datafork;
1620 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
9bccf70c
A
1621
1622 /* Preflight check:
1623 *
1624 * The root directory cannot be deleted
1625 * A directory must be empty
1626 * A file must be zero length (no blocks)
1627 */
9bccf70c 1628 if (descp->cd_cnid < kHFSFirstUserCatalogNodeID ||
91447636 1629 descp->cd_parentcnid == kHFSRootParentID)
9bccf70c
A
1630 return (EINVAL);
1631
1632 /* XXX Preflight Missing */
1633
2d21ac55
A
1634 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1635 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
91447636 1636 iterator->hint.nodeNum = 0;
9bccf70c
A
1637
1638 /*
1639 * Derive a key from either the file ID (for a virtual inode)
1640 * or the descriptor.
1641 */
1642 if (descp->cd_namelen == 0) {
91447636
A
1643 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
1644 cnid = attrp->ca_fileid;
9bccf70c
A
1645 } else {
1646 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
1647 cnid = descp->cd_cnid;
1648 }
1649 if (result)
1650 goto exit;
1651
1652 /* Delete record */
1653 result = BTDeleteRecord(fcb, iterator);
91447636
A
1654 if (result) {
1655 if (std_hfs || (result != btNotFound))
1656 goto exit;
1657
1658 struct cat_desc temp_desc;
1659
1660 /* Probably the node has mangled name */
1661 result = cat_lookupmangled(hfsmp, descp, 0, &temp_desc, attrp, NULL);
1662 if (result)
1663 goto exit;
1664
1665 /* The file has mangled name. Delete the file using full name */
1666 bzero(iterator, sizeof(*iterator));
1667 result = buildkey(hfsmp, &temp_desc, (HFSPlusCatalogKey *)&iterator->key, 0);
1668 cnid = temp_desc.cd_cnid;
1669 if (result) {
1670 cat_releasedesc(&temp_desc);
1671 goto exit;
1672 }
1673
1674 result = BTDeleteRecord(fcb, iterator);
1675 if (result) {
1676 cat_releasedesc(&temp_desc);
1677 goto exit;
1678 }
1679
1680 cat_releasedesc(&temp_desc);
1681 }
9bccf70c 1682
2d21ac55 1683 /* Delete thread record. On error, mark volume inconsistent */
9bccf70c 1684 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
2d21ac55
A
1685 if (BTDeleteRecord(fcb, iterator)) {
1686 if (!std_hfs) {
39236c6e 1687 printf ("hfs: cat_delete() failed to delete thread record id=%u on vol=%s\n", cnid, hfsmp->vcbVN);
fe8ab488 1688 hfs_mark_inconsistent(hfsmp, HFS_OP_INCOMPLETE);
2d21ac55
A
1689 }
1690 }
9bccf70c 1691
9bccf70c 1692exit:
b4c24cb9 1693 (void) BTFlushPath(fcb);
9bccf70c
A
1694
1695 return MacToVFSError(result);
1696}
1697
1698
1699/*
6d2010ae
A
1700 * cat_update_internal - update the catalog node described by descp
1701 * using the data from attrp and forkp.
1702 * If update_hardlink is true, the hard link catalog record is updated
1703 * and not the inode catalog record.
9bccf70c 1704 */
6d2010ae
A
1705static int
1706cat_update_internal(struct hfsmount *hfsmp, int update_hardlink, struct cat_desc *descp, struct cat_attr *attrp,
fe8ab488 1707 const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
9bccf70c 1708{
9bccf70c
A
1709 FCB * fcb;
1710 BTreeIterator * iterator;
1711 struct update_state state;
9bccf70c
A
1712 int result;
1713
2d21ac55 1714 fcb = hfsmp->hfs_catalog_cp->c_datafork;
9bccf70c
A
1715
1716 state.s_desc = descp;
1717 state.s_attr = attrp;
1718 state.s_datafork = dataforkp;
1719 state.s_rsrcfork = rsrcforkp;
1720 state.s_hfsmp = hfsmp;
1721
2d21ac55
A
1722 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
1723 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
9bccf70c
A
1724
1725 /*
1726 * For open-deleted files we need to do a lookup by cnid
1727 * (using thread rec).
1728 *
6d2010ae
A
1729 * For hard links and if not requested by caller, the target
1730 * of the update is the inode itself (not the link record)
1731 * so a lookup by fileid (i.e. thread rec) is needed.
9bccf70c 1732 */
6d2010ae
A
1733 if ((update_hardlink == false) &&
1734 ((descp->cd_cnid != attrp->ca_fileid) ||
1735 (descp->cd_namelen == 0) ||
1736 (attrp->ca_recflags & kHFSHasLinkChainMask))) {
9bccf70c 1737 result = getkey(hfsmp, attrp->ca_fileid, (CatalogKey *)&iterator->key);
2d21ac55 1738 } else {
9bccf70c 1739 result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0);
2d21ac55 1740 }
9bccf70c
A
1741 if (result)
1742 goto exit;
1743
1744 /* Pass a node hint */
1745 iterator->hint.nodeNum = descp->cd_hint;
1746
1747 result = BTUpdateRecord(fcb, iterator,
1748 (IterateCallBackProcPtr)catrec_update, &state);
1749 if (result)
1750 goto exit;
1751
1752 /* Update the node hint. */
1753 descp->cd_hint = iterator->hint.nodeNum;
1754
9bccf70c 1755exit:
b4c24cb9 1756 (void) BTFlushPath(fcb);
9bccf70c
A
1757
1758 return MacToVFSError(result);
1759}
1760
6d2010ae
A
1761/*
1762 * cat_update - update the catalog node described by descp
1763 * using the data from attrp and forkp.
1764 */
1765int
1766cat_update(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
fe8ab488 1767 const struct cat_fork *dataforkp, const struct cat_fork *rsrcforkp)
6d2010ae
A
1768{
1769 return cat_update_internal(hfsmp, false, descp, attrp, dataforkp, rsrcforkp);
1770}
1771
9bccf70c
A
1772/*
1773 * catrec_update - Update the fields of a catalog record
1774 * This is called from within BTUpdateRecord.
1775 */
1776static int
91447636 1777catrec_update(const CatalogKey *ckp, CatalogRecord *crp, struct update_state *state)
9bccf70c
A
1778{
1779 struct cat_desc *descp;
1780 struct cat_attr *attrp;
fe8ab488 1781 const struct cat_fork *forkp;
9bccf70c
A
1782 struct hfsmount *hfsmp;
1783 long blksize;
9bccf70c
A
1784
1785 descp = state->s_desc;
1786 attrp = state->s_attr;
1787 hfsmp = state->s_hfsmp;
1788 blksize = HFSTOVCB(hfsmp)->blockSize;
1789
1790 switch (crp->recordType) {
39236c6e
A
1791
1792#if CONFIG_HFS_STD
9bccf70c
A
1793 case kHFSFolderRecord: {
1794 HFSCatalogFolder *dir;
1795
1796 dir = (struct HFSCatalogFolder *)crp;
1797 /* Do a quick sanity check */
1798 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1799 (dir->folderID != descp->cd_cnid))
1800 return (btNotFound);
1801 dir->valence = attrp->ca_entries;
1802 dir->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1803 dir->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1804 dir->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1805 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 16);
1806 bcopy(&attrp->ca_finderinfo[16], &dir->finderInfo, 16);
1807 break;
1808 }
1809 case kHFSFileRecord: {
1810 HFSCatalogFile *file;
39236c6e
A
1811 int i;
1812
9bccf70c
A
1813 file = (struct HFSCatalogFile *)crp;
1814 /* Do a quick sanity check */
1815 if ((ckp->hfs.parentID != descp->cd_parentcnid) ||
1816 (file->fileID != attrp->ca_fileid))
1817 return (btNotFound);
1818 file->createDate = UTCToLocal(to_hfs_time(attrp->ca_itime));
1819 file->modifyDate = UTCToLocal(to_hfs_time(attrp->ca_mtime));
1820 file->backupDate = UTCToLocal(to_hfs_time(attrp->ca_btime));
1821 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 16);
1822 bcopy(&attrp->ca_finderinfo[16], &file->finderInfo, 16);
1823 if (state->s_rsrcfork) {
1824 forkp = state->s_rsrcfork;
1825 file->rsrcLogicalSize = forkp->cf_size;
1826 file->rsrcPhysicalSize = forkp->cf_blocks * blksize;
1827 for (i = 0; i < kHFSExtentDensity; ++i) {
1828 file->rsrcExtents[i].startBlock =
1829 (u_int16_t)forkp->cf_extents[i].startBlock;
1830 file->rsrcExtents[i].blockCount =
1831 (u_int16_t)forkp->cf_extents[i].blockCount;
1832 }
1833 }
1834 if (state->s_datafork) {
1835 forkp = state->s_datafork;
1836 file->dataLogicalSize = forkp->cf_size;
1837 file->dataPhysicalSize = forkp->cf_blocks * blksize;
1838 for (i = 0; i < kHFSExtentDensity; ++i) {
1839 file->dataExtents[i].startBlock =
1840 (u_int16_t)forkp->cf_extents[i].startBlock;
1841 file->dataExtents[i].blockCount =
1842 (u_int16_t)forkp->cf_extents[i].blockCount;
1843 }
1844 }
2d21ac55
A
1845
1846 /* Synchronize the lock state */
1847 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
1848 file->flags |= kHFSFileLockedMask;
1849 else
1850 file->flags &= ~kHFSFileLockedMask;
9bccf70c
A
1851 break;
1852 }
39236c6e
A
1853#endif
1854
9bccf70c
A
1855 case kHFSPlusFolderRecord: {
1856 HFSPlusCatalogFolder *dir;
1857
1858 dir = (struct HFSPlusCatalogFolder *)crp;
1859 /* Do a quick sanity check */
2d21ac55 1860 if (dir->folderID != attrp->ca_fileid) {
39236c6e 1861 printf("hfs: catrec_update: id %d != %d, vol=%s\n", dir->folderID, attrp->ca_fileid, hfsmp->vcbVN);
9bccf70c 1862 return (btNotFound);
2d21ac55 1863 }
91447636 1864 dir->flags = attrp->ca_recflags;
9bccf70c
A
1865 dir->valence = attrp->ca_entries;
1866 dir->createDate = to_hfs_time(attrp->ca_itime);
1867 dir->contentModDate = to_hfs_time(attrp->ca_mtime);
1868 dir->backupDate = to_hfs_time(attrp->ca_btime);
1869 dir->accessDate = to_hfs_time(attrp->ca_atime);
91447636 1870 attrp->ca_atimeondisk = attrp->ca_atime;
9bccf70c 1871 dir->attributeModDate = to_hfs_time(attrp->ca_ctime);
2d21ac55
A
1872 /* Note: directory hardlink inodes don't require a text encoding hint. */
1873 if (ckp->hfsPlus.parentID != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
1874 dir->textEncoding = descp->cd_encoding;
1875 }
1876 dir->folderCount = attrp->ca_dircount;
9bccf70c
A
1877 bcopy(&attrp->ca_finderinfo[0], &dir->userInfo, 32);
1878 /*
1879 * Update the BSD Info if it was already initialized on
1880 * disk or if the runtime values have been modified.
1881 *
1882 * If the BSD info was already initialized, but
1883 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1884 * probably different than what was on disk. We don't want
1885 * to overwrite the on-disk values (so if we turn off
1886 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1887 * This way, we can still change fields like the mode or
1888 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1889 *
1890 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1891 * won't change the uid or gid from their defaults. So, if
1892 * the BSD info wasn't set, and the runtime values are not
1893 * default, then what changed was the mode or flags. We
1894 * have to set the uid and gid to something, so use the
1895 * supplied values (which will be default), which has the
1896 * same effect as creating a new file while
1897 * MNT_UNKNOWNPERMISSIONS is set.
1898 */
1899 if ((dir->bsdInfo.fileMode != 0) ||
1900 (attrp->ca_flags != 0) ||
1901 (attrp->ca_uid != hfsmp->hfs_uid) ||
1902 (attrp->ca_gid != hfsmp->hfs_gid) ||
1903 ((attrp->ca_mode & ALLPERMS) !=
1904 (hfsmp->hfs_dir_mask & ACCESSPERMS))) {
1905 if ((dir->bsdInfo.fileMode == 0) ||
91447636 1906 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
9bccf70c
A
1907 dir->bsdInfo.ownerID = attrp->ca_uid;
1908 dir->bsdInfo.groupID = attrp->ca_gid;
1909 }
1910 dir->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1911 dir->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1912 dir->bsdInfo.fileMode = attrp->ca_mode;
2d21ac55
A
1913 /* A directory hardlink has a link count. */
1914 if (attrp->ca_linkcount > 1 || dir->hl_linkCount > 1) {
1915 dir->hl_linkCount = attrp->ca_linkcount;
1916 }
9bccf70c
A
1917 }
1918 break;
1919 }
1920 case kHFSPlusFileRecord: {
1921 HFSPlusCatalogFile *file;
6d2010ae 1922 int is_dirlink;
9bccf70c
A
1923
1924 file = (struct HFSPlusCatalogFile *)crp;
1925 /* Do a quick sanity check */
1926 if (file->fileID != attrp->ca_fileid)
1927 return (btNotFound);
91447636 1928 file->flags = attrp->ca_recflags;
9bccf70c
A
1929 file->createDate = to_hfs_time(attrp->ca_itime);
1930 file->contentModDate = to_hfs_time(attrp->ca_mtime);
1931 file->backupDate = to_hfs_time(attrp->ca_btime);
1932 file->accessDate = to_hfs_time(attrp->ca_atime);
91447636 1933 attrp->ca_atimeondisk = attrp->ca_atime;
9bccf70c 1934 file->attributeModDate = to_hfs_time(attrp->ca_ctime);
2d21ac55
A
1935 /*
1936 * Note: file hardlink inodes don't require a text encoding
1937 * hint, but they do have a first link value.
1938 */
1939 if (ckp->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
1940 file->hl_firstLinkID = attrp->ca_firstlink;
1941 } else {
1942 file->textEncoding = descp->cd_encoding;
1943 }
9bccf70c
A
1944 bcopy(&attrp->ca_finderinfo[0], &file->userInfo, 32);
1945 /*
1946 * Update the BSD Info if it was already initialized on
1947 * disk or if the runtime values have been modified.
1948 *
1949 * If the BSD info was already initialized, but
1950 * MNT_UNKNOWNPERMISSIONS is set, then the runtime IDs are
1951 * probably different than what was on disk. We don't want
1952 * to overwrite the on-disk values (so if we turn off
1953 * MNT_UNKNOWNPERMISSIONS, the old IDs get used again).
1954 * This way, we can still change fields like the mode or
1955 * dates even when MNT_UNKNOWNPERMISSIONS is set.
1956 *
1957 * Note that if MNT_UNKNOWNPERMISSIONS is set, hfs_chown
1958 * won't change the uid or gid from their defaults. So, if
1959 * the BSD info wasn't set, and the runtime values are not
1960 * default, then what changed was the mode or flags. We
1961 * have to set the uid and gid to something, so use the
1962 * supplied values (which will be default), which has the
1963 * same effect as creating a new file while
1964 * MNT_UNKNOWNPERMISSIONS is set.
6d2010ae
A
1965 *
1966 * Do not modify bsdInfo for directory hard link records.
1967 * They are set during creation and are not modifiable, so just
1968 * leave them alone.
9bccf70c 1969 */
6d2010ae
A
1970 is_dirlink = (file->flags & kHFSHasLinkChainMask) &&
1971 (SWAP_BE32(file->userInfo.fdType) == kHFSAliasType) &&
1972 (SWAP_BE32(file->userInfo.fdCreator) == kHFSAliasCreator);
1973
1974 if (!is_dirlink &&
1975 ((file->bsdInfo.fileMode != 0) ||
1976 (attrp->ca_flags != 0) ||
1977 (attrp->ca_uid != hfsmp->hfs_uid) ||
1978 (attrp->ca_gid != hfsmp->hfs_gid) ||
1979 ((attrp->ca_mode & ALLPERMS) !=
1980 (hfsmp->hfs_file_mask & ACCESSPERMS)))) {
9bccf70c 1981 if ((file->bsdInfo.fileMode == 0) ||
91447636 1982 (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) == 0) {
9bccf70c
A
1983 file->bsdInfo.ownerID = attrp->ca_uid;
1984 file->bsdInfo.groupID = attrp->ca_gid;
1985 }
1986 file->bsdInfo.ownerFlags = attrp->ca_flags & 0x000000FF;
1987 file->bsdInfo.adminFlags = attrp->ca_flags >> 16;
1988 file->bsdInfo.fileMode = attrp->ca_mode;
1989 }
1990 if (state->s_rsrcfork) {
1991 forkp = state->s_rsrcfork;
1992 file->resourceFork.logicalSize = forkp->cf_size;
1993 file->resourceFork.totalBlocks = forkp->cf_blocks;
1994 bcopy(&forkp->cf_extents[0], &file->resourceFork.extents,
1995 sizeof(HFSPlusExtentRecord));
55e303ae
A
1996 /* Push blocks read to disk */
1997 file->resourceFork.clumpSize =
1998 howmany(forkp->cf_bytesread, blksize);
9bccf70c
A
1999 }
2000 if (state->s_datafork) {
2001 forkp = state->s_datafork;
2002 file->dataFork.logicalSize = forkp->cf_size;
2003 file->dataFork.totalBlocks = forkp->cf_blocks;
2004 bcopy(&forkp->cf_extents[0], &file->dataFork.extents,
2005 sizeof(HFSPlusExtentRecord));
55e303ae 2006 /* Push blocks read to disk */
91447636 2007 file->dataFork.clumpSize =
55e303ae 2008 howmany(forkp->cf_bytesread, blksize);
9bccf70c
A
2009 }
2010
2011 if ((file->resourceFork.extents[0].startBlock != 0) &&
2012 (file->resourceFork.extents[0].startBlock ==
2d21ac55 2013 file->dataFork.extents[0].startBlock)) {
b0d623f7 2014 panic("hfs: catrec_update: rsrc fork == data fork");
2d21ac55 2015 }
9bccf70c
A
2016
2017 /* Synchronize the lock state */
2018 if (attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE))
2019 file->flags |= kHFSFileLockedMask;
2020 else
2021 file->flags &= ~kHFSFileLockedMask;
2022
2023 /* Push out special field if necessary */
2d21ac55 2024 if (S_ISBLK(attrp->ca_mode) || S_ISCHR(attrp->ca_mode)) {
9bccf70c 2025 file->bsdInfo.special.rawDevice = attrp->ca_rdev;
6d2010ae
A
2026 }
2027 else {
2028 /*
2029 * Protect against the degenerate case where the descriptor contains the
2030 * raw inode ID in its CNID field. If the HFSPlusCatalogFile record indicates
2031 * the linkcount was greater than 1 (the default value), then it must have become
2032 * a hardlink. In this case, update the linkcount from the cat_attr passed in.
2033 */
2034 if ((descp->cd_cnid != attrp->ca_fileid) || (attrp->ca_linkcount > 1 ) ||
316670eb 2035 (file->hl_linkCount > 1)) {
6d2010ae
A
2036 file->hl_linkCount = attrp->ca_linkcount;
2037 }
2d21ac55 2038 }
9bccf70c
A
2039 break;
2040 }
2041 default:
2042 return (btNotFound);
2043 }
2044 return (0);
2045}
2046
2d21ac55
A
2047/* This function sets kHFSHasChildLinkBit in a directory hierarchy in the
2048 * catalog btree of given cnid by walking up the parent chain till it reaches
2049 * either the root folder, or the private metadata directory for storing
2050 * directory hard links. This function updates the corresponding in-core
2051 * cnode, if any, and the directory record in the catalog btree.
2052 * On success, returns zero. On failure, returns non-zero value.
9bccf70c 2053 */
2d21ac55
A
2054__private_extern__
2055int
2056cat_set_childlinkbit(struct hfsmount *hfsmp, cnid_t cnid)
9bccf70c 2057{
2d21ac55
A
2058 int retval = 0;
2059 int lockflags = 0;
2060 struct cat_desc desc;
2061 struct cat_attr attr;
2062
2063 while ((cnid != kHFSRootFolderID) && (cnid != kHFSRootParentID) &&
2064 (cnid != hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid)) {
2065 /* Update the bit in corresponding cnode, if any, in the hash.
2066 * If the cnode has the bit already set, stop the traversal.
2067 */
b0d623f7 2068 retval = hfs_chash_set_childlinkbit(hfsmp, cnid);
2d21ac55
A
2069 if (retval == 0) {
2070 break;
2071 }
9bccf70c 2072
2d21ac55
A
2073 /* Update the catalog record on disk if either cnode was not
2074 * found in the hash, or if a cnode was found and the cnode
2075 * did not have the bit set previously.
2076 */
2077 retval = hfs_start_transaction(hfsmp);
2078 if (retval) {
2079 break;
2080 }
2081 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_EXCLUSIVE_LOCK);
9bccf70c 2082
2d21ac55 2083 /* Look up our catalog folder record */
db609669 2084 retval = cat_idlookup(hfsmp, cnid, 0, 0, &desc, &attr, NULL);
2d21ac55
A
2085 if (retval) {
2086 hfs_systemfile_unlock(hfsmp, lockflags);
2087 hfs_end_transaction(hfsmp);
2088 break;
9bccf70c 2089 }
9bccf70c 2090
2d21ac55
A
2091 /* Update the bit in the catalog record */
2092 attr.ca_recflags |= kHFSHasChildLinkMask;
2093 retval = cat_update(hfsmp, &desc, &attr, NULL, NULL);
2094 if (retval) {
2095 hfs_systemfile_unlock(hfsmp, lockflags);
2096 hfs_end_transaction(hfsmp);
4a3eedf9 2097 cat_releasedesc(&desc);
2d21ac55 2098 break;
b4c24cb9 2099 }
b4c24cb9 2100
2d21ac55
A
2101 hfs_systemfile_unlock(hfsmp, lockflags);
2102 hfs_end_transaction(hfsmp);
2103
2104 cnid = desc.cd_parentcnid;
4a3eedf9 2105 cat_releasedesc(&desc);
2d21ac55
A
2106 }
2107
2108 return retval;
2109}
2110
2111/* This function traverses the parent directory hierarchy from the given
2112 * directory to one level below root directory and checks if any of its
2113 * ancestors is -
2114 * 1. A directory hard link.
2115 * 2. The 'pointed at' directory.
2116 * If any of these conditions fail or an internal error is encountered
2117 * during look up of the catalog record, this function returns non-zero value.
2118 */
2119__private_extern__
2120int
2121cat_check_link_ancestry(struct hfsmount *hfsmp, cnid_t cnid, cnid_t pointed_at_cnid)
2122{
2123 HFSPlusCatalogKey *keyp;
2124 BTreeIterator *ip;
2125 FSBufferDescriptor btdata;
2126 HFSPlusCatalogFolder folder;
2127 FCB *fcb;
2128 int invalid;
2129 int result;
2130
2131 invalid = 0;
2132 BDINIT(btdata, &folder);
2133 MALLOC(ip, BTreeIterator *, sizeof(*ip), M_TEMP, M_WAITOK);
2134 keyp = (HFSPlusCatalogKey *)&ip->key;
2135 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2136
2137 while (cnid != kHFSRootParentID) {
2138 /* Check if the 'pointed at' directory is an ancestor */
2139 if (pointed_at_cnid == cnid) {
2140 invalid = 1;
2141 break;
2142 }
2143 if ((result = getkey(hfsmp, cnid, (CatalogKey *)keyp))) {
39236c6e 2144 printf("hfs: cat_check_link_ancestry: getkey failed id=%u, vol=%s\n", cnid, hfsmp->vcbVN);
2d21ac55
A
2145 invalid = 1; /* On errors, assume an invalid parent */
2146 break;
2147 }
2148 if ((result = BTSearchRecord(fcb, ip, &btdata, NULL, NULL))) {
39236c6e 2149 printf("hfs: cat_check_link_ancestry: cannot find id=%u, vol=%s\n", cnid, hfsmp->vcbVN);
2d21ac55
A
2150 invalid = 1; /* On errors, assume an invalid parent */
2151 break;
2152 }
2153 /* Check if this ancestor is a directory hard link */
2154 if (folder.flags & kHFSHasLinkChainMask) {
2155 invalid = 1;
2156 break;
b4c24cb9 2157 }
2d21ac55 2158 cnid = keyp->parentID;
9bccf70c 2159 }
2d21ac55
A
2160 FREE(ip, M_TEMP);
2161 return (invalid);
2162}
9bccf70c 2163
9bccf70c 2164
2d21ac55 2165/*
6d2010ae 2166 * update_siblinglinks_callback - update a link's chain
2d21ac55 2167 */
9bccf70c 2168
2d21ac55
A
2169struct linkupdate_state {
2170 cnid_t filelinkid;
2171 cnid_t prevlinkid;
2172 cnid_t nextlinkid;
2173};
9bccf70c 2174
2d21ac55 2175static int
6d2010ae 2176update_siblinglinks_callback(__unused const CatalogKey *ckp, CatalogRecord *crp, struct linkupdate_state *state)
2d21ac55
A
2177{
2178 HFSPlusCatalogFile *file;
9bccf70c 2179
2d21ac55 2180 if (crp->recordType != kHFSPlusFileRecord) {
6d2010ae 2181 printf("hfs: update_siblinglinks_callback: unexpected rec type %d\n", crp->recordType);
2d21ac55
A
2182 return (btNotFound);
2183 }
9bccf70c 2184
2d21ac55
A
2185 file = (struct HFSPlusCatalogFile *)crp;
2186 if (file->flags & kHFSHasLinkChainMask) {
2187 if (state->prevlinkid != HFS_IGNORABLE_LINK) {
2188 file->hl_prevLinkID = state->prevlinkid;
9bccf70c 2189 }
2d21ac55
A
2190 if (state->nextlinkid != HFS_IGNORABLE_LINK) {
2191 file->hl_nextLinkID = state->nextlinkid;
9bccf70c 2192 }
2d21ac55 2193 } else {
6d2010ae 2194 printf("hfs: update_siblinglinks_callback: file %d isn't a chain\n", file->fileID);
9bccf70c 2195 }
2d21ac55 2196 return (0);
9bccf70c
A
2197}
2198
2199/*
6d2010ae 2200 * cat_update_siblinglinks - update a link's chain
9bccf70c
A
2201 */
2202int
6d2010ae 2203cat_update_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t prevlinkid, cnid_t nextlinkid)
9bccf70c 2204{
2d21ac55 2205 FCB * fcb;
9bccf70c 2206 BTreeIterator * iterator;
2d21ac55
A
2207 struct linkupdate_state state;
2208 int result;
9bccf70c 2209
2d21ac55
A
2210 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2211 state.filelinkid = linkfileid;
2212 state.prevlinkid = prevlinkid;
2213 state.nextlinkid = nextlinkid;
9bccf70c 2214
6d2010ae
A
2215 /* Create an iterator for use by us temporarily */
2216 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2217 bzero(iterator, sizeof(*iterator));
316670eb 2218
2d21ac55
A
2219 result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key);
2220 if (result == 0) {
6d2010ae 2221 result = BTUpdateRecord(fcb, iterator, (IterateCallBackProcPtr)update_siblinglinks_callback, &state);
2d21ac55
A
2222 (void) BTFlushPath(fcb);
2223 } else {
39236c6e 2224 printf("hfs: cat_update_siblinglinks: couldn't resolve cnid=%d, vol=%s\n", linkfileid, hfsmp->vcbVN);
2d21ac55 2225 }
316670eb 2226
6d2010ae 2227 FREE (iterator, M_TEMP);
2d21ac55
A
2228 return MacToVFSError(result);
2229}
9bccf70c 2230
2d21ac55
A
2231/*
2232 * cat_lookuplink - lookup a link by it's name
2233 */
2d21ac55
A
2234int
2235cat_lookuplink(struct hfsmount *hfsmp, struct cat_desc *descp, cnid_t *linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
2236{
2237 FCB * fcb;
2238 BTreeIterator * iterator;
2239 struct FSBufferDescriptor btdata;
2240 struct HFSPlusCatalogFile file;
2241 int result;
2242
2243 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2244
6d2010ae
A
2245 /* Create an iterator for use by us temporarily */
2246 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2247 bzero(iterator, sizeof(*iterator));
2d21ac55
A
2248
2249 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
2250 goto exit;
2251 }
2252 BDINIT(btdata, &file);
2253
2254 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2255 goto exit;
2256 }
2257 if (file.recordType != kHFSPlusFileRecord) {
2258 result = ENOENT;
2259 goto exit;
2260 }
2261 *linkfileid = file.fileID;
2262
2263 if (file.flags & kHFSHasLinkChainMask) {
2264 *prevlinkid = file.hl_prevLinkID;
2265 *nextlinkid = file.hl_nextLinkID;
2266 } else {
2267 *prevlinkid = 0;
2268 *nextlinkid = 0;
2269 }
2270exit:
6d2010ae 2271 FREE(iterator, M_TEMP);
2d21ac55
A
2272 return MacToVFSError(result);
2273}
2274
2275
2276/*
6d2010ae 2277 * cat_lookup_siblinglinks - lookup previous and next link ID for link using its cnid
2d21ac55 2278 */
2d21ac55 2279int
6d2010ae 2280cat_lookup_siblinglinks(struct hfsmount *hfsmp, cnid_t linkfileid, cnid_t *prevlinkid, cnid_t *nextlinkid)
2d21ac55
A
2281{
2282 FCB * fcb;
2283 BTreeIterator * iterator;
2284 struct FSBufferDescriptor btdata;
2285 struct HFSPlusCatalogFile file;
2286 int result;
2287
2288 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2289
6d2010ae
A
2290 /* Create an iterator for use by us temporarily */
2291 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2292 bzero(iterator, sizeof(*iterator));
316670eb 2293
2d21ac55 2294 if ((result = getkey(hfsmp, linkfileid, (CatalogKey *)&iterator->key))) {
2d21ac55
A
2295 goto exit;
2296 }
2297 BDINIT(btdata, &file);
2298
2299 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2d21ac55
A
2300 goto exit;
2301 }
2302 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2303 if (file.flags & kHFSHasLinkChainMask) {
2304 cnid_t parent;
2305
2306 parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
2307
6d2010ae 2308 /* directory inodes don't have a chain (its in an EA) */
2d21ac55
A
2309 if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2310 result = ENOLINK; /* signal to caller to get head of list */
2311 } else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
2312 *prevlinkid = 0;
2313 *nextlinkid = file.hl_firstLinkID;
2314 } else {
2315 *prevlinkid = file.hl_prevLinkID;
2316 *nextlinkid = file.hl_nextLinkID;
2317 }
2318 } else {
2319 *prevlinkid = 0;
2320 *nextlinkid = 0;
2321 }
2322exit:
6d2010ae 2323 FREE(iterator, M_TEMP);
2d21ac55
A
2324 return MacToVFSError(result);
2325}
2326
2327
39236c6e
A
2328/*
2329 * cat_lookup_lastlink - find the last sibling link in the chain (no "next" ptr)
2330 */
2331int
2332cat_lookup_lastlink(struct hfsmount *hfsmp, cnid_t linkfileid,
2333 cnid_t *lastlink, struct cat_desc *cdesc)
2334{
2335 FCB * fcb;
2336 BTreeIterator * iterator;
2337 struct FSBufferDescriptor btdata;
2338 struct HFSPlusCatalogFile file;
2339 int result;
2340 int itercount = 0;
2341 int foundlast = 0;
2342 cnid_t currentlink = linkfileid;
2343
2344 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2345
2346 /* Create an iterator for use by us temporarily */
2347 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2348
2349 while ((foundlast == 0) && (itercount < HFS_LINK_MAX )) {
2350 itercount++;
2351 bzero(iterator, sizeof(*iterator));
2352
2353 if ((result = getkey(hfsmp, currentlink, (CatalogKey *)&iterator->key))) {
2354 goto exit;
2355 }
2356 BDINIT(btdata, &file);
2357
2358 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2359 goto exit;
2360 }
2361
2362 /* The prev/next chain is only valid when kHFSHasLinkChainMask is set. */
2363 if (file.flags & kHFSHasLinkChainMask) {
2364 cnid_t parent;
2365
2366 parent = ((HFSPlusCatalogKey *)&iterator->key)->parentID;
2367 /*
2368 * The raw inode for a directory hardlink doesn't have a chain.
2369 * Its link information lives in an EA.
2370 */
2371 if (parent == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2372 /* We don't iterate to find the oldest directory hardlink. */
2373 result = ENOLINK;
2374 goto exit;
2375 }
2376 else if (parent == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) {
2377 /* Raw inode for file hardlink (the base inode) */
2378 currentlink = file.hl_firstLinkID;
2379
2380 /*
2381 * One minor special-casing here is necessary.
2382 * If our ID brought us to the raw hardlink inode, and it does
2383 * not have any siblings, then it's an open-unlinked file, and we
2384 * should not proceed any further.
2385 */
2386 if (currentlink == 0) {
2387 result = ENOLINK;
2388 goto exit;
2389 }
2390 }
2391 else {
2392 /* Otherwise, this item's parent is a legitimate directory in the namespace */
2393 if (file.hl_nextLinkID == 0) {
2394 /* If nextLinkID is 0, then we found the end; no more hardlinks */
2395 foundlast = 1;
2396 *lastlink = currentlink;
2397 /*
2398 * Since we had to construct a catalog key to do this lookup
2399 * we still hold it in-hand. We might as well use it to build
2400 * the descriptor that the caller asked for.
2401 */
2402 builddesc ((HFSPlusCatalogKey*)&iterator->key, currentlink, 0, 0, 0, cdesc);
2403 break;
2404 }
2405
2406 currentlink = file.hl_nextLinkID;
2407 }
2408 }
2409 else {
2410 /* Sorry, can't help you without a link chain */
2411 result = ENOLINK;
2412 goto exit;
2413 }
2414 }
2415exit:
2416 /* If we didn't find what we were looking for, zero out the args */
2417 if (foundlast == 0) {
2418 if (cdesc) {
2419 bzero (cdesc, sizeof(struct cat_desc));
2420 }
2421 if (lastlink) {
2422 *lastlink = 0;
2423 }
2424 }
2425
2426 FREE(iterator, M_TEMP);
2427 return MacToVFSError(result);
2428}
2429
2430
2d21ac55
A
2431/*
2432 * cat_createlink - create a link in the catalog
2433 *
2434 * The following cat_attr fields are expected to be set:
2435 * ca_linkref
2436 * ca_itime
2437 * ca_mode (S_IFREG)
2438 * ca_recflags
2439 * ca_flags
2440 * ca_finderinfo (type and creator)
2441 */
2d21ac55
A
2442int
2443cat_createlink(struct hfsmount *hfsmp, struct cat_desc *descp, struct cat_attr *attrp,
2444 cnid_t nextlinkid, cnid_t *linkfileid)
2445{
2446 FCB * fcb;
2447 struct btobj * bto;
2448 FSBufferDescriptor btdata;
2449 HFSPlusForkData *rsrcforkp;
2450 u_int32_t nextCNID;
2451 u_int32_t datalen;
b0d623f7 2452 u_int32_t encoding;
2d21ac55
A
2453 int thread_inserted = 0;
2454 int alias_allocated = 0;
2455 int result = 0;
ebb1b9f4
A
2456 int std_hfs;
2457
2458 std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
2d21ac55
A
2459
2460 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2461
2462 /*
fe8ab488 2463 * Get the next CNID. Note that we are currently holding catalog lock.
2d21ac55 2464 */
fe8ab488
A
2465 result = cat_acquire_cnid(hfsmp, &nextCNID);
2466 if (result) {
2467 return result;
2d21ac55 2468 }
2d21ac55
A
2469
2470 /* Get space for iterator, key and data */
2471 MALLOC(bto, struct btobj *, sizeof(struct btobj), M_TEMP, M_WAITOK);
2472 bto->iterator.hint.nodeNum = 0;
2473 rsrcforkp = &bto->data.hfsPlusFile.resourceFork;
2474
2475 result = buildkey(hfsmp, descp, &bto->key, 0);
2476 if (result) {
b0d623f7 2477 printf("hfs: cat_createlink: err %d from buildkey\n", result);
2d21ac55
A
2478 goto exit;
2479 }
2480
2481 /* This is our only chance to set the encoding (other than a rename). */
2482 encoding = hfs_pickencoding(bto->key.nodeName.unicode, bto->key.nodeName.length);
2483
fe8ab488
A
2484 /*
2485 * Insert the thread record first.
2486 */
2d21ac55
A
2487 datalen = buildthread((void*)&bto->key, &bto->data, 0, 0);
2488 btdata.bufferAddress = &bto->data;
2489 btdata.itemSize = datalen;
2490 btdata.itemCount = 1;
2d21ac55 2491
fe8ab488
A
2492 buildthreadkey(nextCNID, 0, (CatalogKey *) &bto->iterator.key);
2493 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2494 if (result) {
2d21ac55 2495 goto exit;
2d21ac55 2496 }
fe8ab488 2497 thread_inserted = 1;
2d21ac55
A
2498
2499 /*
2500 * Now insert the link record.
2501 */
2502 buildrecord(attrp, nextCNID, 0, encoding, &bto->data, &datalen);
2503
2504 bto->data.hfsPlusFile.hl_prevLinkID = 0;
2505 bto->data.hfsPlusFile.hl_nextLinkID = nextlinkid;
2506 bto->data.hfsPlusFile.hl_linkReference = attrp->ca_linkref;
2507
2508 /* For directory hard links, create alias in resource fork */
2509 if (descp->cd_flags & CD_ISDIR) {
2510 if ((result = cat_makealias(hfsmp, attrp->ca_linkref, &bto->data.hfsPlusFile))) {
2511 goto exit;
2512 }
2513 alias_allocated = 1;
2514 }
2515 btdata.bufferAddress = &bto->data;
2516 btdata.itemSize = datalen;
2517 btdata.itemCount = 1;
2518
2519 bcopy(&bto->key, &bto->iterator.key, sizeof(bto->key));
2520
2521 result = BTInsertRecord(fcb, &bto->iterator, &btdata, datalen);
2522 if (result) {
2523 if (result == btExists)
2524 result = EEXIST;
2525 goto exit;
2526 }
2527 if (linkfileid != NULL) {
2528 *linkfileid = nextCNID;
2529 }
2530exit:
2531 if (result) {
2532 if (thread_inserted) {
39236c6e 2533 printf("hfs: cat_createlink: BTInsertRecord err=%d, vol=%s\n", MacToVFSError(result), hfsmp->vcbVN);
2d21ac55
A
2534
2535 buildthreadkey(nextCNID, 0, (CatalogKey *)&bto->iterator.key);
2536 if (BTDeleteRecord(fcb, &bto->iterator)) {
fe8ab488
A
2537 printf("hfs: cat_createlink() failed to delete thread record on volume %s\n", hfsmp->vcbVN);
2538 hfs_mark_inconsistent(hfsmp, HFS_ROLLBACK_FAILED);
2d21ac55
A
2539 }
2540 }
2541 if (alias_allocated && rsrcforkp->extents[0].startBlock != 0) {
2542 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock,
0b4c1975 2543 rsrcforkp->extents[0].blockCount, 0);
2d21ac55
A
2544 rsrcforkp->extents[0].startBlock = 0;
2545 rsrcforkp->extents[0].blockCount = 0;
2546 }
2547 }
2548 (void) BTFlushPath(fcb);
2549 FREE(bto, M_TEMP);
2550
2551 return MacToVFSError(result);
2552}
2553
2554/* Directory hard links are visible as aliases on pre-Leopard systems and
2555 * as normal directories on Leopard or later. All directory hard link aliases
2556 * have the same resource fork content except for the three uniquely
2557 * identifying values that are updated in the resource fork data when the alias
2558 * is created. The following array is the constant resource fork data used
2559 * only for creating directory hard link aliases.
2560 */
2561static const char hfs_dirlink_alias_rsrc[] = {
2562 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32,
2563 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2564 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2565 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2566 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2567 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2568 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2571 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2572 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2573 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2574 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2575 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2578 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00,
2579 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2580 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x2b,
2581 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2582 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2583 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2585 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2586 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
2587 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
2588 0x01, 0x00, 0x00, 0x00, 0x01, 0x9e, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00,
2589 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x32, 0x00, 0x00, 0x61, 0x6c, 0x69, 0x73,
2590 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2591};
2592
2593/* Constants for directory hard link alias */
2594enum {
2595 /* Size of resource fork data array for directory hard link alias */
2596 kHFSAliasSize = 0x1d0,
2597
2598 /* Volume type for ejectable devices like disk image */
2599 kHFSAliasVolTypeEjectable = 0x5,
2600
2601 /* Offset for volume create date, in Mac OS local time */
2602 kHFSAliasVolCreateDateOffset = 0x12a,
2603
2604 /* Offset for the type of volume */
2605 kHFSAliasVolTypeOffset = 0x130,
2606
2607 /* Offset for folder ID of the parent directory of the directory inode */
2608 kHFSAliasParentIDOffset = 0x132,
2609
2610 /* Offset for folder ID of the directory inode */
2611 kHFSAliasTargetIDOffset = 0x176,
2612};
2613
2614/* Create and write an alias that points at the directory represented by given
2615 * inode number on the same volume. Directory hard links are visible as
2616 * aliases in pre-Leopard systems and this function creates these aliases.
2617 *
2618 * Note: This code is very specific to creating alias for the purpose
2619 * of directory hard links only, and should not be generalized.
2620 */
2621static int
2622cat_makealias(struct hfsmount *hfsmp, u_int32_t inode_num, struct HFSPlusCatalogFile *crp)
2623{
2624 struct buf *bp;
2625 daddr64_t blkno;
2626 u_int32_t blkcount;
2627 int blksize;
2628 int sectorsize;
2629 int result;
2630 HFSPlusForkData *rsrcforkp;
2631 char *alias;
2632 uint32_t *valptr;
2633
2634 rsrcforkp = &(crp->resourceFork);
2635
2636 blksize = hfsmp->blockSize;
2637 blkcount = howmany(kHFSAliasSize, blksize);
593a1d5f 2638 sectorsize = hfsmp->hfs_logical_block_size;
2d21ac55
A
2639 bzero(rsrcforkp, sizeof(HFSPlusForkData));
2640
2641 /* Allocate some disk space for the alias content. */
0b4c1975
A
2642 result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
2643 HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE,
2d21ac55
A
2644 &rsrcforkp->extents[0].startBlock,
2645 &rsrcforkp->extents[0].blockCount);
39236c6e
A
2646 /* Did it fail with an out of space error? If so, re-try and allow journal flushing. */
2647 if (result == dskFulErr ) {
2648 result = BlockAllocate(hfsmp, 0, blkcount, blkcount,
2649 HFS_ALLOC_FORCECONTIG | HFS_ALLOC_METAZONE | HFS_ALLOC_FLUSHTXN,
2650 &rsrcforkp->extents[0].startBlock,
2651 &rsrcforkp->extents[0].blockCount);
2652 }
2d21ac55
A
2653 if (result) {
2654 rsrcforkp->extents[0].startBlock = 0;
2655 goto exit;
2656 }
2657
2658 /* Acquire a buffer cache block for our block. */
2659 blkno = ((u_int64_t)rsrcforkp->extents[0].startBlock * (u_int64_t)blksize) / sectorsize;
2660 blkno += hfsmp->hfsPlusIOPosOffset / sectorsize;
2661
593a1d5f 2662 bp = buf_getblk(hfsmp->hfs_devvp, blkno, roundup(kHFSAliasSize, hfsmp->hfs_logical_block_size), 0, 0, BLK_META);
2d21ac55
A
2663 if (hfsmp->jnl) {
2664 journal_modify_block_start(hfsmp->jnl, bp);
2665 }
2666
2667 /* Generate alias content */
2668 alias = (char *)buf_dataptr(bp);
2669 bzero(alias, buf_size(bp));
2670 bcopy(hfs_dirlink_alias_rsrc, alias, kHFSAliasSize);
2671
2672 /* Set the volume create date, local time in Mac OS format */
2673 valptr = (uint32_t *)(alias + kHFSAliasVolCreateDateOffset);
2674 *valptr = OSSwapHostToBigInt32(hfsmp->localCreateDate);
2675
2676 /* If the file system is on a virtual device like disk image,
2677 * update the volume type to be ejectable device.
2678 */
2679 if (hfsmp->hfs_flags & HFS_VIRTUAL_DEVICE) {
2680 *(uint16_t *)(alias + kHFSAliasVolTypeOffset) =
2681 OSSwapHostToBigInt16(kHFSAliasVolTypeEjectable);
2682 }
2683
2684 /* Set id of the parent of the target directory */
2685 valptr = (uint32_t *)(alias + kHFSAliasParentIDOffset);
2686 *valptr = OSSwapHostToBigInt32(hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid);
2687
2688 /* Set id of the target directory */
2689 valptr = (uint32_t *)(alias + kHFSAliasTargetIDOffset);
2690 *valptr = OSSwapHostToBigInt32(inode_num);
2691
2692 /* Write alias content to disk. */
2693 if (hfsmp->jnl) {
2694 journal_modify_block_end(hfsmp->jnl, bp, NULL, NULL);
2695 } else if ((result = buf_bwrite(bp))) {
2696 goto exit;
2697 }
2698
2699 /* Finish initializing the fork data. */
2700 rsrcforkp->logicalSize = kHFSAliasSize;
2701 rsrcforkp->totalBlocks = rsrcforkp->extents[0].blockCount;
2702
2703exit:
2704 if (result && rsrcforkp->extents[0].startBlock != 0) {
0b4c1975 2705 (void) BlockDeallocate(hfsmp, rsrcforkp->extents[0].startBlock, rsrcforkp->extents[0].blockCount, 0);
2d21ac55
A
2706 rsrcforkp->extents[0].startBlock = 0;
2707 rsrcforkp->extents[0].blockCount = 0;
2708 rsrcforkp->logicalSize = 0;
2709 rsrcforkp->totalBlocks = 0;
2710 }
2711 return (result);
2712}
2713
2714/*
2715 * cat_deletelink - delete a link from the catalog
2716 */
2d21ac55
A
2717int
2718cat_deletelink(struct hfsmount *hfsmp, struct cat_desc *descp)
2719{
2720 struct HFSPlusCatalogFile file;
2721 struct cat_attr cattr;
2722 uint32_t totalBlocks;
2723 int i;
2724 int result;
2725
2726 bzero(&file, sizeof (file));
2727 bzero(&cattr, sizeof (cattr));
2728 cattr.ca_fileid = descp->cd_cnid;
2729
2730 /* Directory links have alias content to remove. */
2731 if (descp->cd_flags & CD_ISDIR) {
2732 FCB * fcb;
2733 BTreeIterator * iterator;
2734 struct FSBufferDescriptor btdata;
2735
2736 fcb = hfsmp->hfs_catalog_cp->c_datafork;
2737
2738 /* Borrow the btcb iterator since we have an exclusive catalog lock. */
2739 iterator = &((BTreeControlBlockPtr)(fcb->ff_sysfileinfo))->iterator;
2740 iterator->hint.nodeNum = 0;
2741
2742 if ((result = buildkey(hfsmp, descp, (HFSPlusCatalogKey *)&iterator->key, 0))) {
2743 goto exit;
2744 }
2745 BDINIT(btdata, &file);
2746
2747 if ((result = BTSearchRecord(fcb, iterator, &btdata, NULL, NULL))) {
2748 goto exit;
2749 }
2750 }
2751
2752 result = cat_delete(hfsmp, descp, &cattr);
2753
2754 if ((result == 0) &&
2755 (descp->cd_flags & CD_ISDIR) &&
2756 (file.recordType == kHFSPlusFileRecord)) {
2757
2758 totalBlocks = file.resourceFork.totalBlocks;
2759
2760 for (i = 0; (i < 8) && (totalBlocks > 0); i++) {
2761 if ((file.resourceFork.extents[i].blockCount == 0) &&
2762 (file.resourceFork.extents[i].startBlock == 0)) {
2763 break;
2764 }
2765
2766 (void) BlockDeallocate(hfsmp,
2767 file.resourceFork.extents[i].startBlock,
0b4c1975 2768 file.resourceFork.extents[i].blockCount, 0);
2d21ac55
A
2769
2770 totalBlocks -= file.resourceFork.extents[i].blockCount;
2771 file.resourceFork.extents[i].startBlock = 0;
2772 file.resourceFork.extents[i].blockCount = 0;
2773 }
2774 }
2775exit:
2776 return (result);
2777}
2778
2779
2780/*
2781 * Callback to collect directory entries.
2782 * Called with readattr_state for each item in a directory.
2783 */
2784struct readattr_state {
2785 struct hfsmount *hfsmp;
2786 struct cat_entrylist *list;
2787 cnid_t dir_cnid;
2788 int stdhfs;
2789 int error;
fe8ab488 2790 int reached_eof;
2d21ac55
A
2791};
2792
2793static int
2794getentriesattr_callback(const CatalogKey *key, const CatalogRecord *rec,
2795 struct readattr_state *state)
2796{
2797 struct cat_entrylist *list = state->list;
2798 struct hfsmount *hfsmp = state->hfsmp;
2799 struct cat_entry *cep;
2800 cnid_t parentcnid;
2801
2802 if (list->realentries >= list->maxentries)
2803 return (0); /* stop */
2804
2805 parentcnid = state->stdhfs ? key->hfs.parentID : key->hfsPlus.parentID;
2806
2807 switch(rec->recordType) {
2808 case kHFSPlusFolderRecord:
2809 case kHFSPlusFileRecord:
39236c6e 2810#if CONFIG_HFS_STD
2d21ac55
A
2811 case kHFSFolderRecord:
2812 case kHFSFileRecord:
39236c6e 2813#endif
2d21ac55
A
2814 if (parentcnid != state->dir_cnid) {
2815 state->error = ENOENT;
fe8ab488 2816 state->reached_eof = 1;
2d21ac55
A
2817 return (0); /* stop */
2818 }
2819 break;
2820 default:
2821 state->error = ENOENT;
2822 return (0); /* stop */
2823 }
2824
2825 /* Hide the private system directories and journal files */
2826 if (parentcnid == kHFSRootFolderID) {
2827 if (rec->recordType == kHFSPlusFolderRecord) {
2828 if (rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
2829 rec->hfsPlusFolder.folderID == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
2830 list->skipentries++;
2831 return (1); /* continue */
2832 }
2833 }
2834 if ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY))) &&
2835 (rec->recordType == kHFSPlusFileRecord) &&
2836 ((rec->hfsPlusFile.fileID == hfsmp->hfs_jnlfileid) ||
2837 (rec->hfsPlusFile.fileID == hfsmp->hfs_jnlinfoblkid))) {
2838 list->skipentries++;
2839 return (1); /* continue */
2840 }
2841 }
2842
2843 cep = &list->entry[list->realentries++];
39236c6e
A
2844
2845 if (state->stdhfs == 0) {
2846 getbsdattr(hfsmp, (const struct HFSPlusCatalogFile *)rec, &cep->ce_attr);
2847 builddesc((const HFSPlusCatalogKey *)key, getcnid(rec), 0, getencoding(rec),
2848 isadir(rec), &cep->ce_desc);
2d21ac55 2849
39236c6e
A
2850 if (rec->recordType == kHFSPlusFileRecord) {
2851 cep->ce_datasize = rec->hfsPlusFile.dataFork.logicalSize;
2852 cep->ce_datablks = rec->hfsPlusFile.dataFork.totalBlocks;
2853 cep->ce_rsrcsize = rec->hfsPlusFile.resourceFork.logicalSize;
2854 cep->ce_rsrcblks = rec->hfsPlusFile.resourceFork.totalBlocks;
2855
2856 /* Save link reference for later processing. */
2857 if ((SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
2858 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator)) {
2859 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2860 } else if ((rec->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
2861 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
2862 (SWAP_BE32(rec->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator)) {
2863 cep->ce_attr.ca_linkref = rec->hfsPlusFile.bsdInfo.special.iNodeNum;
2864 }
2865 }
2866 }
2867#if CONFIG_HFS_STD
2868 else {
2d21ac55
A
2869 struct HFSPlusCatalogFile cnoderec;
2870 HFSPlusCatalogKey * pluskey;
b0d623f7 2871 u_int32_t encoding;
2d21ac55
A
2872
2873 promoteattr(hfsmp, rec, &cnoderec);
2874 getbsdattr(hfsmp, &cnoderec, &cep->ce_attr);
2875
2876 MALLOC(pluskey, HFSPlusCatalogKey *, sizeof(HFSPlusCatalogKey), M_TEMP, M_WAITOK);
2877 promotekey(hfsmp, (const HFSCatalogKey *)key, pluskey, &encoding);
2878 builddesc(pluskey, getcnid(rec), 0, encoding, isadir(rec), &cep->ce_desc);
2879 FREE(pluskey, M_TEMP);
2880
2881 if (rec->recordType == kHFSFileRecord) {
2882 int blksize = HFSTOVCB(hfsmp)->blockSize;
2883
2884 cep->ce_datasize = rec->hfsFile.dataLogicalSize;
2885 cep->ce_datablks = rec->hfsFile.dataPhysicalSize / blksize;
2886 cep->ce_rsrcsize = rec->hfsFile.rsrcLogicalSize;
2887 cep->ce_rsrcblks = rec->hfsFile.rsrcPhysicalSize / blksize;
2888 }
2d21ac55 2889 }
39236c6e 2890#endif
2d21ac55
A
2891
2892 return (list->realentries < list->maxentries);
2893}
2894
2895/*
2896 * Pack a cat_entrylist buffer with attributes from the catalog
2897 *
2898 * Note: index is zero relative
2899 */
2d21ac55 2900int
fe8ab488 2901cat_getentriesattr(struct hfsmount *hfsmp, directoryhint_t *dirhint, struct cat_entrylist *ce_list, int *reachedeof)
2d21ac55
A
2902{
2903 FCB* fcb;
2904 CatalogKey * key;
2905 BTreeIterator * iterator;
2906 struct readattr_state state;
2907 cnid_t parentcnid;
2908 int i;
2909 int std_hfs;
2910 int index;
2911 int have_key;
2912 int result = 0;
fe8ab488 2913 int reached_eof = 0;
2d21ac55
A
2914
2915 ce_list->realentries = 0;
2916
2917 fcb = GetFileControlBlock(HFSTOVCB(hfsmp)->catalogRefNum);
2918 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
2919 parentcnid = dirhint->dh_desc.cd_parentcnid;
2920
fe8ab488
A
2921 bzero (&state, sizeof(struct readattr_state));
2922
2d21ac55
A
2923 state.hfsmp = hfsmp;
2924 state.list = ce_list;
2925 state.dir_cnid = parentcnid;
2926 state.stdhfs = std_hfs;
2927 state.error = 0;
2928
2929 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
2930 bzero(iterator, sizeof(*iterator));
2931 key = (CatalogKey *)&iterator->key;
2932 have_key = 0;
91447636
A
2933 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
2934 index = dirhint->dh_index + 1;
9bccf70c
A
2935
2936 /*
91447636 2937 * Attempt to build a key from cached filename
9bccf70c 2938 */
91447636
A
2939 if (dirhint->dh_desc.cd_namelen != 0) {
2940 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2941 have_key = 1;
2942 }
2943 }
2944
2945 /*
2946 * If the last entry wasn't cached then position the btree iterator
2947 */
2948 if ((index == 0) || !have_key) {
9bccf70c 2949 /*
91447636
A
2950 * Position the iterator at the directory's thread record.
2951 * (i.e. just before the first entry)
9bccf70c 2952 */
91447636 2953 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
9bccf70c 2954 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
91447636
A
2955 if (result) {
2956 result = MacToVFSError(result);
2957 goto exit;
2958 }
2959
9bccf70c
A
2960 /*
2961 * Iterate until we reach the entry just
2962 * before the one we want to start with.
2963 */
91447636
A
2964 if (index > 0) {
2965 struct position_state ps;
2966
2967 ps.error = 0;
2968 ps.count = 0;
2969 ps.index = index;
2970 ps.parentID = dirhint->dh_desc.cd_parentcnid;
2971 ps.hfsmp = hfsmp;
2972
2973 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2974 (IterateCallBackProcPtr)cat_findposition, &ps);
2975 if (ps.error)
2976 result = ps.error;
2977 else
2978 result = MacToVFSError(result);
fe8ab488 2979
91447636 2980 if (result) {
fe8ab488
A
2981 /*
2982 * Note: the index may now point to EOF if the directory
2983 * was modified in between system calls. We will return
2984 * ENOENT from cat_findposition if this is the case, and
2985 * when we bail out with an error, our caller (hfs_readdirattr_internal)
2986 * will suppress the error and indicate EOF to its caller.
2987 */
91447636
A
2988 result = MacToVFSError(result);
2989 goto exit;
2990 }
9bccf70c
A
2991 }
2992 }
2993
91447636 2994 /* Fill list with entries starting at iterator->key. */
9bccf70c 2995 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
2d21ac55 2996 (IterateCallBackProcPtr)getentriesattr_callback, &state);
9bccf70c 2997
fe8ab488 2998 if (state.error) {
9bccf70c 2999 result = state.error;
fe8ab488
A
3000 reached_eof = state.reached_eof;
3001 }
3002 else if (ce_list->realentries == 0) {
9bccf70c 3003 result = ENOENT;
fe8ab488
A
3004 reached_eof = 1;
3005 }
3006 else {
9bccf70c 3007 result = MacToVFSError(result);
fe8ab488 3008 }
9bccf70c
A
3009
3010 if (std_hfs)
3011 goto exit;
3012
3013 /*
3014 * Resolve any hard links.
3015 */
91447636 3016 for (i = 0; i < (int)ce_list->realentries; ++i) {
9bccf70c
A
3017 struct FndrFileInfo *fip;
3018 struct cat_entry *cep;
3019 struct HFSPlusCatalogFile filerec;
2d21ac55
A
3020 int isdirlink = 0;
3021 int isfilelink = 0;
9bccf70c
A
3022
3023 cep = &ce_list->entry[i];
2d21ac55 3024 if (cep->ce_attr.ca_linkref == 0)
9bccf70c 3025 continue;
2d21ac55 3026
9bccf70c
A
3027 /* Note: Finder info is still in Big Endian */
3028 fip = (struct FndrFileInfo *)&cep->ce_attr.ca_finderinfo;
3029
2d21ac55
A
3030 if (S_ISREG(cep->ce_attr.ca_mode) &&
3031 (SWAP_BE32(fip->fdType) == kHardLinkFileType) &&
3032 (SWAP_BE32(fip->fdCreator) == kHFSPlusCreator)) {
3033 isfilelink = 1;
3034 }
3035 if (S_ISREG(cep->ce_attr.ca_mode) &&
3036 (SWAP_BE32(fip->fdType) == kHFSAliasType) &&
3037 (SWAP_BE32(fip->fdCreator) == kHFSAliasCreator) &&
3038 (cep->ce_attr.ca_recflags & kHFSHasLinkChainMask)) {
3039 isdirlink = 1;
3040 }
3041 if (isfilelink || isdirlink) {
3042 if (cat_resolvelink(hfsmp, cep->ce_attr.ca_linkref, isdirlink, &filerec) != 0)
9bccf70c
A
3043 continue;
3044 /* Repack entry from inode record. */
3045 getbsdattr(hfsmp, &filerec, &cep->ce_attr);
3046 cep->ce_datasize = filerec.dataFork.logicalSize;
3047 cep->ce_datablks = filerec.dataFork.totalBlocks;
3048 cep->ce_rsrcsize = filerec.resourceFork.logicalSize;
3049 cep->ce_rsrcblks = filerec.resourceFork.totalBlocks;
3050 }
3051 }
fe8ab488 3052
9bccf70c
A
3053exit:
3054 FREE(iterator, M_TEMP);
fe8ab488 3055 *reachedeof = reached_eof;
9bccf70c
A
3056 return MacToVFSError(result);
3057}
3058
91447636 3059#define SMALL_DIRENTRY_SIZE (int)(sizeof(struct dirent) - (MAXNAMLEN + 1) + 8)
9bccf70c 3060
91447636
A
3061/*
3062 * Callback to pack directory entries.
3063 * Called with packdirentry_state for each item in a directory.
3064 */
9bccf70c 3065
91447636
A
3066/* Hard link information collected during cat_getdirentries. */
3067struct linkinfo {
b0d623f7 3068 u_int32_t link_ref;
91447636
A
3069 user_addr_t dirent_addr;
3070};
3071typedef struct linkinfo linkinfo_t;
3072
2d21ac55 3073/* State information for the getdirentries_callback function. */
91447636 3074struct packdirentry_state {
316670eb 3075 int cbs_flags; /* VNODE_READDIR_* flags */
91447636
A
3076 u_int32_t cbs_parentID;
3077 u_int32_t cbs_index;
3078 uio_t cbs_uio;
3079 ExtendedVCB * cbs_hfsmp;
3080 int cbs_result;
3081 int32_t cbs_nlinks;
3082 int32_t cbs_maxlinks;
3083 linkinfo_t * cbs_linkinfo;
3084 struct cat_desc * cbs_desc;
2d21ac55
A
3085 u_int8_t * cbs_namebuf;
3086 /*
3087 * The following fields are only used for NFS readdir, which
3088 * uses the next file id as the seek offset of each entry.
3089 */
91447636 3090 struct direntry * cbs_direntry;
3a60a9f5
A
3091 struct direntry * cbs_prevdirentry;
3092 u_int32_t cbs_previlinkref;
3093 Boolean cbs_hasprevdirentry;
3094 Boolean cbs_eof;
55e303ae 3095};
9bccf70c 3096
2d21ac55
A
3097/*
3098 * getdirentries callback for HFS Plus directories.
3099 */
9bccf70c 3100static int
2d21ac55 3101getdirentries_callback(const CatalogKey *ckp, const CatalogRecord *crp,
91447636 3102 struct packdirentry_state *state)
9bccf70c 3103{
55e303ae 3104 struct hfsmount *hfsmp;
2d21ac55 3105 const CatalogName *cnp;
91447636 3106 cnid_t curID;
9bccf70c
A
3107 OSErr result;
3108 struct dirent catent;
91447636 3109 struct direntry * entry = NULL;
55e303ae 3110 time_t itime;
3a60a9f5
A
3111 u_int32_t ilinkref = 0;
3112 u_int32_t curlinkref = 0;
91447636
A
3113 cnid_t cnid;
3114 int hide = 0;
2d21ac55 3115 u_int8_t type = DT_UNKNOWN;
91447636 3116 u_int8_t is_mangled = 0;
b0d623f7 3117 u_int8_t is_link = 0;
2d21ac55 3118 u_int8_t *nameptr;
cf7d32b8 3119 user_addr_t uiobase = USER_ADDR_NULL;
91447636
A
3120 size_t namelen = 0;
3121 size_t maxnamelen;
3122 size_t uiosize = 0;
3123 caddr_t uioaddr;
3a60a9f5 3124 Boolean stop_after_pack = false;
9bccf70c 3125
91447636 3126 hfsmp = state->cbs_hfsmp;
2d21ac55 3127 curID = ckp->hfsPlus.parentID;
9bccf70c
A
3128
3129 /* We're done when parent directory changes */
3130 if (state->cbs_parentID != curID) {
7e4a7d39
A
3131 /*
3132 * If the parent ID is different from curID this means we've hit
3133 * the EOF for the directory. To help future callers, we mark
3134 * the cbs_eof boolean. However, we should only mark the EOF
3135 * boolean if we're about to return from this function.
3136 *
3137 * This is because this callback function does its own uiomove
3138 * to get the data to userspace. If we set the boolean before determining
3139 * whether or not the current entry has enough room to write its
3140 * data to userland, we could fool the callers of this catalog function
3141 * into thinking they've hit EOF earlier than they really would have.
3142 * In that case, we'd know that we have more entries to process and
3143 * send to userland, but we didn't have enough room.
3144 *
3145 * To be safe, we mark cbs_eof here ONLY for the cases where we know we're
3146 * about to return and won't write any new data back
3147 * to userland. In the stop_after_pack case, we'll set this boolean
3148 * regardless, so it's slightly safer to let that logic mark the boolean,
3149 * especially since it's closer to the return of this function.
3150 */
6d2010ae 3151
316670eb 3152 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
2d21ac55
A
3153 /* The last record has not been returned yet, so we
3154 * want to stop after packing the last item
3155 */
3156 if (state->cbs_hasprevdirentry) {
3a60a9f5
A
3157 stop_after_pack = true;
3158 } else {
7e4a7d39 3159 state->cbs_eof = true;
3a60a9f5
A
3160 state->cbs_result = ENOENT;
3161 return (0); /* stop */
3162 }
3163 } else {
7e4a7d39 3164 state->cbs_eof = true;
3a60a9f5
A
3165 state->cbs_result = ENOENT;
3166 return (0); /* stop */
3167 }
9bccf70c
A
3168 }
3169
316670eb 3170 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
91447636 3171 entry = state->cbs_direntry;
2d21ac55 3172 nameptr = (u_int8_t *)&entry->d_name[0];
316670eb
A
3173 if (state->cbs_flags & VNODE_READDIR_NAMEMAX) {
3174 /*
3175 * The NFS server sometimes needs to make filenames fit in
3176 * NAME_MAX bytes (since its client may not be able to
3177 * handle a longer name). In that case, NFS will ask us
3178 * to mangle the name to keep it short enough.
3179 */
39236c6e 3180 maxnamelen = NAME_MAX + 1;
316670eb
A
3181 } else {
3182 maxnamelen = sizeof(entry->d_name);
3183 }
91447636 3184 } else {
2d21ac55 3185 nameptr = (u_int8_t *)&catent.d_name[0];
316670eb 3186 maxnamelen = sizeof(catent.d_name);
91447636
A
3187 }
3188
316670eb 3189 if ((state->cbs_flags & VNODE_READDIR_EXTENDED) && stop_after_pack) {
2d21ac55
A
3190 /* The last item returns a non-zero invalid cookie */
3191 cnid = INT_MAX;
3a60a9f5 3192 } else {
2d21ac55
A
3193 switch(crp->recordType) {
3194 case kHFSPlusFolderRecord:
3195 type = DT_DIR;
3196 cnid = crp->hfsPlusFolder.folderID;
3197 /* Hide our private system directories. */
3198 if (curID == kHFSRootFolderID) {
3199 if (cnid == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid ||
3200 cnid == hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid) {
3a60a9f5
A
3201 hide = 1;
3202 }
2d21ac55
A
3203 }
3204 break;
3205 case kHFSPlusFileRecord:
3206 itime = to_bsd_time(crp->hfsPlusFile.createDate);
3207 type = MODE_TO_DT(crp->hfsPlusFile.bsdInfo.fileMode);
3208 cnid = crp->hfsPlusFile.fileID;
3209 /*
3210 * When a hardlink link is encountered save its link ref.
3211 */
3212 if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHardLinkFileType) &&
3213 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSPlusCreator) &&
3214 ((itime == (time_t)hfsmp->hfs_itime) ||
3215 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
3216 /* If link ref is inode's file id then use it directly. */
3217 if (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) {
3218 cnid = crp->hfsPlusFile.hl_linkReference;
3219 } else {
3220 ilinkref = crp->hfsPlusFile.hl_linkReference;
3a60a9f5 3221 }
b0d623f7 3222 is_link =1;
2d21ac55
A
3223 } else if ((SWAP_BE32(crp->hfsPlusFile.userInfo.fdType) == kHFSAliasType) &&
3224 (SWAP_BE32(crp->hfsPlusFile.userInfo.fdCreator) == kHFSAliasCreator) &&
3225 (crp->hfsPlusFile.flags & kHFSHasLinkChainMask) &&
3226 (crp->hfsPlusFile.hl_linkReference >= kHFSFirstUserCatalogNodeID) &&
3227 ((itime == (time_t)hfsmp->hfs_itime) ||
3228 (itime == (time_t)hfsmp->hfs_metadata_createdate))) {
3229 /* A directory's link resolves to a directory. */
3230 type = DT_DIR;
3231 /* A directory's link ref is always inode's file id. */
3232 cnid = crp->hfsPlusFile.hl_linkReference;
b0d623f7 3233 is_link = 1;
2d21ac55
A
3234 }
3235 /* Hide the journal files */
3236 if ((curID == kHFSRootFolderID) &&
3237 ((hfsmp->jnl || ((HFSTOVCB(hfsmp)->vcbAtrb & kHFSVolumeJournaledMask) && (hfsmp->hfs_flags & HFS_READ_ONLY)))) &&
3238 ((cnid == hfsmp->hfs_jnlfileid) ||
3239 (cnid == hfsmp->hfs_jnlinfoblkid))) {
3240 hide = 1;
3241 }
3242 break;
3243 default:
3244 return (0); /* stop */
3245 };
3246
3247 cnp = (const CatalogName*) &ckp->hfsPlus.nodeName;
3248
3249 namelen = cnp->ustr.length;
3250 /*
3251 * For MacRoman encoded names, assume that its ascii and
3252 * convert it directly in an attempt to avoid the more
3253 * expensive utf8_encodestr conversion.
3254 */
3255 if ((namelen < maxnamelen) && (crp->hfsPlusFile.textEncoding == 0)) {
3256 int i;
3257 u_int16_t ch;
3258 const u_int16_t *chp;
3259
3260 chp = &cnp->ustr.unicode[0];
3261 for (i = 0; i < (int)namelen; ++i) {
3262 ch = *chp++;
3263 if (ch > 0x007f || ch == 0x0000) {
3264 /* Perform expensive utf8_encodestr conversion */
3265 goto encodestr;
3a60a9f5 3266 }
2d21ac55 3267 nameptr[i] = (ch == '/') ? ':' : (u_int8_t)ch;
3a60a9f5 3268 }
2d21ac55
A
3269 nameptr[namelen] = '\0';
3270 result = 0;
3271 } else {
3272encodestr:
3273 result = utf8_encodestr(cnp->ustr.unicode, namelen * sizeof(UniChar),
39236c6e 3274 nameptr, &namelen, maxnamelen, ':', 0);
2d21ac55 3275 }
3a60a9f5 3276
2d21ac55
A
3277 /* Check result returned from encoding the filename to utf8 */
3278 if (result == ENAMETOOLONG) {
b0d623f7
A
3279 /*
3280 * If we were looking at a catalog record for a hardlink (not the inode),
3281 * then we want to use its link ID as opposed to the inode ID for
3282 * a mangled name. For all other cases, they are the same. Note that
3283 * due to the way directory hardlinks are implemented, the actual link
3284 * is going to be counted as a file record, so we can catch both
3285 * with is_link.
3286 */
3287 cnid_t linkid = cnid;
3288 if (is_link) {
3289 linkid = crp->hfsPlusFile.fileID;
3290 }
3291
2d21ac55 3292 result = ConvertUnicodeToUTF8Mangled(cnp->ustr.length * sizeof(UniChar),
39236c6e 3293 cnp->ustr.unicode, maxnamelen,
b0d623f7 3294 (ByteCount*)&namelen, nameptr, linkid);
2d21ac55 3295 is_mangled = 1;
9bccf70c 3296 }
9bccf70c
A
3297 }
3298
316670eb 3299 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
91447636
A
3300 /*
3301 * The index is 1 relative and includes "." and ".."
3302 *
2d21ac55
A
3303 * Also stuff the cnid in the upper 32 bits of the cookie.
3304 * The cookie is stored to the previous entry, which will
3305 * be packed and copied this time
91447636 3306 */
3a60a9f5
A
3307 state->cbs_prevdirentry->d_seekoff = (state->cbs_index + 3) | ((u_int64_t)cnid << 32);
3308 uiosize = state->cbs_prevdirentry->d_reclen;
3309 uioaddr = (caddr_t) state->cbs_prevdirentry;
91447636
A
3310 } else {
3311 catent.d_type = type;
3312 catent.d_namlen = namelen;
3313 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
3314 if (hide)
3315 catent.d_fileno = 0; /* file number = 0 means skip entry */
3316 else
3317 catent.d_fileno = cnid;
3318 uioaddr = (caddr_t) &catent;
b4c24cb9
A
3319 }
3320
91447636 3321 /* Save current base address for post processing of hard-links. */
2d21ac55
A
3322 if (ilinkref || state->cbs_previlinkref) {
3323 uiobase = uio_curriovbase(state->cbs_uio);
3324 }
91447636 3325 /* If this entry won't fit then we're done */
b0d623f7 3326 if ((uiosize > (user_size_t)uio_resid(state->cbs_uio)) ||
91447636 3327 (ilinkref != 0 && state->cbs_nlinks == state->cbs_maxlinks)) {
9bccf70c 3328 return (0); /* stop */
91447636 3329 }
9bccf70c 3330
316670eb 3331 if (!(state->cbs_flags & VNODE_READDIR_EXTENDED) || state->cbs_hasprevdirentry) {
3a60a9f5
A
3332 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
3333 if (state->cbs_result == 0) {
3334 ++state->cbs_index;
9bccf70c 3335
3a60a9f5
A
3336 /* Remember previous entry */
3337 state->cbs_desc->cd_cnid = cnid;
3338 if (type == DT_DIR) {
3339 state->cbs_desc->cd_flags |= CD_ISDIR;
3340 } else {
3341 state->cbs_desc->cd_flags &= ~CD_ISDIR;
3342 }
3343 if (state->cbs_desc->cd_nameptr != NULL) {
2d21ac55 3344 state->cbs_desc->cd_namelen = 0;
3a60a9f5 3345 }
91447636 3346#if 0
3a60a9f5 3347 state->cbs_desc->cd_encoding = xxxx;
91447636 3348#endif
3a60a9f5
A
3349 if (!is_mangled) {
3350 state->cbs_desc->cd_namelen = namelen;
2d21ac55 3351 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
3a60a9f5
A
3352 } else {
3353 /* Store unmangled name for the directory hint else it will
3354 * restart readdir at the last location again
3355 */
2d21ac55 3356 u_int8_t *new_nameptr;
3a60a9f5
A
3357 size_t bufsize;
3358 size_t tmp_namelen = 0;
91447636 3359
2d21ac55 3360 cnp = (const CatalogName *)&ckp->hfsPlus.nodeName;
3a60a9f5 3361 bufsize = 1 + utf8_encodelen(cnp->ustr.unicode,
2d21ac55
A
3362 cnp->ustr.length * sizeof(UniChar),
3363 ':', 0);
3364 MALLOC(new_nameptr, u_int8_t *, bufsize, M_TEMP, M_WAITOK);
3a60a9f5 3365 result = utf8_encodestr(cnp->ustr.unicode,
2d21ac55
A
3366 cnp->ustr.length * sizeof(UniChar),
3367 new_nameptr, &tmp_namelen, bufsize, ':', 0);
91447636 3368
3a60a9f5 3369 state->cbs_desc->cd_namelen = tmp_namelen;
2d21ac55 3370 bcopy(new_nameptr, state->cbs_namebuf, tmp_namelen + 1);
91447636 3371
3a60a9f5
A
3372 FREE(new_nameptr, M_TEMP);
3373 }
3374 }
3375 if (state->cbs_hasprevdirentry) {
3376 curlinkref = ilinkref; /* save current */
3377 ilinkref = state->cbs_previlinkref; /* use previous */
3378 }
3379 /*
3380 * Record any hard links for post processing.
3381 */
3382 if ((ilinkref != 0) &&
3383 (state->cbs_result == 0) &&
3384 (state->cbs_nlinks < state->cbs_maxlinks)) {
3385 state->cbs_linkinfo[state->cbs_nlinks].dirent_addr = uiobase;
3386 state->cbs_linkinfo[state->cbs_nlinks].link_ref = ilinkref;
3387 state->cbs_nlinks++;
3388 }
3389 if (state->cbs_hasprevdirentry) {
3390 ilinkref = curlinkref; /* restore current */
3391 }
91447636 3392 }
3a60a9f5 3393
2d21ac55 3394 /* Fill the direntry to be used the next time */
316670eb 3395 if (state->cbs_flags & VNODE_READDIR_EXTENDED) {
3a60a9f5
A
3396 if (stop_after_pack) {
3397 state->cbs_eof = true;
3398 return (0); /* stop */
3399 }
3400 entry->d_type = type;
3401 entry->d_namlen = namelen;
3402 entry->d_reclen = EXT_DIRENT_LEN(namelen);
2d21ac55
A
3403 if (hide) {
3404 /* File number = 0 means skip entry */
3405 entry->d_fileno = 0;
3406 } else {
3a60a9f5 3407 entry->d_fileno = cnid;
2d21ac55 3408 }
3a60a9f5
A
3409 /* swap the current and previous entry */
3410 struct direntry * tmp;
3411 tmp = state->cbs_direntry;
3412 state->cbs_direntry = state->cbs_prevdirentry;
3413 state->cbs_prevdirentry = tmp;
3414 state->cbs_hasprevdirentry = true;
3415 state->cbs_previlinkref = ilinkref;
55e303ae
A
3416 }
3417
91447636 3418 /* Continue iteration if there's room */
9bccf70c 3419 return (state->cbs_result == 0 &&
91447636 3420 uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
9bccf70c
A
3421}
3422
39236c6e 3423#if CONFIG_HFS_STD
2d21ac55
A
3424/*
3425 * getdirentries callback for standard HFS (non HFS+) directories.
3426 */
3427static int
3428getdirentries_std_callback(const CatalogKey *ckp, const CatalogRecord *crp,
3429 struct packdirentry_state *state)
3430{
3431 struct hfsmount *hfsmp;
3432 const CatalogName *cnp;
3433 cnid_t curID;
3434 OSErr result;
3435 struct dirent catent;
3436 cnid_t cnid;
3437 u_int8_t type = DT_UNKNOWN;
3438 u_int8_t *nameptr;
3439 size_t namelen = 0;
3440 size_t maxnamelen;
3441 size_t uiosize = 0;
3442 caddr_t uioaddr;
3443
3444 hfsmp = state->cbs_hfsmp;
3445
3446 curID = ckp->hfs.parentID;
3447
3448 /* We're done when parent directory changes */
3449 if (state->cbs_parentID != curID) {
3450 state->cbs_result = ENOENT;
3451 return (0); /* stop */
3452 }
3453
3454 nameptr = (u_int8_t *)&catent.d_name[0];
39236c6e 3455 maxnamelen = sizeof(catent.d_name);
2d21ac55
A
3456
3457 switch(crp->recordType) {
3458 case kHFSFolderRecord:
3459 type = DT_DIR;
3460 cnid = crp->hfsFolder.folderID;
3461 break;
3462 case kHFSFileRecord:
3463 type = DT_REG;
3464 cnid = crp->hfsFile.fileID;
3465 break;
3466 default:
3467 return (0); /* stop */
3468 };
3469
3470 cnp = (const CatalogName*) ckp->hfs.nodeName;
39236c6e 3471 result = hfs_to_utf8(hfsmp, cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr);
2d21ac55
A
3472 /*
3473 * When an HFS name cannot be encoded with the current
3474 * volume encoding we use MacRoman as a fallback.
3475 */
3476 if (result) {
39236c6e 3477 result = mac_roman_to_utf8(cnp->pstr, maxnamelen, (ByteCount *)&namelen, nameptr);
2d21ac55
A
3478 }
3479 catent.d_type = type;
3480 catent.d_namlen = namelen;
3481 catent.d_reclen = uiosize = STD_DIRENT_LEN(namelen);
3482 catent.d_fileno = cnid;
3483 uioaddr = (caddr_t) &catent;
3484
3485 /* If this entry won't fit then we're done */
b0d623f7 3486 if (uiosize > (user_size_t)uio_resid(state->cbs_uio)) {
2d21ac55
A
3487 return (0); /* stop */
3488 }
3489
3490 state->cbs_result = uiomove(uioaddr, uiosize, state->cbs_uio);
3491 if (state->cbs_result == 0) {
3492 ++state->cbs_index;
3493
3494 /* Remember previous entry */
3495 state->cbs_desc->cd_cnid = cnid;
3496 if (type == DT_DIR) {
3497 state->cbs_desc->cd_flags |= CD_ISDIR;
3498 } else {
3499 state->cbs_desc->cd_flags &= ~CD_ISDIR;
3500 }
3501 if (state->cbs_desc->cd_nameptr != NULL) {
3502 state->cbs_desc->cd_namelen = 0;
3503 }
3504 state->cbs_desc->cd_namelen = namelen;
3505 bcopy(nameptr, state->cbs_namebuf, namelen + 1);
3506 }
3507
3508 /* Continue iteration if there's room */
3509 return (state->cbs_result == 0 && uio_resid(state->cbs_uio) >= SMALL_DIRENTRY_SIZE);
3510}
39236c6e 3511#endif
91447636 3512
9bccf70c 3513/*
91447636 3514 * Pack a uio buffer with directory entries from the catalog
9bccf70c
A
3515 */
3516int
316670eb
A
3517cat_getdirentries(struct hfsmount *hfsmp, u_int32_t entrycnt, directoryhint_t *dirhint,
3518 uio_t uio, int flags, int * items, int * eofflag)
9bccf70c 3519{
91447636 3520 FCB* fcb;
9bccf70c 3521 BTreeIterator * iterator;
91447636
A
3522 CatalogKey * key;
3523 struct packdirentry_state state;
55e303ae
A
3524 void * buffer;
3525 int bufsize;
91447636 3526 int maxlinks;
9bccf70c 3527 int result;
91447636
A
3528 int index;
3529 int have_key;
316670eb
A
3530 int extended;
3531
3532 extended = flags & VNODE_READDIR_EXTENDED;
3533
2d21ac55
A
3534 if (extended && (hfsmp->hfs_flags & HFS_STANDARD)) {
3535 return (ENOTSUP);
3536 }
3537 fcb = hfsmp->hfs_catalog_cp->c_datafork;
9bccf70c 3538
3a60a9f5
A
3539 /*
3540 * Get a buffer for link info array, btree iterator and a direntry:
3541 */
316670eb 3542 maxlinks = MIN(entrycnt, (u_int32_t)(uio_resid(uio) / SMALL_DIRENTRY_SIZE));
2d21ac55 3543 bufsize = MAXPATHLEN + (maxlinks * sizeof(linkinfo_t)) + sizeof(*iterator);
91447636 3544 if (extended) {
3a60a9f5 3545 bufsize += 2*sizeof(struct direntry);
91447636 3546 }
55e303ae
A
3547 MALLOC(buffer, void *, bufsize, M_TEMP, M_WAITOK);
3548 bzero(buffer, bufsize);
3549
316670eb 3550 state.cbs_flags = flags;
3a60a9f5
A
3551 state.cbs_hasprevdirentry = false;
3552 state.cbs_previlinkref = 0;
55e303ae 3553 state.cbs_nlinks = 0;
91447636 3554 state.cbs_maxlinks = maxlinks;
2d21ac55 3555 state.cbs_linkinfo = (linkinfo_t *)((char *)buffer + MAXPATHLEN);
7e4a7d39
A
3556 /*
3557 * We need to set cbs_eof to false regardless of whether or not the
3558 * control flow is actually in the extended case, since we use this
3559 * field to track whether or not we've returned EOF from the iterator function.
3560 */
3561 state.cbs_eof = false;
6d2010ae 3562
2d21ac55 3563 iterator = (BTreeIterator *) ((char *)state.cbs_linkinfo + (maxlinks * sizeof(linkinfo_t)));
91447636
A
3564 key = (CatalogKey *)&iterator->key;
3565 have_key = 0;
3566 index = dirhint->dh_index + 1;
3567 if (extended) {
3a60a9f5
A
3568 state.cbs_direntry = (struct direntry *)((char *)iterator + sizeof(BTreeIterator));
3569 state.cbs_prevdirentry = state.cbs_direntry + 1;
91447636
A
3570 }
3571 /*
3572 * Attempt to build a key from cached filename
3573 */
3574 if (dirhint->dh_desc.cd_namelen != 0) {
3575 if (buildkey(hfsmp, &dirhint->dh_desc, (HFSPlusCatalogKey *)key, 0) == 0) {
2d21ac55 3576 iterator->hint.nodeNum = dirhint->dh_desc.cd_hint;
91447636
A
3577 have_key = 1;
3578 }
3579 }
9bccf70c 3580
2d21ac55
A
3581 if (index == 0 && dirhint->dh_threadhint != 0) {
3582 /*
3583 * Position the iterator at the directory's thread record.
3584 * (i.e. just before the first entry)
3585 */
3586 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3587 iterator->hint.nodeNum = dirhint->dh_threadhint;
3588 iterator->hint.index = 0;
3589 have_key = 1;
3590 }
3591
91447636
A
3592 /*
3593 * If the last entry wasn't cached then position the btree iterator
3594 */
2d21ac55 3595 if (!have_key) {
91447636
A
3596 /*
3597 * Position the iterator at the directory's thread record.
3598 * (i.e. just before the first entry)
3599 */
3600 buildthreadkey(dirhint->dh_desc.cd_parentcnid, (hfsmp->hfs_flags & HFS_STANDARD), key);
3601 result = BTSearchRecord(fcb, iterator, NULL, NULL, iterator);
3602 if (result) {
3603 result = MacToVFSError(result);
3604 goto cleanup;
3605 }
2d21ac55
A
3606 if (index == 0) {
3607 dirhint->dh_threadhint = iterator->hint.nodeNum;
3608 }
91447636
A
3609 /*
3610 * Iterate until we reach the entry just
3611 * before the one we want to start with.
3612 */
3613 if (index > 0) {
3614 struct position_state ps;
3615
3616 ps.error = 0;
3617 ps.count = 0;
3618 ps.index = index;
3619 ps.parentID = dirhint->dh_desc.cd_parentcnid;
3620 ps.hfsmp = hfsmp;
3621
3622 result = BTIterateRecords(fcb, kBTreeNextRecord, iterator,
3623 (IterateCallBackProcPtr)cat_findposition, &ps);
3624 if (ps.error)
3625 result = ps.error;
3626 else
3627 result = MacToVFSError(result);
3628 if (result) {
3629 result = MacToVFSError(result);
fe8ab488
A
3630 if (result == ENOENT) {
3631 /*
3632 * ENOENT means we've hit the EOF.
3633 * suppress the error, and set the eof flag.
3634 */
3635 result = 0;
3636 dirhint->dh_desc.cd_flags |= CD_EOF;
3637 *eofflag = 1;
3638 }
91447636
A
3639 goto cleanup;
3640 }
3641 }
b4c24cb9
A
3642 }
3643
91447636
A
3644 state.cbs_index = index;
3645 state.cbs_hfsmp = hfsmp;
9bccf70c 3646 state.cbs_uio = uio;
91447636 3647 state.cbs_desc = &dirhint->dh_desc;
2d21ac55 3648 state.cbs_namebuf = (u_int8_t *)buffer;
9bccf70c 3649 state.cbs_result = 0;
91447636 3650 state.cbs_parentID = dirhint->dh_desc.cd_parentcnid;
9bccf70c 3651
2d21ac55
A
3652 /* Use a temporary buffer to hold intermediate descriptor names. */
3653 if (dirhint->dh_desc.cd_namelen > 0 && dirhint->dh_desc.cd_nameptr != NULL) {
3654 bcopy(dirhint->dh_desc.cd_nameptr, buffer, dirhint->dh_desc.cd_namelen+1);
3655 if (dirhint->dh_desc.cd_flags & CD_HASBUF) {
3656 dirhint->dh_desc.cd_flags &= ~CD_HASBUF;
3657 vfs_removename((const char *)dirhint->dh_desc.cd_nameptr);
3658 }
3659 }
3660 dirhint->dh_desc.cd_nameptr = (u_int8_t *)buffer;
3661
3a60a9f5
A
3662 enum BTreeIterationOperations op;
3663 if (extended && index != 0 && have_key)
3664 op = kBTreeCurrentRecord;
3665 else
3666 op = kBTreeNextRecord;
3667
91447636
A
3668 /*
3669 * Process as many entries as possible starting at iterator->key.
3670 */
39236c6e
A
3671 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0) {
3672 /* HFS+ */
2d21ac55
A
3673 result = BTIterateRecords(fcb, op, iterator,
3674 (IterateCallBackProcPtr)getdirentries_callback, &state);
3675
3676 /* For extended calls, every call to getdirentries_callback()
3677 * transfers the previous directory entry found to the user
3678 * buffer. Therefore when BTIterateRecords reaches the end of
3679 * Catalog BTree, call getdirentries_callback() again with
3680 * dummy values to copy the last directory entry stored in
3681 * packdirentry_state
3682 */
316670eb 3683 if (extended && (result == fsBTRecordNotFoundErr)) {
2d21ac55
A
3684 CatalogKey ckp;
3685 CatalogRecord crp;
8f6c56a5 3686
2d21ac55
A
3687 bzero(&ckp, sizeof(ckp));
3688 bzero(&crp, sizeof(crp));
8f6c56a5 3689
2d21ac55
A
3690 result = getdirentries_callback(&ckp, &crp, &state);
3691 }
8f6c56a5 3692 }
39236c6e
A
3693#if CONFIG_HFS_STD
3694 else {
3695 /* HFS (standard) */
3696 result = BTIterateRecords(fcb, op, iterator,
3697 (IterateCallBackProcPtr)getdirentries_std_callback, &state);
3698 }
3699#endif
8f6c56a5 3700
91447636
A
3701 /* Note that state.cbs_index is still valid on errors */
3702 *items = state.cbs_index - index;
3703 index = state.cbs_index;
6d2010ae 3704
7e4a7d39
A
3705 /*
3706 * Also note that cbs_eof is set in all cases if we ever hit EOF
3707 * during the enumeration by the catalog callback. Mark the directory's hint
3708 * descriptor as having hit EOF.
3709 */
6d2010ae 3710
3a60a9f5 3711 if (state.cbs_eof) {
7e4a7d39 3712 dirhint->dh_desc.cd_flags |= CD_EOF;
3a60a9f5
A
3713 *eofflag = 1;
3714 }
91447636
A
3715
3716 /* Finish updating the catalog iterator. */
3717 dirhint->dh_desc.cd_hint = iterator->hint.nodeNum;
3718 dirhint->dh_desc.cd_flags |= CD_DECOMPOSED;
3719 dirhint->dh_index = index - 1;
3720
2d21ac55
A
3721 /* Fix up the name. */
3722 if (dirhint->dh_desc.cd_namelen > 0) {
3723 dirhint->dh_desc.cd_nameptr = (const u_int8_t *)vfs_addname((char *)buffer, dirhint->dh_desc.cd_namelen, 0, 0);
3724 dirhint->dh_desc.cd_flags |= CD_HASBUF;
3725 } else {
3726 dirhint->dh_desc.cd_nameptr = NULL;
3727 dirhint->dh_desc.cd_namelen = 0;
3728 }
3729
55e303ae
A
3730 /*
3731 * Post process any hard links to get the real file id.
3732 */
3733 if (state.cbs_nlinks > 0) {
b0d623f7 3734 ino_t fileid = 0;
91447636 3735 user_addr_t address;
55e303ae 3736 int i;
55e303ae
A
3737
3738 for (i = 0; i < state.cbs_nlinks; ++i) {
55e303ae
A
3739 if (resolvelinkid(hfsmp, state.cbs_linkinfo[i].link_ref, &fileid) != 0)
3740 continue;
91447636
A
3741 /* This assumes that d_ino is always first field. */
3742 address = state.cbs_linkinfo[i].dirent_addr;
3743 if (address == (user_addr_t)0)
3744 continue;
3745 if (uio_isuserspace(uio)) {
2d21ac55
A
3746 if (extended) {
3747 ino64_t fileid_64 = (ino64_t)fileid;
3748 (void) copyout(&fileid_64, address, sizeof(fileid_64));
3749 } else {
3750 (void) copyout(&fileid, address, sizeof(fileid));
3751 }
91447636 3752 } else /* system space */ {
2d21ac55
A
3753 if (extended) {
3754 ino64_t fileid_64 = (ino64_t)fileid;
3755 bcopy(&fileid_64, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid_64));
3756 } else {
3757 bcopy(&fileid, (void*) CAST_DOWN(caddr_t, address), sizeof(fileid));
3758 }
91447636 3759 }
55e303ae
A
3760 }
3761 }
91447636 3762
9bccf70c
A
3763 if (state.cbs_result)
3764 result = state.cbs_result;
3765 else
3766 result = MacToVFSError(result);
3767
3768 if (result == ENOENT) {
9bccf70c
A
3769 result = 0;
3770 }
3771
9bccf70c 3772cleanup:
55e303ae 3773 FREE(buffer, M_TEMP);
9bccf70c
A
3774
3775 return (result);
3776}
3777
3778
91447636
A
3779/*
3780 * Callback to establish directory position.
3781 * Called with position_state for each item in a directory.
3782 */
3783static int
3784cat_findposition(const CatalogKey *ckp, const CatalogRecord *crp,
3785 struct position_state *state)
3786{
39236c6e 3787 cnid_t curID = 0;
91447636 3788
39236c6e 3789 if ((state->hfsmp->hfs_flags & HFS_STANDARD) == 0) {
91447636 3790 curID = ckp->hfsPlus.parentID;
39236c6e
A
3791 }
3792#if CONFIG_HFS_STD
3793 else {
3794 curID = ckp->hfs.parentID;
3795 }
3796#endif
91447636
A
3797
3798 /* Make sure parent directory didn't change */
3799 if (state->parentID != curID) {
fe8ab488
A
3800 /*
3801 * The parent ID is different from curID this means we've hit
3802 * the EOF for the directory.
3803 */
3804 state->error = ENOENT;
91447636
A
3805 return (0); /* stop */
3806 }
3807
3808 /* Count this entry */
3809 switch(crp->recordType) {
3810 case kHFSPlusFolderRecord:
3811 case kHFSPlusFileRecord:
39236c6e 3812#if CONFIG_HFS_STD
91447636
A
3813 case kHFSFolderRecord:
3814 case kHFSFileRecord:
39236c6e 3815#endif
91447636
A
3816 ++state->count;
3817 break;
3818 default:
b0d623f7 3819 printf("hfs: cat_findposition: invalid record type %d in dir %d\n",
91447636
A
3820 crp->recordType, curID);
3821 state->error = EINVAL;
3822 return (0); /* stop */
3823 };
3824
3825 return (state->count < state->index);
3826}
3827
3828
55e303ae
A
3829/*
3830 * cat_binarykeycompare - compare two HFS Plus catalog keys.
3831
91447636 3832 * The name portion of the key is compared using a 16-bit binary comparison.
55e303ae
A
3833 * This is called from the b-tree code.
3834 */
55e303ae
A
3835int
3836cat_binarykeycompare(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3837{
3838 u_int32_t searchParentID, trialParentID;
3839 int result;
3840
3841 searchParentID = searchKey->parentID;
3842 trialParentID = trialKey->parentID;
3843 result = 0;
3844
3845 if (searchParentID > trialParentID) {
3846 ++result;
3847 } else if (searchParentID < trialParentID) {
3848 --result;
3849 } else {
3850 u_int16_t * str1 = &searchKey->nodeName.unicode[0];
3851 u_int16_t * str2 = &trialKey->nodeName.unicode[0];
3852 int length1 = searchKey->nodeName.length;
3853 int length2 = trialKey->nodeName.length;
39236c6e
A
3854
3855 result = UnicodeBinaryCompare (str1, length1, str2, length2);
55e303ae
A
3856 }
3857
3858 return result;
3859}
3860
3861
39236c6e 3862#if CONFIG_HFS_STD
91447636
A
3863/*
3864 * Compare two standard HFS catalog keys
3865 *
3866 * Result: +n search key > trial key
3867 * 0 search key = trial key
3868 * -n search key < trial key
3869 */
3870int
3871CompareCatalogKeys(HFSCatalogKey *searchKey, HFSCatalogKey *trialKey)
3872{
3873 cnid_t searchParentID, trialParentID;
3874 int result;
3875
3876 searchParentID = searchKey->parentID;
3877 trialParentID = trialKey->parentID;
3878
3879 if (searchParentID > trialParentID)
3880 result = 1;
3881 else if (searchParentID < trialParentID)
3882 result = -1;
3883 else /* parent dirID's are equal, compare names */
3884 result = FastRelString(searchKey->nodeName, trialKey->nodeName);
3885
3886 return result;
3887}
39236c6e 3888#endif
91447636
A
3889
3890
3891/*
3892 * Compare two HFS+ catalog keys
3893 *
3894 * Result: +n search key > trial key
3895 * 0 search key = trial key
3896 * -n search key < trial key
3897 */
3898int
3899CompareExtendedCatalogKeys(HFSPlusCatalogKey *searchKey, HFSPlusCatalogKey *trialKey)
3900{
3901 cnid_t searchParentID, trialParentID;
3902 int result;
3903
3904 searchParentID = searchKey->parentID;
3905 trialParentID = trialKey->parentID;
3906
3907 if (searchParentID > trialParentID) {
3908 result = 1;
3909 }
3910 else if (searchParentID < trialParentID) {
3911 result = -1;
3912 } else {
3913 /* parent node ID's are equal, compare names */
3914 if ( searchKey->nodeName.length == 0 || trialKey->nodeName.length == 0 )
3915 result = searchKey->nodeName.length - trialKey->nodeName.length;
3916 else
3917 result = FastUnicodeCompare(&searchKey->nodeName.unicode[0],
3918 searchKey->nodeName.length,
3919 &trialKey->nodeName.unicode[0],
3920 trialKey->nodeName.length);
3921 }
3922
3923 return result;
3924}
3925
3926
9bccf70c
A
3927/*
3928 * buildkey - build a Catalog b-tree key from a cnode descriptor
3929 */
3930static int
3931buildkey(struct hfsmount *hfsmp, struct cat_desc *descp,
3932 HFSPlusCatalogKey *key, int retry)
3933{
39236c6e 3934 int std_hfs = (hfsmp->hfs_flags & HFS_STANDARD);
2d21ac55 3935 int utf8_flags = UTF_ESCAPE_ILLEGAL;
9bccf70c
A
3936 int result = 0;
3937 size_t unicodeBytes = 0;
39236c6e
A
3938
3939 if (std_hfs == 0) {
3940 retry = 0;
3941 }
9bccf70c
A
3942
3943 if (descp->cd_namelen == 0 || descp->cd_nameptr[0] == '\0')
3944 return (EINVAL); /* invalid name */
3945
3946 key->parentID = descp->cd_parentcnid;
3947 key->nodeName.length = 0;
3948 /*
3949 * Convert filename from UTF-8 into Unicode
3950 */
3951
3952 if ((descp->cd_flags & CD_DECOMPOSED) == 0)
3953 utf8_flags |= UTF_DECOMPOSED;
3954 result = utf8_decodestr(descp->cd_nameptr, descp->cd_namelen,
3955 key->nodeName.unicode, &unicodeBytes,
3956 sizeof(key->nodeName.unicode), ':', utf8_flags);
3957 key->nodeName.length = unicodeBytes / sizeof(UniChar);
3958 key->keyLength = kHFSPlusCatalogKeyMinimumLength + unicodeBytes;
3959 if (result) {
3960 if (result != ENAMETOOLONG)
3961 result = EINVAL; /* name has invalid characters */
3962 return (result);
3963 }
3964
39236c6e 3965#if CONFIG_HFS_STD
9bccf70c
A
3966 /*
3967 * For HFS volumes convert to an HFS compatible key
3968 *
3969 * XXX need to save the encoding that succeeded
3970 */
39236c6e 3971 if (std_hfs) {
9bccf70c
A
3972 HFSCatalogKey hfskey;
3973
3974 bzero(&hfskey, sizeof(hfskey));
3975 hfskey.keyLength = kHFSCatalogKeyMinimumLength;
3976 hfskey.parentID = key->parentID;
3977 hfskey.nodeName[0] = 0;
3978 if (key->nodeName.length > 0) {
b0d623f7
A
3979 int res;
3980 if ((res = unicode_to_hfs(HFSTOVCB(hfsmp),
9bccf70c
A
3981 key->nodeName.length * 2,
3982 key->nodeName.unicode,
b0d623f7
A
3983 &hfskey.nodeName[0], retry)) != 0) {
3984 if (res != ENAMETOOLONG)
3985 res = EINVAL;
3986
3987 return res;
9bccf70c
A
3988 }
3989 hfskey.keyLength += hfskey.nodeName[0];
3990 }
3991 bcopy(&hfskey, key, sizeof(hfskey));
3992 }
39236c6e
A
3993#endif
3994
9bccf70c
A
3995 return (0);
3996 }
3997
3998
3999/*
4000 * Resolve hard link reference to obtain the inode record.
4001 */
9bccf70c 4002int
b0d623f7 4003cat_resolvelink(struct hfsmount *hfsmp, u_int32_t linkref, int isdirlink, struct HFSPlusCatalogFile *recp)
9bccf70c
A
4004{
4005 FSBufferDescriptor btdata;
4006 struct BTreeIterator *iterator;
4007 struct cat_desc idesc;
4008 char inodename[32];
2d21ac55 4009 cnid_t parentcnid;
9bccf70c
A
4010 int result = 0;
4011
4012 BDINIT(btdata, recp);
2d21ac55
A
4013
4014 if (isdirlink) {
4015 MAKE_DIRINODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
4016 parentcnid = hfsmp->hfs_private_desc[DIR_HARDLINKS].cd_cnid;
4017 } else {
4018 MAKE_INODE_NAME(inodename, sizeof(inodename), (unsigned int)linkref);
4019 parentcnid = hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid;
4020 }
9bccf70c
A
4021
4022 /* Get space for iterator */
4023 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4024 bzero(iterator, sizeof(*iterator));
4025
4026 /* Build a descriptor for private dir. */
2d21ac55
A
4027 idesc.cd_parentcnid = parentcnid;
4028 idesc.cd_nameptr = (const u_int8_t *)inodename;
9bccf70c
A
4029 idesc.cd_namelen = strlen(inodename);
4030 idesc.cd_flags = 0;
4031 idesc.cd_hint = 0;
4032 idesc.cd_encoding = 0;
4033 (void) buildkey(hfsmp, &idesc, (HFSPlusCatalogKey *)&iterator->key, 0);
4034
4035 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4036 &btdata, NULL, NULL);
4037
4038 if (result == 0) {
4039 /* Make sure there's a reference */
2d21ac55
A
4040 if (recp->hl_linkCount == 0)
4041 recp->hl_linkCount = 2;
9bccf70c 4042 } else {
39236c6e 4043 printf("hfs: cat_resolvelink: can't find inode=%s on vol=%s\n", inodename, hfsmp->vcbVN);
9bccf70c
A
4044 }
4045
4046 FREE(iterator, M_TEMP);
4047
4048 return (result ? ENOENT : 0);
4049}
4050
55e303ae
A
4051/*
4052 * Resolve hard link reference to obtain the inode number.
4053 */
4054static int
b0d623f7 4055resolvelinkid(struct hfsmount *hfsmp, u_int32_t linkref, ino_t *ino)
55e303ae
A
4056{
4057 struct HFSPlusCatalogFile record;
4058 int error;
2d21ac55
A
4059
4060 /*
4061 * Since we know resolvelinkid is only called from
4062 * cat_getdirentries, we can assume that only file
4063 * hardlinks need to be resolved (cat_getdirentries
4064 * can resolve directory hardlinks in place).
4065 */
4066 error = cat_resolvelink(hfsmp, linkref, 0, &record);
55e303ae
A
4067 if (error == 0) {
4068 if (record.fileID == 0)
4069 error = ENOENT;
4070 else
4071 *ino = record.fileID;
4072 }
4073 return (error);
4074}
4075
9bccf70c
A
4076/*
4077 * getkey - get a key from id by doing a thread lookup
4078 */
4079static int
4080getkey(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key)
4081{
4082 struct BTreeIterator * iterator;
4083 FSBufferDescriptor btdata;
2d21ac55 4084 u_int16_t datasize;
9bccf70c
A
4085 CatalogKey * keyp;
4086 CatalogRecord * recp;
4087 int result;
4088 int std_hfs;
4089
4090 std_hfs = (HFSTOVCB(hfsmp)->vcbSigWord == kHFSSigWord);
4091
4092 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4093 bzero(iterator, sizeof(*iterator));
4094 buildthreadkey(cnid, std_hfs, (CatalogKey *)&iterator->key);
4095
4096 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
4097 BDINIT(btdata, recp);
4098
4099 result = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4100 &btdata, &datasize, iterator);
4101 if (result)
4102 goto exit;
4103
4104 /* Turn thread record into a cnode key (in place) */
4105 switch (recp->recordType) {
39236c6e
A
4106
4107#if CONFIG_HFS_STD
9bccf70c
A
4108 case kHFSFileThreadRecord:
4109 case kHFSFolderThreadRecord:
4110 keyp = (CatalogKey *)((char *)&recp->hfsThread.reserved + 6);
4111 keyp->hfs.keyLength = kHFSCatalogKeyMinimumLength + keyp->hfs.nodeName[0];
4112 bcopy(keyp, key, keyp->hfs.keyLength + 1);
4113 break;
39236c6e 4114#endif
9bccf70c
A
4115
4116 case kHFSPlusFileThreadRecord:
4117 case kHFSPlusFolderThreadRecord:
4118 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
4119 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
4120 (keyp->hfsPlus.nodeName.length * 2);
4121 bcopy(keyp, key, keyp->hfsPlus.keyLength + 2);
4122 break;
4123
4124 default:
4125 result = ENOENT;
4126 break;
4127 }
4128
4129exit:
4130 FREE(iterator, M_TEMP);
4131 FREE(recp, M_TEMP);
4132
4133 return MacToVFSError(result);
4134}
4135
91447636
A
4136/*
4137 * getkeyplusattr - From id, fetch the key and the bsd attrs for a file/dir (could pass
4138 * null arguments to cat_idlookup instead, but we save around 10% by not building the
4139 * cat_desc here). Both key and attrp must point to real structures.
2d21ac55
A
4140 *
4141 * The key's parent id is the only part of the key expected to be used by the caller.
4142 * The name portion of the key may not always be valid (ie in the case of a hard link).
91447636 4143 */
91447636
A
4144int
4145cat_getkeyplusattr(struct hfsmount *hfsmp, cnid_t cnid, CatalogKey * key, struct cat_attr *attrp)
4146{
4147 int result;
4148
4149 result = getkey(hfsmp, cnid, key);
4150
4151 if (result == 0) {
2d21ac55 4152 result = cat_lookupbykey(hfsmp, key, 0, 0, 0, NULL, attrp, NULL, NULL);
91447636 4153 }
2d21ac55
A
4154 /*
4155 * Check for a raw file hardlink inode.
4156 * Fix up the parent id in the key if necessary.
4157 * Only hard links created by Mac OS X 10.5 or later can be resolved here.
4158 */
4159 if ((result == 0) &&
4160 (key->hfsPlus.parentID == hfsmp->hfs_private_desc[FILE_HARDLINKS].cd_cnid) &&
4161 (attrp->ca_recflags & kHFSHasLinkChainMask)) {
4162 cnid_t nextlinkid = 0;
4163 cnid_t prevlinkid = 0;
4164 struct cat_desc linkdesc;
91447636 4165
2d21ac55
A
4166 /*
4167 * Pick up the first link in the chain and get a descriptor for it.
4168 * This allows blind bulk access checks to work for hardlinks.
4169 */
6d2010ae 4170 if ((cat_lookup_siblinglinks(hfsmp, cnid, &prevlinkid, &nextlinkid) == 0) &&
2d21ac55
A
4171 (nextlinkid != 0)) {
4172 if (cat_findname(hfsmp, nextlinkid, &linkdesc) == 0) {
4173 key->hfsPlus.parentID = linkdesc.cd_parentcnid;
4174 cat_releasedesc(&linkdesc);
4175 }
4176 }
4177 }
91447636
A
4178 return MacToVFSError(result);
4179}
4180
9bccf70c
A
4181
4182/*
4183 * buildrecord - build a default catalog directory or file record
4184 */
4185static void
4186buildrecord(struct cat_attr *attrp, cnid_t cnid, int std_hfs, u_int32_t encoding,
2d21ac55 4187 CatalogRecord *crp, u_int32_t *recordSize)
9bccf70c
A
4188{
4189 int type = attrp->ca_mode & S_IFMT;
4190 u_int32_t createtime = to_hfs_time(attrp->ca_itime);
4191
39236c6e 4192 if (std_hfs == 0) {
9bccf70c 4193 struct HFSPlusBSDInfo * bsdp = NULL;
9bccf70c
A
4194
4195 if (type == S_IFDIR) {
9bccf70c 4196 crp->recordType = kHFSPlusFolderRecord;
2d21ac55 4197 crp->hfsPlusFolder.flags = attrp->ca_recflags;
91447636 4198 crp->hfsPlusFolder.valence = 0;
9bccf70c
A
4199 crp->hfsPlusFolder.folderID = cnid;
4200 crp->hfsPlusFolder.createDate = createtime;
4201 crp->hfsPlusFolder.contentModDate = createtime;
9bccf70c 4202 crp->hfsPlusFolder.attributeModDate = createtime;
91447636
A
4203 crp->hfsPlusFolder.accessDate = createtime;
4204 crp->hfsPlusFolder.backupDate = 0;
9bccf70c 4205 crp->hfsPlusFolder.textEncoding = encoding;
2d21ac55 4206 crp->hfsPlusFolder.folderCount = 0;
9bccf70c
A
4207 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFolder.userInfo, 32);
4208 bsdp = &crp->hfsPlusFolder.bsdInfo;
2d21ac55 4209 bsdp->special.linkCount = 1;
9bccf70c
A
4210 *recordSize = sizeof(HFSPlusCatalogFolder);
4211 } else {
9bccf70c 4212 crp->recordType = kHFSPlusFileRecord;
2d21ac55 4213 crp->hfsPlusFile.flags = attrp->ca_recflags;
91447636 4214 crp->hfsPlusFile.reserved1 = 0;
9bccf70c
A
4215 crp->hfsPlusFile.fileID = cnid;
4216 crp->hfsPlusFile.createDate = createtime;
4217 crp->hfsPlusFile.contentModDate = createtime;
4218 crp->hfsPlusFile.accessDate = createtime;
4219 crp->hfsPlusFile.attributeModDate = createtime;
91447636 4220 crp->hfsPlusFile.backupDate = 0;
9bccf70c 4221 crp->hfsPlusFile.textEncoding = encoding;
2d21ac55
A
4222 crp->hfsPlusFile.reserved2 = 0;
4223 bcopy(attrp->ca_finderinfo, &crp->hfsPlusFile.userInfo, 32);
9bccf70c 4224 bsdp = &crp->hfsPlusFile.bsdInfo;
2d21ac55
A
4225 /* BLK/CHR need to save the device info */
4226 if (type == S_IFBLK || type == S_IFCHR) {
9bccf70c 4227 bsdp->special.rawDevice = attrp->ca_rdev;
2d21ac55
A
4228 } else {
4229 bsdp->special.linkCount = 1;
9bccf70c 4230 }
91447636 4231 bzero(&crp->hfsPlusFile.dataFork, 2*sizeof(HFSPlusForkData));
9bccf70c
A
4232 *recordSize = sizeof(HFSPlusCatalogFile);
4233 }
4234 bsdp->ownerID = attrp->ca_uid;
4235 bsdp->groupID = attrp->ca_gid;
4236 bsdp->fileMode = attrp->ca_mode;
4237 bsdp->adminFlags = attrp->ca_flags >> 16;
4238 bsdp->ownerFlags = attrp->ca_flags & 0x000000FF;
4239 }
39236c6e
A
4240#if CONFIG_HFS_STD
4241 else {
4242 createtime = UTCToLocal(createtime);
4243 if (type == S_IFDIR) {
4244 bzero(crp, sizeof(HFSCatalogFolder));
4245 crp->recordType = kHFSFolderRecord;
4246 crp->hfsFolder.folderID = cnid;
4247 crp->hfsFolder.createDate = createtime;
4248 crp->hfsFolder.modifyDate = createtime;
4249 bcopy(attrp->ca_finderinfo, &crp->hfsFolder.userInfo, 32);
4250 *recordSize = sizeof(HFSCatalogFolder);
4251 } else {
4252 bzero(crp, sizeof(HFSCatalogFile));
4253 crp->recordType = kHFSFileRecord;
4254 crp->hfsFile.fileID = cnid;
4255 crp->hfsFile.createDate = createtime;
4256 crp->hfsFile.modifyDate = createtime;
4257 bcopy(attrp->ca_finderinfo, &crp->hfsFile.userInfo, 16);
4258 bcopy(&attrp->ca_finderinfo[16], &crp->hfsFile.finderInfo, 16);
4259 *recordSize = sizeof(HFSCatalogFile);
4260 }
4261 }
4262#endif
4263
9bccf70c
A
4264}
4265
4266
4267/*
4268 * builddesc - build a cnode descriptor from an HFS+ key
4269 */
4270static int
b0d623f7 4271builddesc(const HFSPlusCatalogKey *key, cnid_t cnid, u_int32_t hint, u_int32_t encoding,
9bccf70c
A
4272 int isdir, struct cat_desc *descp)
4273{
4274 int result = 0;
2d21ac55 4275 unsigned char * nameptr;
91447636 4276 size_t bufsize;
9bccf70c 4277 size_t utf8len;
2d21ac55 4278 unsigned char tmpbuff[128];
9bccf70c
A
4279
4280 /* guess a size... */
4281 bufsize = (3 * key->nodeName.length) + 1;
91447636 4282 if (bufsize >= sizeof(tmpbuff) - 1) {
2d21ac55 4283 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
55e303ae
A
4284 } else {
4285 nameptr = &tmpbuff[0];
4286 }
9bccf70c
A
4287
4288 result = utf8_encodestr(key->nodeName.unicode,
4289 key->nodeName.length * sizeof(UniChar),
4290 nameptr, (size_t *)&utf8len,
4291 bufsize, ':', 0);
4292
4293 if (result == ENAMETOOLONG) {
4294 bufsize = 1 + utf8_encodelen(key->nodeName.unicode,
4295 key->nodeName.length * sizeof(UniChar),
4296 ':', 0);
4297 FREE(nameptr, M_TEMP);
2d21ac55 4298 MALLOC(nameptr, unsigned char *, bufsize, M_TEMP, M_WAITOK);
9bccf70c
A
4299
4300 result = utf8_encodestr(key->nodeName.unicode,
4301 key->nodeName.length * sizeof(UniChar),
4302 nameptr, (size_t *)&utf8len,
4303 bufsize, ':', 0);
4304 }
4305 descp->cd_parentcnid = key->parentID;
2d21ac55 4306 descp->cd_nameptr = (const u_int8_t *)vfs_addname((char *)nameptr, utf8len, 0, 0);
9bccf70c
A
4307 descp->cd_namelen = utf8len;
4308 descp->cd_cnid = cnid;
4309 descp->cd_hint = hint;
4310 descp->cd_flags = CD_DECOMPOSED | CD_HASBUF;
4311 if (isdir)
55e303ae 4312 descp->cd_flags |= CD_ISDIR;
9bccf70c 4313 descp->cd_encoding = encoding;
55e303ae
A
4314 if (nameptr != &tmpbuff[0]) {
4315 FREE(nameptr, M_TEMP);
4316 }
9bccf70c
A
4317 return result;
4318}
4319
4320
4321/*
4322 * getbsdattr - get attributes in bsd format
4323 *
4324 */
4325static void
4326getbsdattr(struct hfsmount *hfsmp, const struct HFSPlusCatalogFile *crp, struct cat_attr * attrp)
4327{
4328 int isDirectory = (crp->recordType == kHFSPlusFolderRecord);
4329 const struct HFSPlusBSDInfo *bsd = &crp->bsdInfo;
4330
91447636 4331 attrp->ca_recflags = crp->flags;
9bccf70c 4332 attrp->ca_atime = to_bsd_time(crp->accessDate);
91447636 4333 attrp->ca_atimeondisk = attrp->ca_atime;
9bccf70c 4334 attrp->ca_mtime = to_bsd_time(crp->contentModDate);
9bccf70c
A
4335 attrp->ca_ctime = to_bsd_time(crp->attributeModDate);
4336 attrp->ca_itime = to_bsd_time(crp->createDate);
4337 attrp->ca_btime = to_bsd_time(crp->backupDate);
4338
4339 if ((bsd->fileMode & S_IFMT) == 0) {
4340 attrp->ca_flags = 0;
4341 attrp->ca_uid = hfsmp->hfs_uid;
4342 attrp->ca_gid = hfsmp->hfs_gid;
2d21ac55 4343 if (isDirectory) {
9bccf70c 4344 attrp->ca_mode = S_IFDIR | (hfsmp->hfs_dir_mask & ACCESSPERMS);
2d21ac55 4345 } else {
9bccf70c 4346 attrp->ca_mode = S_IFREG | (hfsmp->hfs_file_mask & ACCESSPERMS);
2d21ac55
A
4347 }
4348 attrp->ca_linkcount = 1;
9bccf70c
A
4349 attrp->ca_rdev = 0;
4350 } else {
2d21ac55 4351 attrp->ca_linkcount = 1; /* may be overridden below */
9bccf70c
A
4352 attrp->ca_rdev = 0;
4353 attrp->ca_uid = bsd->ownerID;
4354 attrp->ca_gid = bsd->groupID;
4355 attrp->ca_flags = bsd->ownerFlags | (bsd->adminFlags << 16);
4356 attrp->ca_mode = (mode_t)bsd->fileMode;
4357 switch (attrp->ca_mode & S_IFMT) {
4358 case S_IFCHR: /* fall through */
4359 case S_IFBLK:
4360 attrp->ca_rdev = bsd->special.rawDevice;
4361 break;
2d21ac55
A
4362
4363 case S_IFDIR: /* fall through */
9bccf70c
A
4364 case S_IFREG:
4365 /* Pick up the hard link count */
4366 if (bsd->special.linkCount > 0)
2d21ac55 4367 attrp->ca_linkcount = bsd->special.linkCount;
9bccf70c
A
4368 break;
4369 }
4370
2d21ac55
A
4371 /*
4372 * Override the permissions as determined by the mount auguments
4373 * in ALMOST the same way unset permissions are treated but keep
4374 * track of whether or not the file or folder is hfs locked
4375 * by leaving the h_pflags field unchanged from what was unpacked
4376 * out of the catalog.
4377 */
4378 /*
4379 * This code was used to do UID translation with MNT_IGNORE_OWNERS
4380 * (aka MNT_UNKNOWNPERMISSIONS) at the HFS layer. It's largely done
4381 * at the VFS layer, so there is no need to do it here now; this also
4382 * allows VFS to let root see the real UIDs.
4383 *
4384 * if (((unsigned int)vfs_flags(HFSTOVFS(hfsmp))) & MNT_UNKNOWNPERMISSIONS) {
4385 * attrp->ca_uid = hfsmp->hfs_uid;
4386 * attrp->ca_gid = hfsmp->hfs_gid;
4387 * }
4388 */
9bccf70c
A
4389 }
4390
4391 if (isDirectory) {
4392 if (!S_ISDIR(attrp->ca_mode)) {
4393 attrp->ca_mode &= ~S_IFMT;
4394 attrp->ca_mode |= S_IFDIR;
4395 }
2d21ac55
A
4396 attrp->ca_entries = ((const HFSPlusCatalogFolder *)crp)->valence;
4397 attrp->ca_dircount = ((hfsmp->hfs_flags & HFS_FOLDERCOUNT) && (attrp->ca_recflags & kHFSHasFolderCountMask)) ?
4398 ((const HFSPlusCatalogFolder *)crp)->folderCount : 0;
4399
4400 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4401 if (((const HFSPlusCatalogFolder *)crp)->userInfo.frFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
4402 attrp->ca_flags |= UF_HIDDEN;
9bccf70c
A
4403 } else {
4404 /* Keep IMMUTABLE bits in sync with HFS locked flag */
4405 if (crp->flags & kHFSFileLockedMask) {
4406 /* The file's supposed to be locked:
4407 Make sure at least one of the IMMUTABLE bits is set: */
4408 if ((attrp->ca_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) == 0)
4409 attrp->ca_flags |= UF_IMMUTABLE;
4410 } else {
4411 /* The file's supposed to be unlocked: */
4412 attrp->ca_flags &= ~(SF_IMMUTABLE | UF_IMMUTABLE);
4413 }
2d21ac55
A
4414 /* Keep UF_HIDDEN bit in sync with Finder Info's invisible bit */
4415 if (crp->userInfo.fdFlags & OSSwapHostToBigConstInt16(kFinderInvisibleMask))
4416 attrp->ca_flags |= UF_HIDDEN;
9bccf70c
A
4417 /* get total blocks (both forks) */
4418 attrp->ca_blocks = crp->dataFork.totalBlocks + crp->resourceFork.totalBlocks;
2d21ac55 4419
b36670ce
A
4420 /* On HFS+ the ThreadExists flag must always be set. */
4421 if ((hfsmp->hfs_flags & HFS_STANDARD) == 0)
4422 attrp->ca_recflags |= kHFSThreadExistsMask;
2d21ac55
A
4423
4424 /* Pick up the hardlink first link, if any. */
4425 attrp->ca_firstlink = (attrp->ca_recflags & kHFSHasLinkChainMask) ? crp->hl_firstLinkID : 0;
9bccf70c
A
4426 }
4427
4428 attrp->ca_fileid = crp->fileID;
4429
4430 bcopy(&crp->userInfo, attrp->ca_finderinfo, 32);
4431}
4432
39236c6e 4433#if CONFIG_HFS_STD
9bccf70c
A
4434/*
4435 * promotekey - promote hfs key to hfs plus key
4436 *
4437 */
4438static void
4439promotekey(struct hfsmount *hfsmp, const HFSCatalogKey *hfskey,
b0d623f7 4440 HFSPlusCatalogKey *keyp, u_int32_t *encoding)
9bccf70c
A
4441{
4442 hfs_to_unicode_func_t hfs_get_unicode = hfsmp->hfs_get_unicode;
2d21ac55 4443 u_int32_t uniCount;
9bccf70c
A
4444 int error;
4445
4446 *encoding = hfsmp->hfs_encoding;
4447
4448 error = hfs_get_unicode(hfskey->nodeName, keyp->nodeName.unicode,
4449 kHFSPlusMaxFileNameChars, &uniCount);
4450 /*
4451 * When an HFS name cannot be encoded with the current
4452 * encoding use MacRoman as a fallback.
4453 */
4454 if (error && hfsmp->hfs_encoding != kTextEncodingMacRoman) {
4455 *encoding = 0;
4456 (void) mac_roman_to_unicode(hfskey->nodeName,
4457 keyp->nodeName.unicode,
4458 kHFSPlusMaxFileNameChars,
4459 &uniCount);
4460 }
4461
4462 keyp->nodeName.length = uniCount;
4463 keyp->parentID = hfskey->parentID;
4464}
4465
4466/*
4467 * promotefork - promote hfs fork info to hfs plus
4468 *
4469 */
4470static void
4471promotefork(struct hfsmount *hfsmp, const struct HFSCatalogFile *filep,
4472 int resource, struct cat_fork * forkp)
4473{
4474 struct HFSPlusExtentDescriptor *xp;
b0d623f7 4475 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
9bccf70c
A
4476
4477 bzero(forkp, sizeof(*forkp));
4478 xp = &forkp->cf_extents[0];
4479 if (resource) {
4480 forkp->cf_size = filep->rsrcLogicalSize;
4481 forkp->cf_blocks = filep->rsrcPhysicalSize / blocksize;
55e303ae
A
4482 forkp->cf_bytesread = 0;
4483 forkp->cf_vblocks = 0;
9bccf70c
A
4484 xp[0].startBlock = (u_int32_t)filep->rsrcExtents[0].startBlock;
4485 xp[0].blockCount = (u_int32_t)filep->rsrcExtents[0].blockCount;
4486 xp[1].startBlock = (u_int32_t)filep->rsrcExtents[1].startBlock;
4487 xp[1].blockCount = (u_int32_t)filep->rsrcExtents[1].blockCount;
4488 xp[2].startBlock = (u_int32_t)filep->rsrcExtents[2].startBlock;
4489 xp[2].blockCount = (u_int32_t)filep->rsrcExtents[2].blockCount;
4490 } else {
4491 forkp->cf_size = filep->dataLogicalSize;
4492 forkp->cf_blocks = filep->dataPhysicalSize / blocksize;
55e303ae
A
4493 forkp->cf_bytesread = 0;
4494 forkp->cf_vblocks = 0;
9bccf70c
A
4495 xp[0].startBlock = (u_int32_t)filep->dataExtents[0].startBlock;
4496 xp[0].blockCount = (u_int32_t)filep->dataExtents[0].blockCount;
4497 xp[1].startBlock = (u_int32_t)filep->dataExtents[1].startBlock;
4498 xp[1].blockCount = (u_int32_t)filep->dataExtents[1].blockCount;
4499 xp[2].startBlock = (u_int32_t)filep->dataExtents[2].startBlock;
4500 xp[2].blockCount = (u_int32_t)filep->dataExtents[2].blockCount;
4501 }
4502}
4503
4504/*
2d21ac55 4505 * promoteattr - promote standard hfs catalog attributes to hfs plus
9bccf70c
A
4506 *
4507 */
4508static void
4509promoteattr(struct hfsmount *hfsmp, const CatalogRecord *dataPtr, struct HFSPlusCatalogFile *crp)
4510{
b0d623f7 4511 u_int32_t blocksize = HFSTOVCB(hfsmp)->blockSize;
9bccf70c
A
4512
4513 if (dataPtr->recordType == kHFSFolderRecord) {
2d21ac55 4514 const struct HFSCatalogFolder * folder;
9bccf70c 4515
2d21ac55 4516 folder = (const struct HFSCatalogFolder *) dataPtr;
9bccf70c
A
4517 crp->recordType = kHFSPlusFolderRecord;
4518 crp->flags = folder->flags;
4519 crp->fileID = folder->folderID;
4520 crp->createDate = LocalToUTC(folder->createDate);
4521 crp->contentModDate = LocalToUTC(folder->modifyDate);
4522 crp->backupDate = LocalToUTC(folder->backupDate);
4523 crp->reserved1 = folder->valence;
2d21ac55 4524 crp->reserved2 = 0;
9bccf70c
A
4525 bcopy(&folder->userInfo, &crp->userInfo, 32);
4526 } else /* file */ {
2d21ac55 4527 const struct HFSCatalogFile * file;
9bccf70c 4528
2d21ac55 4529 file = (const struct HFSCatalogFile *) dataPtr;
9bccf70c
A
4530 crp->recordType = kHFSPlusFileRecord;
4531 crp->flags = file->flags;
4532 crp->fileID = file->fileID;
4533 crp->createDate = LocalToUTC(file->createDate);
4534 crp->contentModDate = LocalToUTC(file->modifyDate);
4535 crp->backupDate = LocalToUTC(file->backupDate);
4536 crp->reserved1 = 0;
2d21ac55 4537 crp->reserved2 = 0;
9bccf70c
A
4538 bcopy(&file->userInfo, &crp->userInfo, 16);
4539 bcopy(&file->finderInfo, &crp->finderInfo, 16);
4540 crp->dataFork.totalBlocks = file->dataPhysicalSize / blocksize;
4541 crp->resourceFork.totalBlocks = file->rsrcPhysicalSize / blocksize;
4542 }
4543 crp->textEncoding = 0;
4544 crp->attributeModDate = crp->contentModDate;
4545 crp->accessDate = crp->contentModDate;
4546 bzero(&crp->bsdInfo, sizeof(HFSPlusBSDInfo));
9bccf70c 4547}
39236c6e 4548#endif
9bccf70c
A
4549
4550/*
4551 * Build a catalog node thread record from a catalog key
4552 * and return the size of the record.
4553 */
4554static int
4555buildthread(void *keyp, void *recp, int std_hfs, int directory)
4556{
4557 int size = 0;
4558
39236c6e 4559 if (std_hfs == 0) {
9bccf70c
A
4560 HFSPlusCatalogKey *key = (HFSPlusCatalogKey *)keyp;
4561 HFSPlusCatalogThread *rec = (HFSPlusCatalogThread *)recp;
4562
4563 size = sizeof(HFSPlusCatalogThread);
4564 if (directory)
4565 rec->recordType = kHFSPlusFolderThreadRecord;
4566 else
4567 rec->recordType = kHFSPlusFileThreadRecord;
4568 rec->reserved = 0;
4569 rec->parentID = key->parentID;
4570 bcopy(&key->nodeName, &rec->nodeName,
4571 sizeof(UniChar) * (key->nodeName.length + 1));
4572
39236c6e 4573 /* HFS Plus has variable sized thread records */
9bccf70c
A
4574 size -= (sizeof(rec->nodeName.unicode) -
4575 (rec->nodeName.length * sizeof(UniChar)));
39236c6e 4576
9bccf70c 4577 }
39236c6e
A
4578#if CONFIG_HFS_STD
4579 else {
4580 HFSCatalogKey *key = (HFSCatalogKey *)keyp;
4581 HFSCatalogThread *rec = (HFSCatalogThread *)recp;
4582
4583 size = sizeof(HFSCatalogThread);
4584 bzero(rec, size);
4585 if (directory)
4586 rec->recordType = kHFSFolderThreadRecord;
4587 else
4588 rec->recordType = kHFSFileThreadRecord;
4589 rec->parentID = key->parentID;
4590 bcopy(key->nodeName, rec->nodeName, key->nodeName[0]+1);
4591
4592 }
4593#endif
4594
9bccf70c
A
4595 return (size);
4596}
4597
4598/*
4599 * Build a catalog node thread key.
4600 */
4601static void
4602buildthreadkey(HFSCatalogNodeID parentID, int std_hfs, CatalogKey *key)
4603{
39236c6e
A
4604 if (std_hfs == 0) {
4605 key->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength;
4606 key->hfsPlus.parentID = parentID;
4607 key->hfsPlus.nodeName.length = 0;
4608 }
4609#if CONFIG_HFS_STD
4610 else {
9bccf70c
A
4611 key->hfs.keyLength = kHFSCatalogKeyMinimumLength;
4612 key->hfs.reserved = 0;
4613 key->hfs.parentID = parentID;
4614 key->hfs.nodeName[0] = 0;
9bccf70c 4615 }
39236c6e
A
4616#endif
4617
9bccf70c
A
4618}
4619
4620/*
4621 * Extract the text encoding from a catalog node record.
4622 */
b0d623f7 4623static u_int32_t
9bccf70c
A
4624getencoding(const CatalogRecord *crp)
4625{
b0d623f7 4626 u_int32_t encoding;
9bccf70c
A
4627
4628 if (crp->recordType == kHFSPlusFolderRecord)
4629 encoding = crp->hfsPlusFolder.textEncoding;
4630 else if (crp->recordType == kHFSPlusFileRecord)
4631 encoding = crp->hfsPlusFile.textEncoding;
4632 else
4633 encoding = 0;
4634
4635 return (encoding);
4636}
4637
4638/*
4639 * Extract the CNID from a catalog node record.
4640 */
4641static cnid_t
4642getcnid(const CatalogRecord *crp)
4643{
4644 cnid_t cnid = 0;
4645
4646 switch (crp->recordType) {
39236c6e
A
4647
4648#if CONFIG_HFS_STD
9bccf70c
A
4649 case kHFSFolderRecord:
4650 cnid = crp->hfsFolder.folderID;
4651 break;
4652 case kHFSFileRecord:
4653 cnid = crp->hfsFile.fileID;
4654 break;
39236c6e
A
4655#endif
4656
9bccf70c
A
4657 case kHFSPlusFolderRecord:
4658 cnid = crp->hfsPlusFolder.folderID;
4659 break;
4660 case kHFSPlusFileRecord:
4661 cnid = crp->hfsPlusFile.fileID;
4662 break;
b4c24cb9 4663 default:
39236c6e 4664 printf("hfs: getcnid: unknown recordType=%d\n", crp->recordType);
b4c24cb9 4665 break;
9bccf70c 4666 }
b4c24cb9 4667
9bccf70c
A
4668 return (cnid);
4669}
4670
4671/*
4672 * Extract the parent ID from a catalog node record.
4673 */
4674static cnid_t
4675getparentcnid(const CatalogRecord *recp)
4676{
4677 cnid_t cnid = 0;
4678
4679 switch (recp->recordType) {
39236c6e
A
4680
4681#if CONFIG_HFS_STD
9bccf70c
A
4682 case kHFSFileThreadRecord:
4683 case kHFSFolderThreadRecord:
4684 cnid = recp->hfsThread.parentID;
4685 break;
39236c6e 4686#endif
9bccf70c
A
4687
4688 case kHFSPlusFileThreadRecord:
4689 case kHFSPlusFolderThreadRecord:
4690 cnid = recp->hfsPlusThread.parentID;
4691 break;
b4c24cb9 4692 default:
2d21ac55 4693 panic("hfs: getparentcnid: unknown recordType (crp @ %p)\n", recp);
b4c24cb9 4694 break;
9bccf70c 4695 }
b4c24cb9 4696
9bccf70c
A
4697 return (cnid);
4698}
4699
4700/*
4701 * Determine if a catalog node record is a directory.
4702 */
4703static int
4704isadir(const CatalogRecord *crp)
4705{
39236c6e
A
4706 if (crp->recordType == kHFSPlusFolderRecord) {
4707 return 1;
4708 }
4709#if CONFIG_HFS_STD
4710 if (crp->recordType == kHFSFolderRecord) {
4711 return 1;
4712 }
4713#endif
4714
4715 return 0;
9bccf70c
A
4716}
4717
6d2010ae
A
4718/*
4719 * cat_lookup_dirlink - lookup a catalog record for directory hard link
4720 * (not inode) using catalog record id. Note that this function does
4721 * NOT resolve directory hard link to its directory inode and return
4722 * the link record.
4723 *
4724 * Note: The caller is responsible for releasing the output catalog
4725 * descriptor (when supplied outdescp is non-null).
4726 */
4727int
4728cat_lookup_dirlink(struct hfsmount *hfsmp, cnid_t dirlink_id,
4729 u_int8_t forktype, struct cat_desc *outdescp,
4730 struct cat_attr *attrp, struct cat_fork *forkp)
4731{
4732 struct BTreeIterator *iterator = NULL;
4733 FSBufferDescriptor btdata;
4734 u_int16_t datasize;
4735 CatalogKey *keyp;
4736 CatalogRecord *recp = NULL;
4737 int error;
4738
4739 /* No directory hard links on standard HFS */
4740 if (hfsmp->vcbSigWord == kHFSSigWord) {
4741 return ENOTSUP;
4742 }
4743
4744 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
4745 if (iterator == NULL) {
4746 return ENOMEM;
4747 }
4748 bzero(iterator, sizeof(*iterator));
4749 buildthreadkey(dirlink_id, 1, (CatalogKey *)&iterator->key);
4750
4751 MALLOC(recp, CatalogRecord *, sizeof(CatalogRecord), M_TEMP, M_WAITOK);
4752 if (recp == NULL) {
4753 error = ENOMEM;
4754 goto out;
4755 }
4756 BDINIT(btdata, recp);
4757
4758 error = BTSearchRecord(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), iterator,
4759 &btdata, &datasize, iterator);
4760 if (error) {
4761 goto out;
4762 }
4763 /* Directory hard links are catalog file record */
4764 if (recp->recordType != kHFSPlusFileThreadRecord) {
4765 error = ENOENT;
4766 goto out;
4767 }
4768
4769 keyp = (CatalogKey *)&recp->hfsPlusThread.reserved;
4770 keyp->hfsPlus.keyLength = kHFSPlusCatalogKeyMinimumLength +
4771 (keyp->hfsPlus.nodeName.length * 2);
4772 if (forktype == kHFSResourceForkType) {
4773 /* Lookup resource fork for directory hard link */
4774 error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, true, outdescp, attrp, forkp, NULL);
4775 } else {
4776 /* Lookup data fork, if any, for directory hard link */
4777 error = cat_lookupbykey(hfsmp, keyp, HFS_LOOKUP_HARDLINK, 0, false, outdescp, attrp, forkp, NULL);
4778 }
4779 if (error) {
4780 printf ("hfs: cat_lookup_dirlink(): Error looking up file record for id=%u (error=%d)\n", dirlink_id, error);
fe8ab488 4781 hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
6d2010ae
A
4782 goto out;
4783 }
4784 /* Just for sanity, make sure that id in catalog record and thread record match */
4785 if ((outdescp != NULL) && (dirlink_id != outdescp->cd_cnid)) {
4786 printf ("hfs: cat_lookup_dirlink(): Requested cnid=%u != found_cnid=%u\n", dirlink_id, outdescp->cd_cnid);
fe8ab488 4787 hfs_mark_inconsistent(hfsmp, HFS_INCONSISTENCY_DETECTED);
6d2010ae
A
4788 error = ENOENT;
4789 }
4790
4791out:
4792 if (recp) {
4793 FREE(recp, M_TEMP);
4794 }
4795 FREE(iterator, M_TEMP);
4796
4797 return MacToVFSError(error);
4798}
4799
4800/*
4801 * cnode_update_dirlink - update the catalog node for directory hard link
4802 * described by descp using the data from attrp and forkp.
4803 */
4804int
4805cat_update_dirlink(struct hfsmount *hfsmp, u_int8_t forktype,
4806 struct cat_desc *descp, struct cat_attr *attrp, struct cat_fork *forkp)
4807{
4808 if (forktype == kHFSResourceForkType) {
4809 return cat_update_internal(hfsmp, true, descp, attrp, NULL, forkp);
4810 } else {
4811 return cat_update_internal(hfsmp, true, descp, attrp, forkp, NULL);
4812 }
4813}
39236c6e 4814