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