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