]> git.saurik.com Git - apple/hfs.git/blob - livefiles_hfs_plugin/lf_hfs_fsops_handler.c
hfs-522.0.9.tar.gz
[apple/hfs.git] / livefiles_hfs_plugin / lf_hfs_fsops_handler.c
1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
2 *
3 * lf_hfs_fsops_handler.c
4 * livefiles_hfs
5 *
6 * Created by Yakov Ben Zaken on 31/12/2017.
7 */
8
9 #include <sys/attr.h>
10 #include "lf_hfs.h"
11 #include "lf_hfs_fsops_handler.h"
12 #include "lf_hfs_dirops_handler.h"
13 #include "lf_hfs_fileops_handler.h"
14 #include "lf_hfs_logger.h"
15 #include "lf_hfs_cnode.h"
16 #include "lf_hfs_vnode.h"
17 #include "lf_hfs_endian.h"
18 #include "lf_hfs_vfsops.h"
19 #include "lf_hfs_vfsutils.h"
20 #include "lf_hfs_generic_buf.h"
21 #include "lf_hfs_raw_read_write.h"
22 #include "lf_hfs_journal.h"
23 #include "lf_hfs_vfsops.h"
24 #include "lf_hfs_mount.h"
25
26 static int
27 FSOPS_GetRootVnode(struct vnode* psDevVnode, struct vnode** ppsRootVnode)
28 {
29 return (hfs_vfs_root(psDevVnode->sFSParams.vnfs_mp, ppsRootVnode));
30 }
31
32 //---------------------------------- API Implementation ------------------------------------------
33
34 uint64_t FSOPS_GetOffsetFromClusterNum(vnode_t vp, uint64_t uClusterNum)
35 {
36 return (HFSTOVCB(vp->sFSParams.vnfs_mp->psHfsmount)->hfsPlusIOPosOffset + uClusterNum * HFSTOVCB(vp->sFSParams.vnfs_mp->psHfsmount)->blockSize);
37 }
38
39 int
40 LFHFS_Taste ( int iFd )
41 {
42 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Taste %d\n", iFd);
43
44 int iError = 0;
45 u_int32_t log_blksize;
46 void* pvBuffer = NULL;
47
48 HFSMasterDirectoryBlock *psMasterBlock = hfs_malloc(kMDBSize);
49 if ( psMasterBlock == NULL )
50 {
51 iError = ENOMEM;
52 LFHFS_LOG(LEVEL_ERROR, "HFS_Taste: failed to malloc psMasterBlock\n");
53 goto exit;
54 }
55
56 /* Get the logical block size (treated as physical block size everywhere) */
57 if (ioctl(iFd, DKIOCGETBLOCKSIZE, &log_blksize))
58 {
59 LFHFS_LOG(LEVEL_DEBUG, "hfs_mountfs: DKIOCGETBLOCKSIZE failed - setting to default -512\n");
60 log_blksize = kMDBSize;
61 }
62
63 if (log_blksize == 0 || log_blksize > 1024*1024*1024)
64 {
65 LFHFS_LOG(LEVEL_ERROR, "hfs_mountfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize);
66 iError = ENXIO;
67 goto exit;
68 }
69
70 if (log_blksize > kMDBSize)
71 {
72 pvBuffer = hfs_malloc(log_blksize);
73 if ( pvBuffer == NULL )
74 {
75 iError = ENOMEM;
76 LFHFS_LOG(LEVEL_ERROR, "HFS_Taste: failed to malloc pvBuffer\n");
77 goto exit;
78 }
79 }
80 else
81 {
82 pvBuffer = (void*) psMasterBlock;
83 }
84
85 // Read VolumeHeader from offset 1024
86 off_t uVolHdrOffset = 1024;
87 off_t uBlockNum = uVolHdrOffset / log_blksize;
88 off_t uOffsetInBlock = uVolHdrOffset % log_blksize;
89
90 ssize_t iReadBytes = pread(iFd, pvBuffer, log_blksize, uBlockNum * log_blksize);
91 if ( iReadBytes < uOffsetInBlock + kMDBSize ) {
92 iError = (iReadBytes < 0) ? errno : EIO;
93 LFHFS_LOG(LEVEL_ERROR, "HFS_Taste: failed to read Master Directory Block with err %d (%ld)\n", iError, iReadBytes);
94
95 if (log_blksize > kMDBSize) {
96 hfs_free(pvBuffer);
97 }
98 goto exit;
99 }
100
101 if (log_blksize > kMDBSize) {
102 memcpy(psMasterBlock, pvBuffer + uOffsetInBlock, kMDBSize);
103 hfs_free(pvBuffer);
104 }
105
106 //Validate Signiture
107 uint32_t drSigWord = SWAP_BE16(psMasterBlock->drSigWord);
108 if ((drSigWord != kHFSPlusSigWord) &&
109 (drSigWord != kHFSXSigWord))
110 {
111 iError = EINVAL;
112 LFHFS_LOG(LEVEL_DEBUG, "HFS_Taste: invalid volume signature %d\n", SWAP_BE16(psMasterBlock->drSigWord));
113 goto exit;
114 }
115
116 exit:
117 if (psMasterBlock)
118 hfs_free(psMasterBlock);
119 return iError;
120 }
121
122 int
123 LFHFS_ScanVols (int iFd, UVFSScanVolsRequest *psRequest, UVFSScanVolsReply *psReply )
124 {
125 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_ScanVols\n");
126
127 if ( psRequest == NULL || psReply == NULL )
128 {
129 return EINVAL;
130 }
131 else if (psRequest->sr_volid > 0)
132 {
133 return UVFS_SCANVOLS_EOF_REACHED;
134 }
135
136 // Tell UVFS that we have a single, non-access controlled volume.
137 psReply->sr_volid = 0;
138 psReply->sr_volac = UAC_UNLOCKED;
139
140 return hfs_ScanVolGetVolName(iFd, psReply->sr_volname);
141 }
142
143 int
144 LFHFS_Init ( void )
145 {
146 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Init\n");
147
148 int iErr = 0;
149
150 iErr = LFHFS_LoggerInit();
151 if ( iErr != 0 )
152 {
153 goto exit;
154 }
155
156 iErr = raw_readwrite_zero_fill_init();
157 if ( iErr != 0 )
158 {
159 goto exit;
160 }
161
162 hfs_chashinit();
163
164 // Initializing Buffer cache
165 lf_hfs_generic_buf_cache_init();
166
167 BTReserveSetup();
168
169 journal_init();
170
171 exit:
172 return iErr;
173 }
174
175 void
176 LFHFS_Fini ( void )
177 {
178 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Fini\n");
179
180 raw_readwrite_zero_fill_de_init();
181
182 // De-Initializing Buffer cache
183 lf_hfs_generic_buf_cache_deinit();
184 }
185
186 int
187 LFHFS_Mount ( int iFd, UVFSVolumeId puVolId, UVFSMountFlags puMountFlags,
188 __unused UVFSVolumeCredential *psVolumeCreds, UVFSFileNode *ppsRootNode )
189 {
190 LFHFS_LOG(LEVEL_DEBUG, "HFS_Mount %d\n", iFd);
191 int iError = 0;
192
193 struct mount* psMount = hfs_mallocz(sizeof(struct mount));
194 struct vnode* psDevVnode = hfs_mallocz(sizeof(struct vnode));
195 struct cnode* psDevCnode = hfs_mallocz(sizeof(struct cnode));
196 struct filefork* psDevFileFork = hfs_mallocz(sizeof(struct filefork));
197 FileSystemRecord_s *psFSRecord = hfs_mallocz(sizeof(FileSystemRecord_s));
198
199 if ( psMount == NULL || psDevVnode == NULL || psDevCnode == NULL || psDevFileFork == NULL || psFSRecord == NULL )
200 {
201 iError = ENOMEM;
202 LFHFS_LOG(LEVEL_ERROR, "HFS_Mount: failed to malloc initial system files\n");
203 goto fail;
204 }
205
206 if (puVolId != 0)
207 {
208 iError = EINVAL;
209 LFHFS_LOG(LEVEL_ERROR, "HFS_Mount: unknown volume ID\n");
210 goto fail;
211 }
212
213 psFSRecord->iFD = iFd;
214 psDevVnode->psFSRecord = psFSRecord;
215 psDevVnode->sFSParams.vnfs_marksystem = 1;
216 psDevVnode->bIsMountVnode = true;
217
218 // Initializing inputs for hfs_mount
219 psDevFileFork->ff_data.cf_blocks = 3;
220 psDevFileFork->ff_data.cf_extents[0].blockCount = 1;
221 psDevFileFork->ff_data.cf_extents[0].startBlock = 0;
222
223 psDevVnode->sFSParams.vnfs_fsnode = psDevCnode;
224 psDevCnode->c_vp = psDevVnode;
225 psDevVnode->is_rsrc = false;
226 psDevCnode->c_datafork = psDevFileFork;
227 psDevVnode->sFSParams.vnfs_mp = psMount;
228
229 psMount->mnt_flag = (puMountFlags == UVFS_MOUNT_RDONLY)? MNT_RDONLY : 0;
230 // Calling to kext hfs_mount
231 iError = hfs_mount(psMount, psDevVnode, 0);
232 if (iError)
233 goto fail;
234
235 struct vnode* psRootVnode;
236 // Creating root vnode
237 iError = FSOPS_GetRootVnode(psDevVnode,&psRootVnode);
238 if (iError)
239 goto fail;
240 *ppsRootNode = (UVFSFileNode) psRootVnode;
241
242 goto end;
243
244 fail:
245 if (psFSRecord)
246 hfs_free(psFSRecord);
247 if (psMount)
248 hfs_free(psMount);
249 if (psDevVnode)
250 hfs_free(psDevVnode);
251 if (psDevCnode)
252 hfs_free(psDevCnode);
253 if (psDevFileFork)
254 hfs_free(psDevFileFork);
255 end:
256 return iError;
257 }
258
259 int
260 LFHFS_Unmount ( UVFSFileNode psRootNode, UVFSUnmountHint hint )
261 {
262 VERIFY_NODE_IS_VALID(psRootNode);
263 LFHFS_LOG(LEVEL_DEBUG, "HFS_Unmount (psRootNode %p) (hint %u)\n", psRootNode, hint);
264
265 int iError = 0;
266 struct vnode *psRootVnode = (struct vnode*) psRootNode;
267 FileSystemRecord_s *psFSRecord = VPTOFSRECORD(psRootVnode);
268 struct mount *psMount = psRootVnode->sFSParams.vnfs_mp;
269 struct cnode *psDevCnode = VTOHFS(psRootVnode)->hfs_devvp->sFSParams.vnfs_fsnode;
270 struct hfsmount *psHfsMp = psMount->psHfsmount;
271
272 #if HFS_CRASH_TEST
273 CRASH_ABORT(CRASH_ABORT_ON_UNMOUNT, psHfsMp, NULL);
274 #endif
275
276 hfs_vnop_reclaim(psRootVnode);
277
278 if (!psHfsMp->jnl) {
279 hfs_flushvolumeheader(psHfsMp, HFS_FVH_SKIP_TRANSACTION | HFS_FVH_MARK_UNMOUNT);
280 }
281
282 hfs_unmount(psMount);
283
284 hfs_free(psFSRecord);
285 hfs_free(psMount);
286 hfs_free(psDevCnode->c_datafork);
287 hfs_free(psDevCnode);
288
289 return iError;
290 }
291
292 int
293 LFHFS_SetFSAttr ( UVFSFileNode psNode, const char *pcAttr, const UVFSFSAttributeValue *psAttrVal, size_t uLen )
294 {
295 #pragma unused (psNode, pcAttr, psAttrVal, uLen)
296 VERIFY_NODE_IS_VALID(psNode);
297 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_SetFSAttr (ENOTSUP)\n");
298
299 return ENOTSUP;
300 }
301
302 int
303 LFHFS_GetFSAttr ( UVFSFileNode psNode, const char *pcAttr, UVFSFSAttributeValue *psAttrVal, size_t uLen, size_t *puRetLen )
304 {
305 VERIFY_NODE_IS_VALID(psNode);
306 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_GetFSAttr (psNode %p)\n", psNode);
307
308 int iError = 0;
309 vnode_t psVnode = (vnode_t)psNode;
310 struct hfsmount *psMount = psVnode->sFSParams.vnfs_mp->psHfsmount;
311
312 if (strcmp(pcAttr, UVFS_FSATTR_PC_LINK_MAX)==0)
313 {
314 *puRetLen = sizeof(uint64_t);
315 if (uLen < *puRetLen)
316 {
317 return E2BIG;
318 }
319
320 if ( vnode_isreg(psVnode) )
321 {
322 psAttrVal->fsa_number = HFS_LINK_MAX;
323 }
324 else
325 {
326 psAttrVal->fsa_number = 1;
327 }
328 goto end;
329 }
330
331 if (strcmp(pcAttr, UVFS_FSATTR_PC_NAME_MAX)==0)
332 {
333 *puRetLen = sizeof(uint64_t);
334 if (uLen < *puRetLen)
335 {
336 return E2BIG;
337 }
338 psAttrVal->fsa_number = MAXPATHLEN;
339 goto end;
340 }
341
342 if (strcmp(pcAttr, UVFS_FSATTR_PC_NO_TRUNC)==0)
343 {
344 *puRetLen = sizeof(bool);
345 if (uLen < *puRetLen)
346 {
347 return E2BIG;
348 }
349 psAttrVal->fsa_bool = true;
350 goto end;
351 }
352
353 if (strcmp(pcAttr, UVFS_FSATTR_PC_FILESIZEBITS)==0)
354 {
355 // The number of bits used to represent the size (in bytes) of a file
356 *puRetLen = sizeof(uint64_t);
357 if (uLen < *puRetLen)
358 {
359 return E2BIG;
360 }
361 psAttrVal->fsa_number = 64;
362 goto end;
363 }
364
365 if (strcmp(pcAttr, UVFS_FSATTR_PC_XATTR_SIZE_BITS)==0)
366 {
367 // The number of bits used to represent the size (in bytes) of an extended attribute.
368 *puRetLen = sizeof(uint64_t);
369 if (uLen < *puRetLen)
370 {
371 return E2BIG;
372 }
373 psAttrVal->fsa_number = HFS_XATTR_SIZE_BITS;
374 goto end;
375 }
376
377 if (strcmp(pcAttr, UVFS_FSATTR_BLOCKSIZE)==0)
378 {
379 *puRetLen = sizeof(uint64_t);
380 if (uLen < *puRetLen)
381 {
382 return E2BIG;
383 }
384 psAttrVal->fsa_number = psMount->blockSize;
385 goto end;
386 }
387
388 if (strcmp(pcAttr, UVFS_FSATTR_IOSIZE)==0)
389 {
390 // Size (in bytes) of the optimal transfer block size
391 *puRetLen = sizeof(uint64_t);
392 if (uLen < *puRetLen)
393 {
394 return E2BIG;
395 }
396 psAttrVal->fsa_number = 1024*1024*128;
397 goto end;
398 }
399
400 if (strcmp(pcAttr, UVFS_FSATTR_TOTALBLOCKS)==0)
401 {
402 // Total number of file system blocks
403 *puRetLen = sizeof(uint64_t);
404 if (uLen < *puRetLen)
405 {
406 return E2BIG;
407 }
408 psAttrVal->fsa_number = psMount->totalBlocks;
409 goto end;
410 }
411
412 if (strcmp(pcAttr, UVFS_FSATTR_BLOCKSFREE)==0)
413 {
414 // Total number of free file system blocks
415 *puRetLen = sizeof(uint64_t);
416 if (uLen < *puRetLen)
417 {
418 return E2BIG;
419 }
420 psAttrVal->fsa_number = hfs_freeblks( psMount, 0 );
421 goto end;
422 }
423
424 if (strcmp(pcAttr, UVFS_FSATTR_BLOCKSAVAIL)==0)
425 {
426 // Total number of free file system blocks available for allocation to files (in our case - the same as UVFS_FSATTR_BLOCKSFREE)
427 *puRetLen = sizeof(uint64_t);
428 if (uLen < *puRetLen)
429 {
430 return E2BIG;
431 }
432 psAttrVal->fsa_number = hfs_freeblks( psMount, 1 );
433 goto end;
434 }
435
436 if (strcmp(pcAttr, UVFS_FSATTR_BLOCKSUSED)==0)
437 {
438 // Number of file system blocks currently allocated for some use (TOTAL_BLOCKS - BLOCKSAVAIL)
439 *puRetLen = sizeof(uint64_t);
440 if (uLen < *puRetLen)
441 {
442 return E2BIG;
443 }
444 psAttrVal->fsa_number = psMount->totalBlocks - hfs_freeblks( psMount, 1 );
445 goto end;
446 }
447
448 if (strcmp(pcAttr, UVFS_FSATTR_CNAME)==0)
449 {
450 char* pcName;
451 //The file name
452 if (IS_ROOT(psVnode))
453 pcName = "";
454 else
455 pcName = (char*) psVnode->sFSParams.vnfs_cnp->cn_nameptr;
456
457 if (pcName == NULL)
458 return EINVAL;
459
460 *puRetLen = strlen(pcName) + 1;
461 if (uLen < *puRetLen)
462 {
463 return E2BIG;
464 }
465 strlcpy(psAttrVal->fsa_string, pcName, *puRetLen);
466 return 0;
467 }
468
469 if (strcmp(pcAttr, UVFS_FSATTR_FSTYPENAME)==0)
470 {
471 *puRetLen = 4;
472 if (uLen < *puRetLen)
473 {
474 return E2BIG;
475 }
476 // A string representing the type of file system
477 strcpy(psAttrVal->fsa_string, "HFS");
478 *(psAttrVal->fsa_string+3) = 0; // Must be null terminated
479 goto end;
480 }
481
482 if (strcmp(pcAttr, UVFS_FSATTR_FSSUBTYPE)==0)
483 {
484 #define HFS_PLUS_STR "HFS Plus"
485 #define HFS_PLUS_JOURNALED_STR "HFS Plus (Journaled)"
486 #define HFS_PLUS_CASE_SENS_STR "HFS Plus (Case Sensitive)"
487 #define HFS_PLUS_CASE_SENS_JOURNALED_STR "HFS Plus (Case Sensitive, Journaled)"
488
489 char* pcFSSubType = HFS_PLUS_STR;
490 if ( (psMount->hfs_flags & HFS_CASE_SENSITIVE) && psMount->jnl )
491 {
492 pcFSSubType = HFS_PLUS_CASE_SENS_JOURNALED_STR;
493 }
494 else if ( psMount->hfs_flags & HFS_CASE_SENSITIVE )
495 {
496 pcFSSubType = HFS_PLUS_CASE_SENS_STR;
497 }
498 else if ( psMount->jnl )
499 {
500 pcFSSubType = HFS_PLUS_JOURNALED_STR;
501 }
502
503 *puRetLen = strlen( pcFSSubType ) + 1;
504 if ( uLen < *puRetLen )
505 {
506 return E2BIG;
507 }
508
509 strcpy( psAttrVal->fsa_string, pcFSSubType );
510 goto end;
511 }
512
513 if (strcmp(pcAttr, UVFS_FSATTR_VOLNAME)==0)
514 {
515 *puRetLen = strlen((char *)psMount->vcbVN)+1; // Add 1 for the NULL terminator
516 if (uLen < *puRetLen)
517 {
518 return E2BIG;
519 }
520 strcpy(psAttrVal->fsa_string, (char *)psMount->vcbVN);
521 goto end;
522 }
523
524 if (strcmp(pcAttr, UVFS_FSATTR_VOLUUID)==0)
525 {
526 *puRetLen = sizeof(uuid_t);
527 if (uLen < *puRetLen)
528 {
529 return E2BIG;
530 }
531 hfs_getvoluuid( psMount, psAttrVal->fsa_opaque );
532 goto end;
533 }
534
535 if (strcmp(pcAttr, UVFS_FSATTR_CAPS_FORMAT)==0)
536 {
537 // A bitmask indicating the capabilities of the volume format
538 *puRetLen = sizeof(uint64_t);
539 if (uLen < *puRetLen)
540 {
541 return E2BIG;
542 }
543
544 psAttrVal->fsa_number =
545 VOL_CAP_FMT_PERSISTENTOBJECTIDS |
546 VOL_CAP_FMT_SYMBOLICLINKS |
547 VOL_CAP_FMT_HARDLINKS |
548 VOL_CAP_FMT_JOURNAL |
549 (psMount->jnl ? VOL_CAP_FMT_JOURNAL_ACTIVE : 0) |
550 (psMount->hfs_flags & HFS_CASE_SENSITIVE ? VOL_CAP_FMT_CASE_SENSITIVE : 0) |
551 VOL_CAP_FMT_CASE_PRESERVING |
552 VOL_CAP_FMT_2TB_FILESIZE |
553 VOL_CAP_FMT_HIDDEN_FILES |
554 /* XXX rdar://problem/48128963 VOL_CAP_FMT_PATH_FROM_ID */ 0;
555
556 goto end;
557 }
558
559 if (strcmp(pcAttr, UVFS_FSATTR_CAPS_INTERFACES)==0)
560 {
561 // A bitmask indicating the interface capabilities of the file system
562 *puRetLen = sizeof(uint64_t);
563 if (uLen < *puRetLen)
564 {
565 return E2BIG;
566 }
567
568 psAttrVal->fsa_number =
569 #if LF_HFS_NATIVE_SEARCHFS_SUPPORT
570 VOL_CAP_INT_SEARCHFS |
571 #endif
572 VOL_CAP_INT_EXTENDED_ATTR;
573
574 goto end;
575 }
576
577 if (strcmp(pcAttr, UVFS_FSATTR_LAST_MTIME)==0)
578 {
579 // system lsat mounted time
580 *puRetLen = sizeof(uint64_t);
581 if (uLen < *puRetLen)
582 {
583 return E2BIG;
584 }
585 psAttrVal->fsa_number = psMount->hfs_last_mounted_mtime;
586 goto end;
587 }
588
589 if (strcmp(pcAttr, UVFS_FSATTR_MOUNT_TIME)==0)
590 {
591 // system mount time
592 *puRetLen = sizeof(uint64_t);
593 if (uLen < *puRetLen)
594 {
595 return E2BIG;
596 }
597 psAttrVal->fsa_number = psMount->hfs_mount_time;
598 goto end;
599 }
600
601 iError = ENOTSUP;
602 end:
603 return iError;
604 }
605
606 // kHFSVolumeUnmountedMask: this bit is used to indicate whether the volume is dirty (for which fsck needs to run prior to mount) or clean.
607 // For non-journaled volumes:
608 // - Each operation that causes metadata modification clears this bit.
609 // - A Sync operation that takes place after all 'dirtying' operations are completed sets this bit.
610 // Syncronization between the 'dirtying' operations and the Sync is performed by the hfs_global_lock().
611 // For journaled volumes, the volume is considered clean after a journal has been committed to the media.
612 int LFHFS_Sync(UVFSFileNode psNode) {
613 VERIFY_NODE_IS_VALID(psNode);
614 LFHFS_LOG(LEVEL_DEBUG, "LFHFS_Sync (psNode %p)\n", psNode);
615
616 int iErr = 0;
617 vnode_t psVnode = (vnode_t)psNode;
618 struct hfsmount *psMount = psVnode->sFSParams.vnfs_mp->psHfsmount;
619 bool bNeedUnlock = false;
620
621 lf_lck_mtx_lock(&psMount->sync_mutex);
622 psMount->hfs_syncer_thread = pthread_self();
623
624 if (psMount->jnl) {
625
626 hfs_flush(psMount, HFS_FLUSH_JOURNAL_META);
627
628 } else {
629
630 if (psMount->hfs_global_lockowner != pthread_self()) {
631 hfs_lock_global(psMount, HFS_EXCLUSIVE_LOCK);
632 bNeedUnlock = true;
633 }
634
635 hfs_flushvolumeheader(psMount, HFS_FVH_SKIP_TRANSACTION | HFS_FVH_MARK_UNMOUNT);
636
637 if (bNeedUnlock) {
638 hfs_unlock_global(psMount);
639 }
640 }
641
642 psMount->hfs_syncer_thread = NULL;
643 lf_lck_mtx_unlock(&psMount->sync_mutex);
644
645 return(iErr);
646 }
647
648 int
649 LFHFS_Check( int fdToCheck , __unused UVFSVolumeId volId,
650 __unused UVFSVolumeCredential *volumeCreds, check_flags_t how )
651 {
652 return fsck_hfs(fdToCheck, how);
653 }
654
655 UVFSFSOps HFS_fsOps = {
656 .fsops_version = UVFS_FSOPS_VERSION_CURRENT,
657
658 .fsops_init = LFHFS_Init,
659 .fsops_fini = LFHFS_Fini,
660
661 .fsops_taste = LFHFS_Taste,
662 .fsops_scanvols = LFHFS_ScanVols,
663 .fsops_mount = LFHFS_Mount,
664 .fsops_sync = LFHFS_Sync,
665 .fsops_unmount = LFHFS_Unmount,
666
667 .fsops_getfsattr = LFHFS_GetFSAttr,
668 .fsops_setfsattr = LFHFS_SetFSAttr,
669
670 .fsops_getattr = LFHFS_GetAttr,
671 .fsops_setattr = LFHFS_SetAttr,
672 .fsops_lookup = LFHFS_Lookup,
673 .fsops_reclaim = LFHFS_Reclaim,
674 .fsops_readlink = LFHFS_ReadLink,
675 .fsops_read = LFHFS_Read,
676 .fsops_write = LFHFS_Write,
677 .fsops_create = LFHFS_Create,
678 .fsops_mkdir = LFHFS_MkDir,
679 .fsops_symlink = LFHFS_SymLink,
680 .fsops_remove = LFHFS_Remove,
681 .fsops_rmdir = LFHFS_RmDir,
682 .fsops_rename = LFHFS_Rename,
683 .fsops_readdir = LFHFS_ReadDir,
684 .fsops_readdirattr = LFHFS_ReadDirAttr,
685 .fsops_link = LFHFS_Link,
686 .fsops_check = LFHFS_Check,
687
688 .fsops_getxattr = LFHFS_GetXAttr,
689 .fsops_setxattr = LFHFS_SetXAttr,
690 .fsops_listxattr = LFHFS_ListXAttr,
691
692 .fsops_scandir = LFHFS_ScanDir,
693 .fsops_scanids = LFHFS_ScanIDs
694 };
695
696 #if HFS_CRASH_TEST
697 CrashAbortFunction_FP gpsCrashAbortFunctionArray[CRASH_ABORT_LAST] = {0};
698 #endif
699
700 __attribute__((visibility("default")))
701 void
702 livefiles_plugin_init(UVFSFSOps **ops)
703 {
704 if (ops) {
705 *ops = &HFS_fsOps;
706 }
707
708 return;
709 }