1 /* Copyright © 2017-2018 Apple Inc. All rights reserved.
3 * lf_hfs_fsops_handler.c
6 * Created by Yakov Ben Zaken on 31/12/2017.
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 #include "lf_hfs_readwrite_ops.h"
27 #include "lf_hfs_vnops.h"
30 FSOPS_GetRootVnode(struct vnode
* psDevVnode
, struct vnode
** ppsRootVnode
)
32 return (hfs_vfs_root(psDevVnode
->sFSParams
.vnfs_mp
, ppsRootVnode
));
35 //---------------------------------- API Implementation ------------------------------------------
37 uint64_t FSOPS_GetOffsetFromClusterNum(vnode_t vp
, uint64_t uClusterNum
)
39 return (HFSTOVCB(vp
->sFSParams
.vnfs_mp
->psHfsmount
)->hfsPlusIOPosOffset
+ uClusterNum
* HFSTOVCB(vp
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
);
43 LFHFS_Taste ( int iFd
)
45 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Taste %d\n", iFd
);
48 u_int32_t log_blksize
;
49 void* pvBuffer
= NULL
;
51 HFSMasterDirectoryBlock
*psMasterBlock
= hfs_malloc(kMDBSize
);
52 if ( psMasterBlock
== NULL
)
55 LFHFS_LOG(LEVEL_ERROR
, "HFS_Taste: failed to malloc psMasterBlock\n");
59 /* Get the logical block size (treated as physical block size everywhere) */
60 if (ioctl(iFd
, DKIOCGETBLOCKSIZE
, &log_blksize
))
62 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: DKIOCGETBLOCKSIZE failed - setting to default -512\n");
63 log_blksize
= kMDBSize
;
66 if (log_blksize
== 0 || log_blksize
> 1024*1024*1024)
68 LFHFS_LOG(LEVEL_ERROR
, "hfs_mountfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize
);
73 if (log_blksize
> kMDBSize
)
75 pvBuffer
= hfs_malloc(log_blksize
);
76 if ( pvBuffer
== NULL
)
79 LFHFS_LOG(LEVEL_ERROR
, "HFS_Taste: failed to malloc pvBuffer\n");
85 pvBuffer
= (void*) psMasterBlock
;
88 // Read VolumeHeader from offset 1024
89 off_t uVolHdrOffset
= 1024;
90 off_t uBlockNum
= uVolHdrOffset
/ log_blksize
;
91 off_t uOffsetInBlock
= uVolHdrOffset
% log_blksize
;
93 ssize_t iReadBytes
= pread(iFd
, pvBuffer
, log_blksize
, uBlockNum
* log_blksize
);
94 if ( iReadBytes
< uOffsetInBlock
+ kMDBSize
) {
95 iError
= (iReadBytes
< 0) ? errno
: EIO
;
96 LFHFS_LOG(LEVEL_ERROR
, "HFS_Taste: failed to read Master Directory Block with err %d (%ld)\n", iError
, iReadBytes
);
98 if (log_blksize
> kMDBSize
) {
104 if (log_blksize
> kMDBSize
) {
105 memcpy(psMasterBlock
, pvBuffer
+ uOffsetInBlock
, kMDBSize
);
110 uint32_t drSigWord
= SWAP_BE16(psMasterBlock
->drSigWord
);
111 if ((drSigWord
!= kHFSPlusSigWord
) &&
112 (drSigWord
!= kHFSXSigWord
))
115 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Taste: invalid volume signature %d\n", SWAP_BE16(psMasterBlock
->drSigWord
));
121 hfs_free(psMasterBlock
);
126 LFHFS_ScanVols (int iFd
, UVFSScanVolsRequest
*psRequest
, UVFSScanVolsReply
*psReply
)
128 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_ScanVols\n");
130 if ( psRequest
== NULL
|| psReply
== NULL
)
134 else if (psRequest
->sr_volid
> 0)
136 return UVFS_SCANVOLS_EOF_REACHED
;
139 // Tell UVFS that we have a single, non-access controlled volume.
140 psReply
->sr_volid
= 0;
141 psReply
->sr_volac
= UAC_UNLOCKED
;
143 return hfs_ScanVolGetVolName(iFd
, psReply
->sr_volname
);
149 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Init\n");
153 iErr
= LFHFS_LoggerInit();
159 iErr
= raw_readwrite_zero_fill_init();
167 // Initializing Buffer cache
168 lf_hfs_generic_buf_cache_init();
181 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Fini\n");
183 raw_readwrite_zero_fill_de_init();
185 // De-Initializing Buffer cache
186 lf_hfs_generic_buf_cache_deinit();
190 LFHFS_Mount ( int iFd
, UVFSVolumeId puVolId
, UVFSMountFlags puMountFlags
,
191 __unused UVFSVolumeCredential
*psVolumeCreds
, UVFSFileNode
*ppsRootNode
)
193 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Mount %d\n", iFd
);
196 struct mount
* psMount
= hfs_mallocz(sizeof(struct mount
));
197 struct vnode
* psDevVnode
= hfs_mallocz(sizeof(struct vnode
));
198 struct cnode
* psDevCnode
= hfs_mallocz(sizeof(struct cnode
));
199 struct filefork
* psDevFileFork
= hfs_mallocz(sizeof(struct filefork
));
200 FileSystemRecord_s
*psFSRecord
= hfs_mallocz(sizeof(FileSystemRecord_s
));
202 if ( psMount
== NULL
|| psDevVnode
== NULL
|| psDevCnode
== NULL
|| psDevFileFork
== NULL
|| psFSRecord
== NULL
)
205 LFHFS_LOG(LEVEL_ERROR
, "HFS_Mount: failed to malloc initial system files\n");
212 LFHFS_LOG(LEVEL_ERROR
, "HFS_Mount: unknown volume ID\n");
216 psFSRecord
->iFD
= iFd
;
217 psDevVnode
->psFSRecord
= psFSRecord
;
218 psDevVnode
->sFSParams
.vnfs_marksystem
= 1;
219 psDevVnode
->bIsMountVnode
= true;
221 // Initializing inputs for hfs_mount
222 psDevFileFork
->ff_data
.cf_blocks
= 3;
223 psDevFileFork
->ff_data
.cf_extents
[0].blockCount
= 1;
224 psDevFileFork
->ff_data
.cf_extents
[0].startBlock
= 0;
226 psDevVnode
->sFSParams
.vnfs_fsnode
= psDevCnode
;
227 psDevCnode
->c_vp
= psDevVnode
;
228 psDevVnode
->is_rsrc
= false;
229 psDevCnode
->c_datafork
= psDevFileFork
;
230 psDevVnode
->sFSParams
.vnfs_mp
= psMount
;
232 psMount
->mnt_flag
= (puMountFlags
== UVFS_MOUNT_RDONLY
)? MNT_RDONLY
: 0;
233 // Calling to kext hfs_mount
234 iError
= hfs_mount(psMount
, psDevVnode
, 0);
238 struct vnode
* psRootVnode
;
239 // Creating root vnode
240 iError
= FSOPS_GetRootVnode(psDevVnode
,&psRootVnode
);
243 *ppsRootNode
= (UVFSFileNode
) psRootVnode
;
249 hfs_free(psFSRecord
);
253 hfs_free(psDevVnode
);
255 hfs_free(psDevCnode
);
257 hfs_free(psDevFileFork
);
263 LFHFS_Unmount ( UVFSFileNode psRootNode
, UVFSUnmountHint hint
)
265 VERIFY_NODE_IS_VALID(psRootNode
);
266 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Unmount (psRootNode %p) (hint %u)\n", psRootNode
, hint
);
269 struct vnode
*psRootVnode
= (struct vnode
*) psRootNode
;
270 FileSystemRecord_s
*psFSRecord
= VPTOFSRECORD(psRootVnode
);
271 struct mount
*psMount
= psRootVnode
->sFSParams
.vnfs_mp
;
272 struct cnode
*psDevCnode
= VTOHFS(psRootVnode
)->hfs_devvp
->sFSParams
.vnfs_fsnode
;
273 struct hfsmount
*psHfsMp
= psMount
->psHfsmount
;
274 psFSRecord
->uUnmountHint
= hint
;
277 CRASH_ABORT(CRASH_ABORT_ON_UNMOUNT
, psHfsMp
, NULL
);
280 hfs_vnop_reclaim(psRootVnode
);
283 hfs_flushvolumeheader(psHfsMp
, HFS_FVH_SKIP_TRANSACTION
| HFS_FVH_MARK_UNMOUNT
);
286 hfs_unmount(psMount
);
288 hfs_free(psFSRecord
);
290 hfs_free(psDevCnode
->c_datafork
);
291 hfs_free(psDevCnode
);
297 LFHFS_SetFSAttr ( UVFSFileNode psNode
, const char *pcAttr
, const UVFSFSAttributeValue
*psAttrVal
, size_t uLen
, UVFSFSAttributeValue
*psOutAttrVal
, size_t uOutLen
)
299 #pragma unused (psNode, pcAttr, psAttrVal, uLen)
300 VERIFY_NODE_IS_VALID(psNode
);
302 if (pcAttr
== NULL
|| psAttrVal
== NULL
|| psOutAttrVal
== NULL
) return EINVAL
;
304 if (strcmp(pcAttr
, LI_FSATTR_PREALLOCATE
) == 0)
306 if (uLen
< sizeof (UVFSFSAttributeValue
) || uOutLen
< sizeof (UVFSFSAttributeValue
))
309 LIFilePreallocateArgs_t
* psPreAllocReq
= (LIFilePreallocateArgs_t
*) ((void *) psAttrVal
->fsa_opaque
);
310 LIFilePreallocateArgs_t
* psPreAllocRes
= (LIFilePreallocateArgs_t
*) ((void *) psOutAttrVal
->fsa_opaque
);
312 memcpy (psPreAllocRes
, psPreAllocReq
, sizeof(LIFilePreallocateArgs_t
));
313 return hfs_vnop_preallocate(psNode
, psPreAllocReq
, psPreAllocRes
);
320 LFHFS_GetFSAttr ( UVFSFileNode psNode
, const char *pcAttr
, UVFSFSAttributeValue
*psAttrVal
, size_t uLen
, size_t *puRetLen
)
322 VERIFY_NODE_IS_VALID(psNode
);
323 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_GetFSAttr (psNode %p)\n", psNode
);
326 vnode_t psVnode
= (vnode_t
)psNode
;
327 struct hfsmount
*psMount
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
;
329 if (strcmp(pcAttr
, UVFS_FSATTR_PC_LINK_MAX
)==0)
331 *puRetLen
= sizeof(uint64_t);
332 if (uLen
< *puRetLen
)
337 if ( vnode_isreg(psVnode
) )
339 psAttrVal
->fsa_number
= HFS_LINK_MAX
;
343 psAttrVal
->fsa_number
= 1;
348 if (strcmp(pcAttr
, UVFS_FSATTR_PC_NAME_MAX
)==0)
350 *puRetLen
= sizeof(uint64_t);
351 if (uLen
< *puRetLen
)
355 psAttrVal
->fsa_number
= MAXPATHLEN
;
359 if (strcmp(pcAttr
, UVFS_FSATTR_PC_NO_TRUNC
)==0)
361 *puRetLen
= sizeof(bool);
362 if (uLen
< *puRetLen
)
366 psAttrVal
->fsa_bool
= true;
370 if (strcmp(pcAttr
, UVFS_FSATTR_PC_FILESIZEBITS
)==0)
372 // The number of bits used to represent the size (in bytes) of a file
373 *puRetLen
= sizeof(uint64_t);
374 if (uLen
< *puRetLen
)
378 psAttrVal
->fsa_number
= 64;
382 if (strcmp(pcAttr
, UVFS_FSATTR_PC_XATTR_SIZE_BITS
)==0)
384 // The number of bits used to represent the size (in bytes) of an extended attribute.
385 *puRetLen
= sizeof(uint64_t);
386 if (uLen
< *puRetLen
)
390 psAttrVal
->fsa_number
= HFS_XATTR_SIZE_BITS
;
394 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSIZE
)==0)
396 *puRetLen
= sizeof(uint64_t);
397 if (uLen
< *puRetLen
)
401 psAttrVal
->fsa_number
= psMount
->blockSize
;
405 if (strcmp(pcAttr
, UVFS_FSATTR_IOSIZE
)==0)
407 // Size (in bytes) of the optimal transfer block size
408 *puRetLen
= sizeof(uint64_t);
409 if (uLen
< *puRetLen
)
413 psAttrVal
->fsa_number
= 1024*1024*128;
417 if (strcmp(pcAttr
, UVFS_FSATTR_TOTALBLOCKS
)==0)
419 // Total number of file system blocks
420 *puRetLen
= sizeof(uint64_t);
421 if (uLen
< *puRetLen
)
425 psAttrVal
->fsa_number
= psMount
->totalBlocks
;
429 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSFREE
)==0)
431 // Total number of free file system blocks
432 *puRetLen
= sizeof(uint64_t);
433 if (uLen
< *puRetLen
)
437 psAttrVal
->fsa_number
= hfs_freeblks( psMount
, 0 );
441 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSAVAIL
)==0)
443 // Total number of free file system blocks available for allocation to files (in our case - the same as UVFS_FSATTR_BLOCKSFREE)
444 *puRetLen
= sizeof(uint64_t);
445 if (uLen
< *puRetLen
)
449 psAttrVal
->fsa_number
= hfs_freeblks( psMount
, 1 );
453 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSUSED
)==0)
455 // Number of file system blocks currently allocated for some use (TOTAL_BLOCKS - BLOCKSAVAIL)
456 *puRetLen
= sizeof(uint64_t);
457 if (uLen
< *puRetLen
)
461 psAttrVal
->fsa_number
= psMount
->totalBlocks
- hfs_freeblks( psMount
, 1 );
465 if (strcmp(pcAttr
, UVFS_FSATTR_CNAME
)==0)
469 if (IS_ROOT(psVnode
))
472 pcName
= (char*) psVnode
->sFSParams
.vnfs_cnp
->cn_nameptr
;
477 *puRetLen
= strlen(pcName
) + 1;
478 if (uLen
< *puRetLen
)
482 strlcpy(psAttrVal
->fsa_string
, pcName
, *puRetLen
);
486 if (strcmp(pcAttr
, UVFS_FSATTR_FSTYPENAME
)==0)
489 if (uLen
< *puRetLen
)
493 // A string representing the type of file system
494 size_t n
= strlcpy(psAttrVal
->fsa_string
, "HFS", *puRetLen
);
497 *(psAttrVal
->fsa_string
+ (*puRetLen
- 1)) = '\0'; // Must be null terminated
502 if (strcmp(pcAttr
, UVFS_FSATTR_FSSUBTYPE
)==0)
504 #define HFS_PLUS_STR "HFS Plus"
505 #define HFS_PLUS_JOURNALED_STR "HFS Plus (Journaled)"
506 #define HFS_PLUS_CASE_SENS_STR "HFS Plus (Case Sensitive)"
507 #define HFS_PLUS_CASE_SENS_JOURNALED_STR "HFS Plus (Case Sensitive, Journaled)"
509 char* pcFSSubType
= HFS_PLUS_STR
;
510 if ( (psMount
->hfs_flags
& HFS_CASE_SENSITIVE
) && psMount
->jnl
)
512 pcFSSubType
= HFS_PLUS_CASE_SENS_JOURNALED_STR
;
514 else if ( psMount
->hfs_flags
& HFS_CASE_SENSITIVE
)
516 pcFSSubType
= HFS_PLUS_CASE_SENS_STR
;
518 else if ( psMount
->jnl
)
520 pcFSSubType
= HFS_PLUS_JOURNALED_STR
;
523 *puRetLen
= strlen( pcFSSubType
) + 1;
524 if ( uLen
< *puRetLen
)
529 strlcpy( psAttrVal
->fsa_string
, pcFSSubType
, *puRetLen
);
533 if (strcmp(pcAttr
, UVFS_FSATTR_VOLNAME
)==0)
535 *puRetLen
= strlen((char *)psMount
->vcbVN
)+1; // Add 1 for the NULL terminator
536 if (uLen
< *puRetLen
)
540 strlcpy(psAttrVal
->fsa_string
, (char *)psMount
->vcbVN
, *puRetLen
);
544 if (strcmp(pcAttr
, UVFS_FSATTR_VOLUUID
)==0)
546 *puRetLen
= sizeof(uuid_t
);
547 if (uLen
< *puRetLen
)
551 hfs_getvoluuid( psMount
, psAttrVal
->fsa_opaque
);
555 if (strcmp(pcAttr
, UVFS_FSATTR_CAPS_FORMAT
)==0)
557 // A bitmask indicating the capabilities of the volume format
558 *puRetLen
= sizeof(uint64_t);
559 if (uLen
< *puRetLen
)
564 psAttrVal
->fsa_number
=
565 VOL_CAP_FMT_PERSISTENTOBJECTIDS
|
566 VOL_CAP_FMT_SYMBOLICLINKS
|
567 VOL_CAP_FMT_HARDLINKS
|
568 VOL_CAP_FMT_JOURNAL
|
569 (psMount
->jnl
? VOL_CAP_FMT_JOURNAL_ACTIVE
: 0) |
570 (psMount
->hfs_flags
& HFS_CASE_SENSITIVE
? VOL_CAP_FMT_CASE_SENSITIVE
: 0) |
571 VOL_CAP_FMT_CASE_PRESERVING
|
572 VOL_CAP_FMT_2TB_FILESIZE
|
573 VOL_CAP_FMT_HIDDEN_FILES
|
574 /* XXX rdar://problem/48128963 VOL_CAP_FMT_PATH_FROM_ID */ 0;
579 if (strcmp(pcAttr
, UVFS_FSATTR_CAPS_INTERFACES
)==0)
581 // A bitmask indicating the interface capabilities of the file system
582 *puRetLen
= sizeof(uint64_t);
583 if (uLen
< *puRetLen
)
588 psAttrVal
->fsa_number
=
589 #if LF_HFS_NATIVE_SEARCHFS_SUPPORT
590 VOL_CAP_INT_SEARCHFS
|
592 VOL_CAP_INT_EXTENDED_ATTR
;
597 if (strcmp(pcAttr
, UVFS_FSATTR_LAST_MTIME
)==0)
599 // system lsat mounted time
600 *puRetLen
= sizeof(uint64_t);
601 if (uLen
< *puRetLen
)
605 psAttrVal
->fsa_number
= psMount
->hfs_last_mounted_mtime
;
609 if (strcmp(pcAttr
, UVFS_FSATTR_MOUNT_TIME
)==0)
612 *puRetLen
= sizeof(uint64_t);
613 if (uLen
< *puRetLen
)
617 psAttrVal
->fsa_number
= psMount
->hfs_mount_time
;
626 // kHFSVolumeUnmountedMask: this bit is used to indicate whether the volume is dirty (for which fsck needs to run prior to mount) or clean.
627 // For non-journaled volumes:
628 // - Each operation that causes metadata modification clears this bit.
629 // - A Sync operation that takes place after all 'dirtying' operations are completed sets this bit.
630 // Syncronization between the 'dirtying' operations and the Sync is performed by the hfs_global_lock().
631 // For journaled volumes, the volume is considered clean after a journal has been committed to the media.
632 int LFHFS_Sync(UVFSFileNode psNode
) {
633 VERIFY_NODE_IS_VALID(psNode
);
634 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Sync (psNode %p)\n", psNode
);
637 vnode_t psVnode
= (vnode_t
)psNode
;
638 struct hfsmount
*psMount
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
;
639 bool bNeedUnlock
= false;
641 lf_lck_mtx_lock(&psMount
->sync_mutex
);
642 psMount
->hfs_syncer_thread
= pthread_self();
646 hfs_flush(psMount
, HFS_FLUSH_JOURNAL_META
);
650 if (psMount
->hfs_global_lockowner
!= pthread_self()) {
651 hfs_lock_global(psMount
, HFS_EXCLUSIVE_LOCK
);
655 hfs_flushvolumeheader(psMount
, HFS_FVH_SKIP_TRANSACTION
| HFS_FVH_MARK_UNMOUNT
);
658 hfs_unlock_global(psMount
);
662 psMount
->hfs_syncer_thread
= NULL
;
663 lf_lck_mtx_unlock(&psMount
->sync_mutex
);
669 LFHFS_Check( int fdToCheck
, __unused UVFSVolumeId volId
,
670 __unused UVFSVolumeCredential
*volumeCreds
, check_flags_t how
)
672 return fsck_hfs(fdToCheck
, how
);
675 UVFSFSOps HFS_fsOps
= {
676 .fsops_version
= UVFS_FSOPS_VERSION_CURRENT
,
678 .fsops_init
= LFHFS_Init
,
679 .fsops_fini
= LFHFS_Fini
,
681 .fsops_taste
= LFHFS_Taste
,
682 .fsops_scanvols
= LFHFS_ScanVols
,
683 .fsops_mount
= LFHFS_Mount
,
684 .fsops_sync
= LFHFS_Sync
,
685 .fsops_unmount
= LFHFS_Unmount
,
687 .fsops_getfsattr
= LFHFS_GetFSAttr
,
688 .fsops_setfsattr
= LFHFS_SetFSAttr
,
690 .fsops_getattr
= LFHFS_GetAttr
,
691 .fsops_setattr
= LFHFS_SetAttr
,
692 .fsops_lookup
= LFHFS_Lookup
,
693 .fsops_reclaim
= LFHFS_Reclaim
,
694 .fsops_readlink
= LFHFS_ReadLink
,
695 .fsops_read
= LFHFS_Read
,
696 .fsops_write
= LFHFS_Write
,
697 .fsops_create
= LFHFS_Create
,
698 .fsops_mkdir
= LFHFS_MkDir
,
699 .fsops_symlink
= LFHFS_SymLink
,
700 .fsops_remove
= LFHFS_Remove
,
701 .fsops_rmdir
= LFHFS_RmDir
,
702 .fsops_rename
= LFHFS_Rename
,
703 .fsops_readdir
= LFHFS_ReadDir
,
704 .fsops_readdirattr
= LFHFS_ReadDirAttr
,
705 .fsops_link
= LFHFS_Link
,
706 .fsops_check
= LFHFS_Check
,
708 .fsops_getxattr
= LFHFS_GetXAttr
,
709 .fsops_setxattr
= LFHFS_SetXAttr
,
710 .fsops_listxattr
= LFHFS_ListXAttr
,
712 .fsops_scandir
= LFHFS_ScanDir
,
713 .fsops_scanids
= LFHFS_ScanIDs
,
715 .fsops_stream_lookup
= LFHFS_StreamLookup
,
716 .fsops_stream_reclaim
= LFHFS_StreamReclaim
,
717 .fsops_stream_read
= LFHFS_StreamRead
,
721 CrashAbortFunction_FP gpsCrashAbortFunctionArray
[CRASH_ABORT_LAST
] = {0};
724 __attribute__((visibility("default")))
726 livefiles_plugin_init(UVFSFSOps
**ops
)