]> git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_fileops_handler.c
hfs-522.0.9.tar.gz
[apple/hfs.git] / livefiles_hfs_plugin / lf_hfs_fileops_handler.c
1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
2 *
3 * lf_hfs_fileops_handler.c
4 * livefiles_hfs
5 *
6 * Created by Yakov Ben Zaken on 31/12/2017.
7 */
8
9 #include "lf_hfs_fileops_handler.h"
10 #include "lf_hfs_dirops_handler.h"
11 #include "lf_hfs.h"
12 #include "lf_hfs_utils.h"
13 #include "lf_hfs_vnode.h"
14 #include "lf_hfs_raw_read_write.h"
15 #include "lf_hfs_vnops.h"
16 #include "lf_hfs_xattr.h"
17 #include "lf_hfs_cnode.h"
18 #include "lf_hfs_logger.h"
19 #include "lf_hfs_vfsutils.h"
20 #include "lf_hfs_vfsops.h"
21 #include "lf_hfs_file_extent_mapping.h"
22 #include "lf_hfs_readwrite_ops.h"
23 #include "lf_hfs_file_mgr_internal.h"
24
25
26 int LFHFS_Read ( UVFSFileNode psNode, uint64_t uOffset, size_t iLength, void *pvBuf, size_t *iActuallyRead )
27 {
28 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Read (psNode %p, uOffset %llu, iLength %lu)\n", psNode, uOffset, iLength);
29 VERIFY_NODE_IS_VALID(psNode);
30
31 struct vnode *vp = (vnode_t)psNode;
32 struct cnode *cp;
33 struct filefork *fp;
34 uint64_t filesize;
35 int retval = 0;
36 int took_truncate_lock = 0;
37 *iActuallyRead = 0;
38
39 /* Preflight checks */
40 if (!vnode_isreg(vp)) {
41 /* can only read regular files */
42 return ( vnode_isdir(vp) ? EISDIR : EPERM );
43 }
44
45 cp = VTOC(vp);
46 fp = VTOF(vp);
47
48 /* Protect against a size change. */
49 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
50 took_truncate_lock = 1;
51
52 filesize = fp->ff_size;
53 /*
54 * Check the file size. Note that per POSIX spec, we return 0 at
55 * file EOF, so attempting a read at an offset that is too big
56 * should just return 0 on HFS+. Since the return value was initialized
57 * to 0 above, we just jump to exit. HFS Standard has its own behavior.
58 */
59 if (uOffset > filesize)
60 {
61 LFHFS_LOG( LEVEL_ERROR, "LFHFS_Read: wanted offset is greater then file size\n" );
62 goto exit;
63 }
64
65 // If we asked to read above the file size, adjust the read size;
66 if ( uOffset + iLength > filesize )
67 {
68 iLength = filesize - uOffset;
69 }
70
71 uint64_t uReadStartCluster;
72 retval = raw_readwrite_read( vp, uOffset, pvBuf, iLength, iActuallyRead, &uReadStartCluster );
73
74 cp->c_touch_acctime = TRUE;
75
76 exit:
77 if (took_truncate_lock)
78 {
79 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
80 }
81 return retval;
82 }
83
84
85 int LFHFS_Write ( UVFSFileNode psNode, uint64_t uOffset, size_t iLength, const void *pvBuf, size_t *iActuallyWrite )
86 {
87 #pragma unused (psNode, uOffset, iLength, pvBuf, iActuallyWrite)
88
89 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Write (psNode %p, uOffset %llu, iLength %lu)\n", psNode, uOffset, iLength);
90 VERIFY_NODE_IS_VALID(psNode);
91
92 *iActuallyWrite = 0;
93 struct vnode *vp = (vnode_t)psNode;
94 struct cnode *cp;
95 struct filefork *fp;
96 struct hfsmount *hfsmp;
97 off_t origFileSize;
98 off_t writelimit;
99 off_t bytesToAdd = 0;
100 off_t actualBytesAdded;
101 off_t filebytes;
102 int eflags = kEFReserveMask;
103 int retval = 0;
104 int lockflags;
105 int cnode_locked = 0;
106
107 int took_truncate_lock = 0;
108 size_t iActualLengthToWrite = iLength;
109
110 if (!vnode_isreg(vp))
111 {
112 return ( vnode_isdir(vp) ? EISDIR : EPERM ); /* Can only write regular files */
113 }
114
115 cp = VTOC(vp);
116 fp = VTOF(vp);
117 hfsmp = VTOHFS(vp);
118
119 /*
120 * Protect against a size change.
121 */
122 hfs_lock_truncate(cp, HFS_SHARED_LOCK, HFS_LOCK_DEFAULT);
123 took_truncate_lock = 1;
124
125 origFileSize = fp->ff_size;
126 writelimit = uOffset + iLength;
127
128 /*
129 * We may need an exclusive truncate lock for several reasons, all
130 * of which are because we may be writing to a (portion of a) block
131 * for the first time, and we need to make sure no readers see the
132 * prior, uninitialized contents of the block. The cases are:
133 *
134 * 1. We have unallocated (delayed allocation) blocks. We may be
135 * allocating new blocks to the file and writing to them.
136 * (A more precise check would be whether the range we're writing
137 * to contains delayed allocation blocks.)
138 * 2. We need to extend the file. The bytes between the old EOF
139 * and the new EOF are not yet initialized. This is important
140 * even if we're not allocating new blocks to the file. If the
141 * old EOF and new EOF are in the same block, we still need to
142 * protect that range of bytes until they are written for the
143 * first time.
144 *
145 * If we had a shared lock with the above cases, we need to try to upgrade
146 * to an exclusive lock. If the upgrade fails, we will lose the shared
147 * lock, and will need to take the truncate lock again; the took_truncate_lock
148 * flag will still be set, causing us to try for an exclusive lock next time.
149 */
150 if ((cp->c_truncatelockowner == HFS_SHARED_OWNER) &&
151 ((fp->ff_unallocblocks != 0) ||
152 (writelimit > origFileSize)))
153 {
154 lf_lck_rw_lock_shared_to_exclusive(&cp->c_truncatelock);
155 /* Store the owner in the c_truncatelockowner field if we successfully upgrade */
156 cp->c_truncatelockowner = pthread_self();
157 }
158
159 if ( (retval = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
160 goto exit;
161 }
162 cnode_locked = 1;
163
164 filebytes = blk_to_bytes(fp->ff_blocks, hfsmp->blockSize);
165
166 if ((off_t)uOffset > filebytes
167 && (blk_to_bytes(hfs_freeblks(hfsmp, ISSET(eflags, kEFReserveMask)) , hfsmp->blockSize) < (off_t)uOffset - filebytes))
168 {
169 retval = ENOSPC;
170 goto exit;
171 }
172
173 /* Check if we do not need to extend the file */
174 if (writelimit <= filebytes) {
175 goto sizeok;
176 }
177
178 bytesToAdd = writelimit - filebytes;
179 if (hfs_start_transaction(hfsmp) != 0) {
180 retval = EINVAL;
181 goto exit;
182 }
183
184 while (writelimit > filebytes)
185 {
186 bytesToAdd = writelimit - filebytes;
187
188 /* Protect extents b-tree and allocation bitmap */
189 lockflags = SFL_BITMAP;
190 if (overflow_extents(fp))
191 lockflags |= SFL_EXTENTS;
192 lockflags = hfs_systemfile_lock(hfsmp, lockflags, HFS_EXCLUSIVE_LOCK);
193
194 retval = MacToVFSError(ExtendFileC (hfsmp, (FCB*)fp, bytesToAdd,
195 0, eflags, &actualBytesAdded));
196
197 hfs_systemfile_unlock(hfsmp, lockflags);
198
199 if ((actualBytesAdded == 0) && (retval == E_NONE))
200 retval = ENOSPC;
201 if (retval != E_NONE)
202 break;
203 filebytes = (off_t)fp->ff_blocks * (off_t)hfsmp->blockSize;
204 }
205
206 (void) hfs_update(vp, 0);
207 (void) hfs_volupdate(hfsmp, VOL_UPDATE, 0);
208 (void) hfs_end_transaction(hfsmp);
209
210 /*
211 * If we didn't grow the file enough try a partial write.
212 * POSIX expects this behavior.
213 */
214 if ((retval == ENOSPC) && (filebytes > (off_t)uOffset)) {
215 retval = 0;
216 iActualLengthToWrite -= bytesToAdd;
217 writelimit = filebytes;
218 }
219 sizeok:
220 if (retval == E_NONE) {
221 off_t filesize;
222
223 if (writelimit > fp->ff_size) {
224 filesize = writelimit;
225 struct timeval tv;
226 rl_add(fp->ff_size, writelimit - 1 , &fp->ff_invalidranges);
227 microuptime(&tv);
228 cp->c_zftimeout = (uint32_t)(tv.tv_sec + ZFTIMELIMIT);
229 } else
230 filesize = fp->ff_size;
231
232
233 // Fill last cluster with zeros.
234 if ( origFileSize < (off_t)uOffset )
235 {
236 raw_readwrite_zero_fill_last_block_suffix(vp);
237 }
238
239 if (filesize > fp->ff_size) {
240 fp->ff_new_size = filesize;
241 }
242
243 uint64_t uActuallyWritten;
244 retval = raw_readwrite_write(vp, uOffset, (void*)pvBuf, iActualLengthToWrite, &uActuallyWritten);
245 *iActuallyWrite = uActuallyWritten;
246 if (retval) {
247 fp->ff_new_size = 0; /* no longer extending; use ff_size */
248 goto ioerr_exit;
249 }
250
251 if (filesize > origFileSize) {
252 fp->ff_size = filesize;
253 }
254 fp->ff_new_size = 0; /* ff_size now has the correct size */
255 }
256
257 hfs_flush(hfsmp, HFS_FLUSH_CACHE);
258
259 ioerr_exit:
260 if (!cnode_locked)
261 {
262 hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
263 cnode_locked = 1;
264 }
265
266 if (*iActuallyWrite > 0)
267 {
268 cp->c_flag |= C_MODIFIED;
269 cp->c_touch_chgtime = TRUE;
270 cp->c_touch_modtime = TRUE;
271 hfs_incr_gencount(cp);
272 }
273 if (retval)
274 {
275 (void)hfs_truncate(vp, origFileSize, IO_SYNC, 0);
276 }
277 else if (*iActuallyWrite > 0)
278 {
279 retval = hfs_update(vp, 0);
280 }
281
282 /* Updating vcbWrCnt doesn't need to be atomic. */
283 hfsmp->vcbWrCnt++;
284
285 exit:
286 if (retval && took_truncate_lock
287 && cp->c_truncatelockowner == pthread_self()) {
288 fp->ff_new_size = 0;
289 rl_remove(fp->ff_size, RL_INFINITY, &fp->ff_invalidranges);
290 }
291
292 if (cnode_locked) {
293 hfs_unlock(cp);
294 }
295
296 if (took_truncate_lock) {
297 hfs_unlock_truncate(cp, HFS_LOCK_DEFAULT);
298 }
299
300 return (retval);
301 }
302
303 int LFHFS_Create ( UVFSFileNode psNode, const char *pcName, const UVFSFileAttributes *psAttr, UVFSFileNode *ppsOutNode )
304 {
305 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Create\n");
306 VERIFY_NODE_IS_VALID(psNode);
307
308 int iError = 0;
309 vnode_t psParentVnode = (vnode_t)psNode;
310
311 if (!vnode_isdir(psParentVnode))
312 {
313 iError = ENOTDIR;
314 goto exit;
315 }
316
317 //@param cnp Name information for new directory.
318 struct componentname sNewFileComponentName = {0};
319 sNewFileComponentName.cn_nameptr = (char*) pcName;
320 sNewFileComponentName.cn_namelen = (int) strlen(pcName);
321
322 iError = hfs_vnop_create(psParentVnode, (vnode_t*)ppsOutNode, &sNewFileComponentName, (UVFSFileAttributes *) psAttr);
323 if (iError)
324 goto exit;
325
326 //Since hfs_vnop_create doesn’t allocate clusters for new files.
327 //In case of non-zero given size, we need to call setAttr, after successfully creating the file.
328 if ((psAttr->fa_validmask & UVFS_FA_VALID_SIZE) != 0 && psAttr->fa_size != 0)
329 {
330 iError = hfs_vnop_setattr( (vnode_t) *ppsOutNode, psAttr );
331 //In case of a failure in setAttr, need to remove the created file
332 if (iError)
333 {
334 DIROPS_RemoveInternal(psParentVnode, pcName);
335 LFHFS_Reclaim((vnode_t) *ppsOutNode);
336 }
337 }
338
339 exit:
340 return iError;
341 }
342
343 int LFHFS_GetAttr ( UVFSFileNode psNode, UVFSFileAttributes *psOutAttr )
344 {
345 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_GetAttr\n");
346 VERIFY_NODE_IS_VALID(psNode);
347
348 int iErr = 0;
349 vnode_t vp = (vnode_t)psNode;
350
351 hfs_lock(VTOC(vp),0,0);
352 vnode_GetAttrInternal(vp, psOutAttr);
353 hfs_unlock(VTOC(vp));
354
355 return iErr;
356 }
357
358 int LFHFS_SetAttr ( UVFSFileNode psNode, const UVFSFileAttributes *psSetAttr, UVFSFileAttributes *psOutAttr )
359 {
360 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_SetAttr\n");
361 VERIFY_NODE_IS_VALID(psNode);
362
363 vnode_t psVnode = (vnode_t)psNode;
364
365 int iErr = hfs_vnop_setattr( psVnode, psSetAttr );
366 if ( iErr != 0 )
367 {
368 goto exit;
369 }
370
371 iErr = LFHFS_GetAttr( psNode, psOutAttr );
372
373 exit:
374 return iErr;
375 }
376
377 int LFHFS_Reclaim ( UVFSFileNode psNode )
378 {
379 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Reclaim\n");
380
381 int iErr = 0;
382 vnode_t vp = (vnode_t)psNode;
383
384 if ( psNode != NULL )
385 {
386 VERIFY_NODE_IS_VALID_FOR_RECLAIM(psNode);
387
388 iErr = hfs_vnop_reclaim(vp);
389 psNode = NULL;
390 }
391
392 return iErr;
393 }
394
395 int LFHFS_ReadLink ( UVFSFileNode psNode, void *pvOutBuf, size_t iBufSize, size_t *iActuallyRead, UVFSFileAttributes *psOutAttr )
396 {
397 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_ReadLink\n");
398 VERIFY_NODE_IS_VALID(psNode);
399
400 int iErr = 0;
401 *iActuallyRead = 0;
402 vnode_t vp = (vnode_t)psNode;
403
404 iErr = hfs_vnop_readlink(vp, pvOutBuf, iBufSize, iActuallyRead);
405 if ( iErr != 0 )
406 {
407 goto exit;
408 }
409
410 iErr = LFHFS_GetAttr( psNode, psOutAttr );
411 if ( iErr != 0 )
412 {
413 goto exit;
414 }
415
416 exit:
417 return iErr;
418 }
419
420 int LFHFS_SymLink ( UVFSFileNode psNode, const char *pcName, const char *psContent, const UVFSFileAttributes *psAttr, UVFSFileNode *ppsOutNode )
421 {
422 VERIFY_NODE_IS_VALID(psNode);
423 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_SymLink\n");
424
425 int iErr = 0;
426 vnode_t psParentVnode = (vnode_t)psNode;
427
428 if (!vnode_isdir(psParentVnode))
429 {
430 iErr = ENOTDIR;
431 goto exit;
432 }
433
434 vnode_t psSymLinkVnode = {0};
435 struct componentname sCompName = {0};
436 sCompName.cn_nameiop = CREATE;
437 sCompName.cn_flags = ISLASTCN;
438 sCompName.cn_pnbuf = (char *)pcName;
439 sCompName.cn_pnlen = (int)strlen(pcName);
440 sCompName.cn_nameptr = (char *)pcName;
441 sCompName.cn_namelen = (int)strlen(pcName);
442 sCompName.cn_hash = 0;
443 sCompName.cn_consume = (int)strlen(pcName);
444
445 iErr = hfs_vnop_symlink( psParentVnode, &psSymLinkVnode, &sCompName, (char*)psContent, (UVFSFileAttributes *)psAttr );
446
447 *ppsOutNode = (UVFSFileNode)psSymLinkVnode;
448
449 exit:
450 return iErr;
451 }
452
453 int LFHFS_Rename (UVFSFileNode psFromDirNode, UVFSFileNode psFromNode, const char *pcFromName, UVFSFileNode psToDirNode, UVFSFileNode psToNode, const char *pcToName, uint32_t flags __unused)
454 {
455 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Rename\n");
456
457 VERIFY_NODE_IS_VALID(psFromDirNode);
458 VERIFY_NODE_IS_VALID(psToDirNode);
459 if ( psFromNode != NULL )
460 {
461 VERIFY_NODE_IS_VALID(psFromNode);
462 }
463 if ( psToNode != NULL )
464 {
465 VERIFY_NODE_IS_VALID(psToNode);
466 }
467
468 int iErr = 0;
469 vnode_t psFromParentVnode = (vnode_t)psFromDirNode;
470 vnode_t psToParentVnode = (vnode_t)psToDirNode;
471
472 if (!vnode_isdir(psFromParentVnode) || !vnode_isdir(psToParentVnode))
473 {
474 iErr = ENOTDIR;
475 goto exit;
476 }
477
478 UVFSFileNode psFromFileNode = {0};
479 UVFSFileNode psToFileNode = {0};
480 bool bGotFromNode = (psFromNode != NULL);
481 bool bGotToNode = (psToNode != NULL);
482
483 vnode_t psFromVnode = (vnode_t) psFromNode;
484
485 if (!bGotFromNode)
486 {
487 iErr = DIROPS_LookupInternal( psFromDirNode, pcFromName, &psFromFileNode );
488 if ( iErr != 0 )
489 {
490 goto exit;
491 }
492 psFromVnode = (vnode_t)psFromFileNode;
493 }
494
495 vnode_t psToVnode = psToNode;
496 if (!bGotToNode)
497 {
498 iErr = DIROPS_LookupInternal( psToDirNode, pcToName, &psToFileNode );
499 if ( !iErr )
500 {
501 psToVnode = (vnode_t)psToFileNode;
502 }
503 else if (iErr != ENOENT)
504 {
505 goto exit;
506 }
507 }
508
509 // If only one of the vnodes is of type directory,
510 // we can't allow the rename
511 if (psToVnode)
512 {
513 if (vnode_isdir(psFromVnode) && !vnode_isdir(psToVnode))
514 {
515 iErr = ENOTDIR;
516 goto exit;
517 }
518
519 if (!vnode_isdir(psFromVnode) && vnode_isdir(psToVnode))
520 {
521 iErr = EISDIR;
522 goto exit;
523 }
524 }
525 struct componentname sFromCompName = {0};
526 sFromCompName.cn_nameiop = RENAME;
527 sFromCompName.cn_flags = ISLASTCN;
528 sFromCompName.cn_pnbuf = (char *)pcFromName;
529 sFromCompName.cn_pnlen = (int)strlen(pcFromName);
530 sFromCompName.cn_nameptr = (char *)pcFromName;
531 sFromCompName.cn_namelen = (int)strlen(pcFromName);
532 sFromCompName.cn_hash = 0;
533 sFromCompName.cn_consume = (int)strlen(pcFromName);
534
535 struct componentname sToCompName = {0};
536 sToCompName.cn_nameiop = RENAME;
537 sToCompName.cn_flags = ISLASTCN;
538 sToCompName.cn_pnbuf = (char *)pcToName;
539 sToCompName.cn_pnlen = (int)strlen(pcToName);
540 sToCompName.cn_nameptr = (char *)pcToName;
541 sToCompName.cn_namelen = (int)strlen(pcToName);
542 sToCompName.cn_hash = 0;
543 sToCompName.cn_consume = (int)strlen(pcToName);
544
545 iErr = hfs_vnop_renamex(psFromParentVnode, psFromVnode, &sFromCompName, psToParentVnode, psToVnode, &sToCompName);
546
547 if (!bGotFromNode)
548 LFHFS_Reclaim(psFromVnode);
549 if (!bGotToNode && psToVnode)
550 LFHFS_Reclaim(psToVnode);
551
552 exit:
553 return iErr;
554 }
555
556 int LFHFS_Link ( UVFSFileNode psFromNode, UVFSFileNode psToDirNode, const char *pcToName, UVFSFileAttributes* psOutFileAttrs, UVFSFileAttributes* psOutDirAttrs )
557 {
558 VERIFY_NODE_IS_VALID(psFromNode);
559 VERIFY_NODE_IS_VALID(psToDirNode);
560
561 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Link\n");
562 int iErr = 0;
563
564 vnode_t psFromVnode = (vnode_t)psFromNode;
565 vnode_t psToDirVnode = (vnode_t)psToDirNode;
566
567 if (!vnode_isdir(psToDirVnode))
568 {
569 return ENOTDIR;
570 }
571
572 /* Preflight checks */
573 if (!vnode_isreg(psFromVnode)) {
574 /* can only create hardlinks for regular files */
575 return ( vnode_isdir(psFromVnode) ? EISDIR : EPERM );
576 }
577
578 struct componentname sToCompName = {0};
579 sToCompName.cn_nameiop = CREATE;
580 sToCompName.cn_flags = ISLASTCN;
581 sToCompName.cn_pnbuf = (char *)pcToName;
582 sToCompName.cn_pnlen = (int)strlen(pcToName);
583 sToCompName.cn_nameptr = (char *)pcToName;
584 sToCompName.cn_namelen = (int)strlen(pcToName);
585 sToCompName.cn_hash = 0;
586 sToCompName.cn_consume = (int)strlen(pcToName);
587
588 iErr = hfs_vnop_link(psFromVnode, psToDirVnode, &sToCompName);
589 if ( iErr != 0 )
590 {
591 goto exit;
592 }
593
594 iErr = LFHFS_GetAttr( psFromNode, psOutFileAttrs );
595 if ( iErr != 0 )
596 {
597 LFHFS_LOG(LEVEL_ERROR, "LFHFS_Link: Failed in getting FromNode Attr\n");
598 goto exit;
599 }
600
601 iErr = LFHFS_GetAttr( psToDirNode, psOutDirAttrs );
602 if ( iErr != 0 )
603 {
604 LFHFS_LOG(LEVEL_ERROR, "LFHFS_Link: Failed in getting ToDir Attr\n");
605 goto exit;
606 }
607
608 exit:
609 return iErr;
610 }
611
612 int LFHFS_GetXAttr ( UVFSFileNode psNode, const char *pcAttr, void *pvOutBuf, size_t iBufSize, size_t *iActualSize )
613 {
614 int iErr = 0;
615
616 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_GetXAttr\n");
617
618 VERIFY_NODE_IS_VALID(psNode);
619
620 iErr = hfs_vnop_getxattr((vnode_t)psNode, pcAttr, pvOutBuf, iBufSize, iActualSize);
621
622 return iErr;
623 }
624
625 int LFHFS_SetXAttr ( UVFSFileNode psNode, const char *pcAttr, const void *pvInBuf, size_t iBufSize, UVFSXattrHow How )
626 {
627 int iErr = 0;
628
629 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_SetXAttr\n");
630
631 VERIFY_NODE_IS_VALID(psNode);
632
633 if (How == UVFSXattrHowRemove)
634 {
635 iErr = hfs_vnop_removexattr((vnode_t)psNode, pcAttr);
636 }
637 else
638 {
639 iErr = hfs_vnop_setxattr((vnode_t)psNode, pcAttr, pvInBuf, iBufSize, How);
640 }
641
642 return iErr;
643 }
644
645 int LFHFS_ListXAttr ( UVFSFileNode psNode, void *pvOutBuf, size_t iBufSize, size_t *iActualSize )
646 {
647 int iErr = 0;
648
649 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_ListXAttr\n");
650
651 VERIFY_NODE_IS_VALID(psNode);
652
653 iErr = hfs_vnop_listxattr((vnode_t)psNode, pvOutBuf, iBufSize, iActualSize);
654
655 return iErr;
656 }