]> git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_xattr.c
5030db050680343ba3990022d165505a4b0ae38e
[apple/xnu.git] / bsd / hfs / hfs_xattr.c
1 /*
2 * Copyright (c) 2004-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/kernel.h>
26 #include <sys/malloc.h>
27 #include <sys/utfconv.h>
28 #include <sys/vnode.h>
29 #include <sys/xattr.h>
30
31 #include "hfs.h"
32 #include "hfs_cnode.h"
33 #include "hfs_mount.h"
34 #include "hfs_format.h"
35 #include "hfs_endian.h"
36
37 #include "hfscommon/headers/BTreesInternal.h"
38
39
40 #define ATTRIBUTE_FILE_NODE_SIZE 8192
41
42
43 /* State information for the listattr_callback callback function. */
44 struct listattr_callback_state {
45 u_int32_t fileID;
46 int result;
47 uio_t uio;
48 size_t size;
49 };
50
51 #define HFS_MAXATTRIBUTESIZE (1024*1024)
52
53 /* HFS Internal Names */
54 #define XATTR_EXTENDEDSECURITY_NAME "system.extendedsecurity"
55
56
57 #define RESOURCE_FORK_EXISTS(VP) \
58 ((VTOC((VP))->c_blocks - VTOF((VP))->ff_blocks) > 0)
59
60 static u_int32_t emptyfinfo[8] = {0};
61
62
63 extern int hfs_create_attr_btree(struct hfsmount *hfsmp, uint32_t nodesize, uint32_t nodecnt);
64
65
66 int hfs_vnop_getxattr(struct vnop_getxattr_args *ap);
67 int hfs_vnop_setxattr(struct vnop_setxattr_args *ap);
68 int hfs_vnop_removexattr(struct vnop_removexattr_args *ap);
69 int hfs_vnop_listxattr(struct vnop_listxattr_args *ap);
70 int hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey);
71
72
73
74 static int listattr_callback(const HFSPlusAttrKey *key, const HFSPlusAttrData *data,
75 struct listattr_callback_state *state);
76
77 static int buildkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key);
78
79 static int getnodecount(struct hfsmount *hfsmp, size_t nodesize);
80
81 static size_t getmaxinlineattrsize(struct vnode * attrvp);
82
83 /*
84 * Retrieve the data of an extended attribute.
85 */
86 __private_extern__
87 int
88 hfs_vnop_getxattr(struct vnop_getxattr_args *ap)
89 /*
90 struct vnop_getxattr_args {
91 struct vnodeop_desc *a_desc;
92 vnode_t a_vp;
93 char * a_name;
94 uio_t a_uio;
95 size_t *a_size;
96 int a_options;
97 vfs_context_t a_context;
98 };
99 */
100 {
101 struct vnode *vp = ap->a_vp;
102 struct hfsmount *hfsmp;
103 uio_t uio = ap->a_uio;
104 struct BTreeIterator * iterator = NULL;
105 struct filefork *btfile;
106 FSBufferDescriptor btdata;
107 HFSPlusAttrData * datap = NULL;
108 size_t bufsize;
109 UInt16 datasize;
110 int lockflags;
111 int result;
112
113 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
114 return (EINVAL); /* invalid name */
115 }
116 hfsmp = VTOHFS(vp);
117
118 if (!VNODE_IS_RSRC(vp)) {
119 /* Get the Finder Info. */
120 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
121 bufsize = 32;
122
123 /* If Finder Info is empty then it doesn't exist. */
124 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
125 return (ENOATTR);
126 }
127 if (uio == NULL) {
128 *ap->a_size = bufsize;
129 return (0);
130 }
131 if (uio_resid(uio) < bufsize)
132 return (ERANGE);
133
134 result = uiomove((caddr_t) &VTOC(vp)->c_finderinfo , bufsize, uio);
135
136 return (result);
137 }
138 /* Read the Resource Fork. */
139 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
140 struct vnode *rvp = NULL;
141
142 if ( !vnode_isreg(vp) ) {
143 return (EPERM);
144 }
145 if ( !RESOURCE_FORK_EXISTS(vp)) {
146 return (ENOATTR);
147 }
148 if ((result = hfs_vgetrsrc(hfsmp, vp, &rvp, vfs_context_proc(ap->a_context)))) {
149 return (result);
150 }
151 if (uio == NULL) {
152 *ap->a_size = (size_t)VTOF(rvp)->ff_size;
153 } else {
154 result = VNOP_READ(rvp, uio, 0, ap->a_context);
155 }
156 vnode_put(rvp);
157 return (result);
158 }
159 }
160 /*
161 * Standard HFS only supports native FinderInfo and Resource Forks.
162 */
163 if (hfsmp->hfs_flags & HFS_STANDARD) {
164 return (EPERM);
165 }
166 /* Bail if we don't have any extended attributes. */
167 if ((hfsmp->hfs_attribute_vp == NULL) ||
168 (VTOC(vp)->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
169 return (ENOATTR);
170 }
171 btfile = VTOF(hfsmp->hfs_attribute_vp);
172
173 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
174 bzero(iterator, sizeof(*iterator));
175
176 bufsize = sizeof(HFSPlusAttrData) - 2;
177 if (uio)
178 bufsize += uio_resid(uio);
179 MALLOC(datap, HFSPlusAttrData *, bufsize, M_TEMP, M_WAITOK);
180 btdata.bufferAddress = datap;
181 btdata.itemSize = bufsize;
182 btdata.itemCount = 1;
183
184 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
185 if (result)
186 goto exit;
187
188 /* Lookup the attribute. */
189 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
190 result = BTSearchRecord(btfile, iterator, &btdata, &datasize, NULL);
191 hfs_systemfile_unlock(hfsmp, lockflags);
192
193 if (result) {
194 if (result == btNotFound)
195 result = ENOATTR;
196 goto exit;
197 }
198
199 *ap->a_size = datap->attrSize;
200
201 /* Copy out the attribute data. */
202 if (uio) {
203 if (datap->attrSize > uio_resid(uio))
204 result = ERANGE;
205 else
206 result = uiomove((caddr_t) &datap->attrData , datap->attrSize, uio);
207 }
208 exit:
209 FREE(datap, M_TEMP);
210 FREE(iterator, M_TEMP);
211
212 return MacToVFSError(result);
213 }
214
215 /*
216 * Set the data of an extended attribute.
217 */
218 __private_extern__
219 int
220 hfs_vnop_setxattr(struct vnop_setxattr_args *ap)
221 /*
222 struct vnop_setxattr_args {
223 struct vnodeop_desc *a_desc;
224 vnode_t a_vp;
225 char * a_name;
226 uio_t a_uio;
227 int a_options;
228 vfs_context_t a_context;
229 };
230 */
231 {
232 struct vnode *vp = ap->a_vp;
233 struct hfsmount *hfsmp;
234 uio_t uio = ap->a_uio;
235 struct BTreeIterator * iterator = NULL;
236 struct filefork *btfile;
237 size_t attrsize;
238 FSBufferDescriptor btdata;
239 HFSPlusAttrData * datap = NULL;
240 UInt16 datasize;
241 int lockflags;
242 int result;
243
244 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
245 return (EINVAL); /* invalid name */
246 }
247 hfsmp = VTOHFS(vp);
248 if (VNODE_IS_RSRC(vp)) {
249 return (EPERM);
250 }
251 /* Set the Finder Info. */
252 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
253 attrsize = 32;
254
255 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo))) {
256 /* attr exists and "create" was specified. */
257 if (ap->a_options & XATTR_CREATE) {
258 return (EEXIST);
259 }
260 } else {
261 /* attr doesn't exists and "replace" was specified. */
262 if (ap->a_options & XATTR_REPLACE) {
263 return (ENOATTR);
264 }
265 }
266 if (uio_resid(uio) != attrsize)
267 return (ERANGE);
268
269 result = uiomove((caddr_t) &VTOC(vp)->c_finderinfo , attrsize, uio);
270 if (result == 0) {
271 VTOC(vp)->c_touch_chgtime = TRUE;
272 VTOC(vp)->c_flag |= C_MODIFIED;
273 result = hfs_update(vp, FALSE);
274 }
275 return (result);
276 }
277 /* Write the Resource Fork. */
278 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
279 struct vnode *rvp = NULL;
280
281 if (!vnode_isreg(vp)) {
282 return (EPERM);
283 }
284 if (RESOURCE_FORK_EXISTS(vp)) {
285 /* attr exists and "create" was specified. */
286 if (ap->a_options & XATTR_CREATE) {
287 return (EEXIST);
288 }
289 } else {
290 /* attr doesn't exists and "replace" was specified. */
291 if (ap->a_options & XATTR_REPLACE) {
292 return (ENOATTR);
293 }
294 }
295 if ((result = hfs_vgetrsrc(hfsmp, vp, &rvp, vfs_context_proc(ap->a_context)))) {
296 return (result);
297 }
298 result = VNOP_WRITE(rvp, uio, 0, ap->a_context);
299 vnode_put(rvp);
300 return (result);
301 }
302 /*
303 * Standard HFS only supports native FinderInfo and Resource Forks.
304 */
305 if (hfsmp->hfs_flags & HFS_STANDARD) {
306 return (EPERM);
307 }
308 if (hfsmp->hfs_max_inline_attrsize == 0) {
309 hfsmp->hfs_max_inline_attrsize = getmaxinlineattrsize(hfsmp->hfs_attribute_vp);
310 }
311 attrsize = uio_resid(uio);
312 if (attrsize > hfsmp->hfs_max_inline_attrsize) {
313 /*
314 * XXX Need to support extent-based attributes XXX
315 */
316 return (E2BIG);
317 }
318 /* Calculate size of record rounded up to multiple of 2 bytes. */
319 datasize = sizeof(HFSPlusAttrData) - 2 + attrsize + ((attrsize & 1) ? 1 : 0);
320
321 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
322 bzero(iterator, sizeof(*iterator));
323
324 MALLOC(datap, HFSPlusAttrData *, datasize, M_TEMP, M_WAITOK);
325 btdata.bufferAddress = datap;
326 btdata.itemSize = datasize;
327 btdata.itemCount = 1;
328 datap->recordType = kHFSPlusAttrInlineData;
329 datap->reserved[0] = 0;
330 datap->reserved[1] = 0;
331 datap->attrSize = attrsize;
332
333 /* Copy in the attribute data. */
334 result = uiomove((caddr_t) &datap->attrData , attrsize, uio);
335 if (result) {
336 goto exit2;
337 }
338 /* Build a b-tree key. */
339 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
340 if (result) {
341 goto exit2;
342 }
343 /* Start a transaction for our changes. */
344 if (hfs_start_transaction(hfsmp) != 0) {
345 result = EINVAL;
346 goto exit2;
347 }
348
349 /* once we started the transaction, nobody can compete with us, so make sure this file is still there */
350 struct cnode *cp;
351 cp = VTOC(vp);
352 if (cp->c_flag & C_NOEXISTS) { /* this file has already been removed */
353 result = ENOENT;
354 goto exit1;
355 }
356
357 /*
358 * If there isn't an attributes b-tree then create one.
359 */
360 if (hfsmp->hfs_attribute_vp == NULL) {
361 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
362 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
363 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
364 hfs_systemfile_unlock(hfsmp, lockflags);
365 if (result) {
366 goto exit1;
367 }
368 }
369 btfile = VTOF(hfsmp->hfs_attribute_vp);
370
371 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
372
373 if (ap->a_options & XATTR_REPLACE) {
374 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
375 if (result)
376 goto exit0;
377 else
378 goto exit;
379 }
380
381 /* Insert the attribute. */
382 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
383 if (result) {
384 if (result != btExists) {
385 goto exit0;
386 }
387
388 // if it exists and XATTR_CREATE was specified,
389 // the spec says to return EEXIST
390 if (ap->a_options & XATTR_CREATE) {
391 result = EEXIST;
392 goto exit0;
393 }
394 /* XXX need to account for old size in c_attrblks */
395 result = BTReplaceRecord(btfile, iterator, &btdata, datasize);
396 }
397 exit:
398 (void) BTFlushPath(btfile);
399 exit0:
400 hfs_systemfile_unlock(hfsmp, lockflags);
401 if (result == 0) {
402 struct cnode * cp;
403
404 cp = VTOC(vp);
405 cp->c_touch_chgtime = TRUE;
406 if ((cp->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
407 cp->c_attr.ca_recflags |= kHFSHasAttributesMask;
408 (void) hfs_update(vp, 0);
409 }
410 HFS_KNOTE(vp, NOTE_ATTRIB);
411 }
412 exit1:
413 /* Finish the transaction of our changes. */
414 hfs_end_transaction(hfsmp);
415 exit2:
416 FREE(datap, M_TEMP);
417 FREE(iterator, M_TEMP);
418
419 if (result == btNotFound)
420 result = ENOATTR;
421 else
422 result = MacToVFSError(result);
423
424 return (result);
425 }
426
427 /*
428 * Remove an extended attribute.
429 */
430 __private_extern__
431 int
432 hfs_vnop_removexattr(struct vnop_removexattr_args *ap)
433 /*
434 struct vnop_removexattr_args {
435 struct vnodeop_desc *a_desc;
436 vnode_t a_vp;
437 char * a_name;
438 int a_options;
439 vfs_context_t a_context;
440 };
441 */
442 {
443 struct vnode *vp = ap->a_vp;
444 struct hfsmount *hfsmp;
445 struct BTreeIterator * iterator = NULL;
446 struct filefork *btfile;
447 struct proc *p = vfs_context_proc(ap->a_context);
448 FSBufferDescriptor btdata;
449 HFSPlusAttrData attrdata;
450 int lockflags;
451 int result;
452
453 if (ap->a_name == NULL || ap->a_name[0] == '\0') {
454 return (EINVAL); /* invalid name */
455 }
456 hfsmp = VTOHFS(vp);
457 if (VNODE_IS_RSRC(vp)) {
458 return (EPERM);
459 }
460
461 /* If Resource Fork is non-empty then truncate it. */
462 if (bcmp(ap->a_name, XATTR_RESOURCEFORK_NAME, sizeof(XATTR_RESOURCEFORK_NAME)) == 0) {
463 struct vnode *rvp = NULL;
464
465 if ( !vnode_isreg(vp) ) {
466 return (EPERM);
467 }
468 if ( !RESOURCE_FORK_EXISTS(vp) ) {
469 return (ENOATTR);
470 }
471 if ((result = hfs_vgetrsrc(hfsmp, vp, &rvp, p))) {
472 return (result);
473 }
474 hfs_lock_truncate(VTOC(rvp), TRUE);
475 if ((result = hfs_lock(VTOC(rvp), HFS_EXCLUSIVE_LOCK))) {
476 hfs_unlock_truncate(VTOC(vp));
477 vnode_put(rvp);
478 return (result);
479 }
480 result = hfs_truncate(rvp, (off_t)0, IO_NDELAY, 0, ap->a_context);
481
482 hfs_unlock_truncate(VTOC(rvp));
483 hfs_unlock(VTOC(rvp));
484
485 vnode_put(rvp);
486 return (result);
487 }
488 /* Clear out the Finder Info. */
489 if (bcmp(ap->a_name, XATTR_FINDERINFO_NAME, sizeof(XATTR_FINDERINFO_NAME)) == 0) {
490 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) == 0) {
491 return (ENOATTR);
492 }
493 bzero(VTOC(vp)->c_finderinfo, sizeof(emptyfinfo));
494 return (0);
495 }
496 /*
497 * Standard HFS only supports native FinderInfo and Resource Forks.
498 */
499 if (hfsmp->hfs_flags & HFS_STANDARD) {
500 return (EPERM);
501 }
502 if (hfsmp->hfs_attribute_vp == NULL) {
503 return (ENOATTR);
504 }
505 btfile = VTOF(hfsmp->hfs_attribute_vp);
506
507 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
508 bzero(iterator, sizeof(*iterator));
509
510 if (hfs_start_transaction(hfsmp) != 0) {
511 result = EINVAL;
512 goto exit2;
513 }
514
515 result = buildkey(VTOC(vp)->c_fileid, ap->a_name, (HFSPlusAttrKey *)&iterator->key);
516 if (result)
517 goto exit2;
518
519 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
520
521 btdata.bufferAddress = &attrdata;
522 btdata.itemSize = sizeof(attrdata);
523 btdata.itemCount = 1;
524 result = BTSearchRecord(btfile, iterator, &btdata, NULL, NULL);
525 if (result)
526 goto exit1;
527
528 result = BTDeleteRecord(btfile, iterator);
529 (void) BTFlushPath(btfile);
530 exit1:
531 hfs_systemfile_unlock(hfsmp, lockflags);
532 if (result == 0) {
533 VTOC(vp)->c_touch_chgtime = TRUE;
534 HFS_KNOTE(vp, NOTE_ATTRIB);
535 }
536 exit2:
537 if (result == btNotFound) {
538 result = ENOATTR;
539 }
540 hfs_end_transaction(hfsmp);
541
542 FREE(iterator, M_TEMP);
543
544 return MacToVFSError(result);
545 }
546
547
548 /*
549 * Retrieve the list of extended attribute names.
550 */
551 __private_extern__
552 int
553 hfs_vnop_listxattr(struct vnop_listxattr_args *ap)
554 /*
555 struct vnop_listxattr_args {
556 struct vnodeop_desc *a_desc;
557 vnode_t a_vp;
558 uio_t a_uio;
559 size_t *a_size;
560 int a_options;
561 vfs_context_t a_context;
562 */
563 {
564 struct vnode *vp = ap->a_vp;
565 struct hfsmount *hfsmp;
566 uio_t uio = ap->a_uio;
567 struct BTreeIterator * iterator = NULL;
568 struct filefork *btfile;
569 struct listattr_callback_state state;
570 int lockflags;
571 int result;
572
573 if (VNODE_IS_RSRC(vp)) {
574 return (EPERM);
575 }
576 hfsmp = VTOHFS(vp);
577 *ap->a_size = 0;
578
579 /* If Finder Info is non-empty then export it. */
580 if (bcmp(VTOC(vp)->c_finderinfo, emptyfinfo, sizeof(emptyfinfo)) != 0) {
581 if (uio == NULL) {
582 *ap->a_size += sizeof(XATTR_FINDERINFO_NAME);
583 } else if (uio_resid(uio) < sizeof(XATTR_FINDERINFO_NAME)) {
584 return (ERANGE);
585 } else {
586 result = uiomove((caddr_t)XATTR_FINDERINFO_NAME,
587 sizeof(XATTR_FINDERINFO_NAME), uio);
588 if (result)
589 return (result);
590 }
591 }
592 /* If Resource Fork is non-empty then export it. */
593 if (vnode_isreg(vp) && RESOURCE_FORK_EXISTS(vp)) {
594 if (uio == NULL) {
595 *ap->a_size += sizeof(XATTR_RESOURCEFORK_NAME);
596 } else if (uio_resid(uio) < sizeof(XATTR_RESOURCEFORK_NAME)) {
597 return (ERANGE);
598 } else {
599 result = uiomove((caddr_t)XATTR_RESOURCEFORK_NAME,
600 sizeof(XATTR_RESOURCEFORK_NAME), uio);
601 if (result)
602 return (result);
603 }
604 }
605 /*
606 * Standard HFS only supports native FinderInfo and Resource Forks.
607 * Return at this point.
608 */
609 if (hfsmp->hfs_flags & HFS_STANDARD) {
610 return (0);
611 }
612 /* Bail if we don't have any extended attributes. */
613 if ((hfsmp->hfs_attribute_vp == NULL) ||
614 (VTOC(vp)->c_attr.ca_recflags & kHFSHasAttributesMask) == 0) {
615 return (0);
616 }
617 btfile = VTOF(hfsmp->hfs_attribute_vp);
618
619 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
620 bzero(iterator, sizeof(*iterator));
621 result = buildkey(VTOC(vp)->c_fileid, NULL, (HFSPlusAttrKey *)&iterator->key);
622 if (result)
623 goto exit;
624
625 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_SHARED_LOCK);
626
627 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
628 if (result && result != btNotFound) {
629 hfs_systemfile_unlock(hfsmp, lockflags);
630 goto exit;
631 }
632
633 state.fileID = VTOC(vp)->c_fileid;
634 state.result = 0;
635 state.uio = uio;
636 state.size = 0;
637
638 /*
639 * Process entries starting just after iterator->key.
640 */
641 result = BTIterateRecords(btfile, kBTreeNextRecord, iterator,
642 (IterateCallBackProcPtr)listattr_callback, &state);
643 hfs_systemfile_unlock(hfsmp, lockflags);
644 if (uio == NULL) {
645 *ap->a_size += state.size;
646 }
647 exit:
648 FREE(iterator, M_TEMP);
649
650 if (state.result || result == btNotFound)
651 result = state.result;
652
653 return MacToVFSError(result);
654 }
655
656
657 /*
658 * Callback - called for each attribute
659 */
660 static int
661 listattr_callback(const HFSPlusAttrKey *key, __unused const HFSPlusAttrData *data, struct listattr_callback_state *state)
662 {
663 char attrname[XATTR_MAXNAMELEN + 1];
664 size_t bytecount;
665 int result;
666
667 if (state->fileID != key->fileID) {
668 state->result = 0;
669 return (0); /* stop */
670 }
671 /*
672 * Skip over non-primary keys
673 */
674 if (key->startBlock != 0) {
675 return (1); /* continue */
676 }
677
678 result = utf8_encodestr(key->attrName, key->attrNameLen * sizeof(UniChar),
679 attrname, &bytecount, sizeof(attrname), 0, 0);
680 if (result) {
681 state->result = result;
682 return (0); /* stop */
683 }
684 bytecount++; /* account for null termination char */
685
686 if (xattr_protected(attrname))
687 return (1); /* continue */
688
689 if (state->uio == NULL) {
690 state->size += bytecount;
691 } else {
692 if (bytecount > uio_resid(state->uio)) {
693 state->result = ERANGE;
694 return (0); /* stop */
695 }
696 result = uiomove((caddr_t) attrname, bytecount, state->uio);
697 if (result) {
698 state->result = result;
699 return (0); /* stop */
700 }
701 }
702 return (1); /* continue */
703 }
704
705
706 /*
707 * Remove all the attributes from a cnode.
708 *
709 * A jornal transaction must be already started.
710 * Attributes b-Tree must have exclusive lock held.
711 */
712 __private_extern__
713 int
714 hfs_removeallattr(struct hfsmount *hfsmp, u_int32_t fileid)
715 {
716 BTreeIterator *next_iterator, *del_iterator;
717 HFSPlusAttrKey *next_key;
718 struct filefork *btfile;
719 int result, iter_result;
720
721 if (hfsmp->hfs_attribute_vp == NULL) {
722 return (0);
723 }
724 btfile = VTOF(hfsmp->hfs_attribute_vp);
725
726 MALLOC(next_iterator, BTreeIterator *, sizeof(BTreeIterator) * 2, M_TEMP, M_WAITOK);
727 bzero(next_iterator, sizeof(BTreeIterator) * 2);
728 del_iterator = &next_iterator[1];
729 next_key = (HFSPlusAttrKey *)&next_iterator->key;
730
731 /*
732 * Go to first possible attribute key/record pair
733 */
734 (void) buildkey(fileid, NULL, next_key);
735 result = BTIterateRecord(btfile, kBTreeNextRecord, next_iterator, NULL, NULL);
736 if (result || next_key->fileID != fileid) {
737 goto exit;
738 }
739 /* Remember iterator of attribute to delete */
740 bcopy(next_iterator, del_iterator, sizeof(BTreeIterator));
741
742 /* Loop until there are no more attributes for this file id */
743 for(;;) {
744 iter_result = BTIterateRecord(btfile, kBTreeNextRecord, next_iterator, NULL, NULL);
745
746 /* XXX need to free and extents for record types 0x20 and 0x30 */
747 result = BTDeleteRecord(btfile, del_iterator);
748 if (result) {
749 goto exit;
750 }
751 if (iter_result) {
752 result = iter_result;
753 break;
754 }
755 if (iter_result || next_key->fileID != fileid) {
756 break; /* end of attributes for this file id */
757 }
758 bcopy(next_iterator, del_iterator, sizeof(BTreeIterator));
759 }
760 exit:
761 (void) BTFlushPath(btfile);
762
763 if (result == btNotFound) {
764 result = 0;
765 }
766 FREE(next_iterator, M_TEMP);
767 return (result);
768 }
769
770 /*
771 * Enable/Disable extended security (ACLs).
772 */
773 __private_extern__
774 int
775 hfs_setextendedsecurity(struct hfsmount *hfsmp, int state)
776 {
777 struct BTreeIterator * iterator = NULL;
778 struct filefork *btfile;
779 int lockflags;
780 int result;
781
782 if (hfsmp->hfs_flags & HFS_STANDARD) {
783 return (ENOTSUP);
784 }
785
786 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
787 bzero(iterator, sizeof(*iterator));
788
789 /*
790 * Build a b-tree key.
791 * We use the root's parent id (1) to hold this volume attribute.
792 */
793 (void) buildkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
794 (HFSPlusAttrKey *)&iterator->key);
795
796 /* Start a transaction for our changes. */
797 if (hfs_start_transaction(hfsmp) != 0) {
798 result = EINVAL;
799 goto exit2;
800 }
801 /*
802 * If there isn't an attributes b-tree then create one.
803 */
804 if (hfsmp->hfs_attribute_vp == NULL) {
805 lockflags = hfs_systemfile_lock(hfsmp, SFL_EXTENTS, HFS_EXCLUSIVE_LOCK);
806 result = hfs_create_attr_btree(hfsmp, ATTRIBUTE_FILE_NODE_SIZE,
807 getnodecount(hfsmp, ATTRIBUTE_FILE_NODE_SIZE));
808 hfs_systemfile_unlock(hfsmp, lockflags);
809 if (result) {
810 goto exit1;
811 }
812 }
813 btfile = VTOF(hfsmp->hfs_attribute_vp);
814
815 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
816
817 if (state == 0) {
818 /* Remove the attribute. */
819 result = BTDeleteRecord(btfile, iterator);
820 if (result == btNotFound)
821 result = 0;
822 } else {
823 FSBufferDescriptor btdata;
824 HFSPlusAttrData attrdata;
825 UInt16 datasize;
826
827 datasize = sizeof(attrdata);
828 btdata.bufferAddress = &attrdata;
829 btdata.itemSize = datasize;
830 btdata.itemCount = 1;
831 attrdata.recordType = kHFSPlusAttrInlineData;
832 attrdata.reserved[0] = 0;
833 attrdata.reserved[1] = 0;
834 attrdata.attrSize = 2;
835 attrdata.attrData[0] = 0;
836 attrdata.attrData[1] = 0;
837
838 /* Insert the attribute. */
839 result = BTInsertRecord(btfile, iterator, &btdata, datasize);
840 if (result == btExists)
841 result = 0;
842 }
843 (void) BTFlushPath(btfile);
844
845 hfs_systemfile_unlock(hfsmp, lockflags);
846 exit1:
847 /* Finish the transaction of our changes. */
848 hfs_end_transaction(hfsmp);
849 exit2:
850 FREE(iterator, M_TEMP);
851
852 if (result == 0) {
853 if (state == 0)
854 vfs_clearextendedsecurity(HFSTOVFS(hfsmp));
855 else
856 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
857 printf("hfs: %s extended security on %s\n",
858 state == 0 ? "disabling" : "enabling", hfsmp->vcbVN);
859 }
860
861 return MacToVFSError(result);
862 }
863
864 /*
865 * Check for extended security (ACLs).
866 */
867 __private_extern__
868 void
869 hfs_checkextendedsecurity(struct hfsmount *hfsmp)
870 {
871 struct BTreeIterator * iterator;
872 struct filefork *btfile;
873 int lockflags;
874 int result;
875
876 if (hfsmp->hfs_flags & HFS_STANDARD ||
877 hfsmp->hfs_attribute_vp == NULL) {
878 return;
879 }
880
881 MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
882 bzero(iterator, sizeof(*iterator));
883
884 /*
885 * Build a b-tree key.
886 * We use the root's parent id (1) to hold this volume attribute.
887 */
888 (void) buildkey(kHFSRootParentID, XATTR_EXTENDEDSECURITY_NAME,
889 (HFSPlusAttrKey *)&iterator->key);
890
891 btfile = VTOF(hfsmp->hfs_attribute_vp);
892
893 lockflags = hfs_systemfile_lock(hfsmp, SFL_ATTRIBUTE, HFS_EXCLUSIVE_LOCK);
894
895 /* Check for our attribute. */
896 result = BTSearchRecord(btfile, iterator, NULL, NULL, NULL);
897
898 hfs_systemfile_unlock(hfsmp, lockflags);
899 FREE(iterator, M_TEMP);
900
901 if (result == 0) {
902 vfs_setextendedsecurity(HFSTOVFS(hfsmp));
903 printf("hfs mount: enabling extended security on %s\n", hfsmp->vcbVN);
904 }
905 }
906
907
908 /*
909 * hfs_attrkeycompare - compare two attribute b-tree keys.
910 *
911 * The name portion of the key is compared using a 16-bit binary comparison.
912 * This is called from the b-tree code.
913 */
914 __private_extern__
915 int
916 hfs_attrkeycompare(HFSPlusAttrKey *searchKey, HFSPlusAttrKey *trialKey)
917 {
918 u_int32_t searchFileID, trialFileID;
919 int result;
920
921 searchFileID = searchKey->fileID;
922 trialFileID = trialKey->fileID;
923 result = 0;
924
925 if (searchFileID > trialFileID) {
926 ++result;
927 } else if (searchFileID < trialFileID) {
928 --result;
929 } else {
930 u_int16_t * str1 = &searchKey->attrName[0];
931 u_int16_t * str2 = &trialKey->attrName[0];
932 int length1 = searchKey->attrNameLen;
933 int length2 = trialKey->attrNameLen;
934 u_int16_t c1, c2;
935 int length;
936
937 if (length1 < length2) {
938 length = length1;
939 --result;
940 } else if (length1 > length2) {
941 length = length2;
942 ++result;
943 } else {
944 length = length1;
945 }
946
947 while (length--) {
948 c1 = *(str1++);
949 c2 = *(str2++);
950
951 if (c1 > c2) {
952 result = 1;
953 break;
954 }
955 if (c1 < c2) {
956 result = -1;
957 break;
958 }
959 }
960 if (result)
961 return (result);
962 /*
963 * Names are equal; compare startBlock
964 */
965 if (searchKey->startBlock == trialKey->startBlock)
966 return (0);
967 else
968 return (searchKey->startBlock < trialKey->startBlock ? -1 : 1);
969 }
970
971 return result;
972 }
973
974
975 /*
976 * buildkey - build an Attribute b-tree key
977 */
978 static int
979 buildkey(u_int32_t fileID, const char *attrname, HFSPlusAttrKey *key)
980 {
981 int result = 0;
982 size_t unicodeBytes = 0;
983
984 if (attrname != NULL) {
985 /*
986 * Convert filename from UTF-8 into Unicode
987 */
988 result = utf8_decodestr(attrname, strlen(attrname), key->attrName,
989 &unicodeBytes, sizeof(key->attrName), 0, 0);
990 if (result) {
991 if (result != ENAMETOOLONG)
992 result = EINVAL; /* name has invalid characters */
993 return (result);
994 }
995 key->attrNameLen = unicodeBytes / sizeof(UniChar);
996 key->keyLength = kHFSPlusAttrKeyMinimumLength + unicodeBytes;
997 } else {
998 key->attrNameLen = 0;
999 key->keyLength = kHFSPlusAttrKeyMinimumLength;
1000 }
1001 key->pad = 0;
1002 key->fileID = fileID;
1003 key->startBlock = 0;
1004
1005 return (0);
1006 }
1007
1008 /*
1009 * getnodecount - calculate starting node count for attributes b-tree.
1010 */
1011 static int
1012 getnodecount(struct hfsmount *hfsmp, size_t nodesize)
1013 {
1014 int avedatasize;
1015 int recpernode;
1016 int count;
1017
1018 avedatasize = sizeof(u_int16_t); /* index slot */
1019 avedatasize += kHFSPlusAttrKeyMinimumLength + HFS_AVERAGE_NAME_SIZE * sizeof(u_int16_t);
1020 avedatasize += sizeof(HFSPlusAttrData) + 32;
1021
1022 recpernode = (nodesize - sizeof(BTNodeDescriptor)) / avedatasize;
1023
1024 count = (hfsmp->hfs_filecount + hfsmp->hfs_dircount) / 8;
1025 count /= recpernode;
1026
1027 /* XXX should also consider volume size XXX */
1028
1029 return (MAX(count, (int)(1024 * 1024) / (int)nodesize));
1030 }
1031
1032
1033 /*
1034 * getmaxinlineattrsize - calculate maximum inline attribute size.
1035 *
1036 * This yields 3,802 bytes for an 8K node size.
1037 */
1038 static size_t
1039 getmaxinlineattrsize(struct vnode * attrvp)
1040 {
1041 struct BTreeInfoRec btinfo;
1042 size_t nodesize = ATTRIBUTE_FILE_NODE_SIZE;
1043 size_t maxsize;
1044
1045 if (attrvp != NULL) {
1046 (void) hfs_lock(VTOC(attrvp), HFS_SHARED_LOCK);
1047 if (BTGetInformation(VTOF(attrvp), 0, &btinfo) == 0)
1048 nodesize = btinfo.nodeSize;
1049 hfs_unlock(VTOC(attrvp));
1050 }
1051 maxsize = nodesize;
1052 maxsize -= sizeof(BTNodeDescriptor); /* minus node descriptor */
1053 maxsize -= 3 * sizeof(UInt16); /* minus 3 index slots */
1054 maxsize /= 2; /* 2 key/rec pairs minumum */
1055 maxsize -= sizeof(HFSPlusAttrKey); /* minus maximum key size */
1056 maxsize -= sizeof(HFSPlusAttrData) - 2; /* minus data header */
1057 maxsize &= 0xFFFFFFFE; /* multiple of 2 bytes */
1058
1059 return (maxsize);
1060 }
1061
1062