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"
27 FSOPS_GetRootVnode(struct vnode
* psDevVnode
, struct vnode
** ppsRootVnode
)
29 return (hfs_vfs_root(psDevVnode
->sFSParams
.vnfs_mp
, ppsRootVnode
));
32 //---------------------------------- API Implementation ------------------------------------------
34 uint64_t FSOPS_GetOffsetFromClusterNum(vnode_t vp
, uint64_t uClusterNum
)
36 return (HFSTOVCB(vp
->sFSParams
.vnfs_mp
->psHfsmount
)->hfsPlusIOPosOffset
+ uClusterNum
* HFSTOVCB(vp
->sFSParams
.vnfs_mp
->psHfsmount
)->blockSize
);
40 LFHFS_Taste ( int iFd
)
42 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Taste %d\n", iFd
);
45 u_int32_t log_blksize
;
46 void* pvBuffer
= NULL
;
48 HFSMasterDirectoryBlock
*psMasterBlock
= hfs_malloc(kMDBSize
);
49 if ( psMasterBlock
== NULL
)
52 LFHFS_LOG(LEVEL_ERROR
, "HFS_Taste: failed to malloc psMasterBlock\n");
56 /* Get the logical block size (treated as physical block size everywhere) */
57 if (ioctl(iFd
, DKIOCGETBLOCKSIZE
, &log_blksize
))
59 LFHFS_LOG(LEVEL_DEBUG
, "hfs_mountfs: DKIOCGETBLOCKSIZE failed - setting to default -512\n");
60 log_blksize
= kMDBSize
;
63 if (log_blksize
== 0 || log_blksize
> 1024*1024*1024)
65 LFHFS_LOG(LEVEL_ERROR
, "hfs_mountfs: logical block size 0x%x looks bad. Not mounting.\n", log_blksize
);
70 if (log_blksize
> kMDBSize
)
72 pvBuffer
= hfs_malloc(log_blksize
);
73 if ( pvBuffer
== NULL
)
76 LFHFS_LOG(LEVEL_ERROR
, "HFS_Taste: failed to malloc pvBuffer\n");
82 pvBuffer
= (void*) psMasterBlock
;
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
;
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
);
95 if (log_blksize
> kMDBSize
) {
101 if (log_blksize
> kMDBSize
) {
102 memcpy(psMasterBlock
, pvBuffer
+ uOffsetInBlock
, kMDBSize
);
107 uint32_t drSigWord
= SWAP_BE16(psMasterBlock
->drSigWord
);
108 if ((drSigWord
!= kHFSPlusSigWord
) &&
109 (drSigWord
!= kHFSXSigWord
))
112 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Taste: invalid volume signature %d\n", SWAP_BE16(psMasterBlock
->drSigWord
));
118 hfs_free(psMasterBlock
);
123 LFHFS_ScanVols (int iFd
, UVFSScanVolsRequest
*psRequest
, UVFSScanVolsReply
*psReply
)
125 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_ScanVols\n");
127 if ( psRequest
== NULL
|| psReply
== NULL
)
131 else if (psRequest
->sr_volid
> 0)
133 return UVFS_SCANVOLS_EOF_REACHED
;
136 // Tell UVFS that we have a single, non-access controlled volume.
137 psReply
->sr_volid
= 0;
138 psReply
->sr_volac
= UAC_UNLOCKED
;
140 return hfs_ScanVolGetVolName(iFd
, psReply
->sr_volname
);
146 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Init\n");
150 iErr
= LFHFS_LoggerInit();
156 iErr
= raw_readwrite_zero_fill_init();
164 // Initializing Buffer cache
165 lf_hfs_generic_buf_cache_init();
178 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_Fini\n");
180 raw_readwrite_zero_fill_de_init();
182 // De-Initializing Buffer cache
183 lf_hfs_generic_buf_cache_deinit();
187 LFHFS_Mount ( int iFd
, UVFSVolumeId puVolId
, UVFSMountFlags puMountFlags
,
188 __unused UVFSVolumeCredential
*psVolumeCreds
, UVFSFileNode
*ppsRootNode
)
190 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Mount %d\n", iFd
);
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
));
199 if ( psMount
== NULL
|| psDevVnode
== NULL
|| psDevCnode
== NULL
|| psDevFileFork
== NULL
|| psFSRecord
== NULL
)
202 LFHFS_LOG(LEVEL_ERROR
, "HFS_Mount: failed to malloc initial system files\n");
209 LFHFS_LOG(LEVEL_ERROR
, "HFS_Mount: unknown volume ID\n");
213 psFSRecord
->iFD
= iFd
;
214 psDevVnode
->psFSRecord
= psFSRecord
;
215 psDevVnode
->sFSParams
.vnfs_marksystem
= 1;
216 psDevVnode
->bIsMountVnode
= true;
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;
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
;
229 psMount
->mnt_flag
= (puMountFlags
== UVFS_MOUNT_RDONLY
)? MNT_RDONLY
: 0;
230 // Calling to kext hfs_mount
231 iError
= hfs_mount(psMount
, psDevVnode
, 0);
235 struct vnode
* psRootVnode
;
236 // Creating root vnode
237 iError
= FSOPS_GetRootVnode(psDevVnode
,&psRootVnode
);
240 *ppsRootNode
= (UVFSFileNode
) psRootVnode
;
246 hfs_free(psFSRecord
);
250 hfs_free(psDevVnode
);
252 hfs_free(psDevCnode
);
254 hfs_free(psDevFileFork
);
260 LFHFS_Unmount ( UVFSFileNode psRootNode
, UVFSUnmountHint hint
)
262 VERIFY_NODE_IS_VALID(psRootNode
);
263 LFHFS_LOG(LEVEL_DEBUG
, "HFS_Unmount (psRootNode %p) (hint %u)\n", psRootNode
, hint
);
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
;
273 CRASH_ABORT(CRASH_ABORT_ON_UNMOUNT
, psHfsMp
, NULL
);
276 hfs_vnop_reclaim(psRootVnode
);
279 hfs_flushvolumeheader(psHfsMp
, HFS_FVH_SKIP_TRANSACTION
| HFS_FVH_MARK_UNMOUNT
);
282 hfs_unmount(psMount
);
284 hfs_free(psFSRecord
);
286 hfs_free(psDevCnode
->c_datafork
);
287 hfs_free(psDevCnode
);
293 LFHFS_SetFSAttr ( UVFSFileNode psNode
, const char *pcAttr
, const UVFSFSAttributeValue
*psAttrVal
, size_t uLen
)
295 #pragma unused (psNode, pcAttr, psAttrVal, uLen)
296 VERIFY_NODE_IS_VALID(psNode
);
297 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_SetFSAttr (ENOTSUP)\n");
303 LFHFS_GetFSAttr ( UVFSFileNode psNode
, const char *pcAttr
, UVFSFSAttributeValue
*psAttrVal
, size_t uLen
, size_t *puRetLen
)
305 VERIFY_NODE_IS_VALID(psNode
);
306 LFHFS_LOG(LEVEL_DEBUG
, "LFHFS_GetFSAttr (psNode %p)\n", psNode
);
309 vnode_t psVnode
= (vnode_t
)psNode
;
310 struct hfsmount
*psMount
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
;
312 if (strcmp(pcAttr
, UVFS_FSATTR_PC_LINK_MAX
)==0)
314 *puRetLen
= sizeof(uint64_t);
315 if (uLen
< *puRetLen
)
320 if ( vnode_isreg(psVnode
) )
322 psAttrVal
->fsa_number
= HFS_LINK_MAX
;
326 psAttrVal
->fsa_number
= 1;
331 if (strcmp(pcAttr
, UVFS_FSATTR_PC_NAME_MAX
)==0)
333 *puRetLen
= sizeof(uint64_t);
334 if (uLen
< *puRetLen
)
338 psAttrVal
->fsa_number
= MAXPATHLEN
;
342 if (strcmp(pcAttr
, UVFS_FSATTR_PC_NO_TRUNC
)==0)
344 *puRetLen
= sizeof(bool);
345 if (uLen
< *puRetLen
)
349 psAttrVal
->fsa_bool
= true;
353 if (strcmp(pcAttr
, UVFS_FSATTR_PC_FILESIZEBITS
)==0)
355 // The number of bits used to represent the size (in bytes) of a file
356 *puRetLen
= sizeof(uint64_t);
357 if (uLen
< *puRetLen
)
361 psAttrVal
->fsa_number
= 64;
365 if (strcmp(pcAttr
, UVFS_FSATTR_PC_XATTR_SIZE_BITS
)==0)
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
)
373 psAttrVal
->fsa_number
= HFS_XATTR_SIZE_BITS
;
377 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSIZE
)==0)
379 *puRetLen
= sizeof(uint64_t);
380 if (uLen
< *puRetLen
)
384 psAttrVal
->fsa_number
= psMount
->blockSize
;
388 if (strcmp(pcAttr
, UVFS_FSATTR_IOSIZE
)==0)
390 // Size (in bytes) of the optimal transfer block size
391 *puRetLen
= sizeof(uint64_t);
392 if (uLen
< *puRetLen
)
396 psAttrVal
->fsa_number
= 1024*1024*128;
400 if (strcmp(pcAttr
, UVFS_FSATTR_TOTALBLOCKS
)==0)
402 // Total number of file system blocks
403 *puRetLen
= sizeof(uint64_t);
404 if (uLen
< *puRetLen
)
408 psAttrVal
->fsa_number
= psMount
->totalBlocks
;
412 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSFREE
)==0)
414 // Total number of free file system blocks
415 *puRetLen
= sizeof(uint64_t);
416 if (uLen
< *puRetLen
)
420 psAttrVal
->fsa_number
= hfs_freeblks( psMount
, 0 );
424 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSAVAIL
)==0)
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
)
432 psAttrVal
->fsa_number
= hfs_freeblks( psMount
, 1 );
436 if (strcmp(pcAttr
, UVFS_FSATTR_BLOCKSUSED
)==0)
438 // Number of file system blocks currently allocated for some use (TOTAL_BLOCKS - BLOCKSAVAIL)
439 *puRetLen
= sizeof(uint64_t);
440 if (uLen
< *puRetLen
)
444 psAttrVal
->fsa_number
= psMount
->totalBlocks
- hfs_freeblks( psMount
, 1 );
448 if (strcmp(pcAttr
, UVFS_FSATTR_CNAME
)==0)
452 if (IS_ROOT(psVnode
))
455 pcName
= (char*) psVnode
->sFSParams
.vnfs_cnp
->cn_nameptr
;
460 *puRetLen
= strlen(pcName
) + 1;
461 if (uLen
< *puRetLen
)
465 strlcpy(psAttrVal
->fsa_string
, pcName
, *puRetLen
);
469 if (strcmp(pcAttr
, UVFS_FSATTR_FSTYPENAME
)==0)
472 if (uLen
< *puRetLen
)
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
482 if (strcmp(pcAttr
, UVFS_FSATTR_FSSUBTYPE
)==0)
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)"
489 char* pcFSSubType
= HFS_PLUS_STR
;
490 if ( (psMount
->hfs_flags
& HFS_CASE_SENSITIVE
) && psMount
->jnl
)
492 pcFSSubType
= HFS_PLUS_CASE_SENS_JOURNALED_STR
;
494 else if ( psMount
->hfs_flags
& HFS_CASE_SENSITIVE
)
496 pcFSSubType
= HFS_PLUS_CASE_SENS_STR
;
498 else if ( psMount
->jnl
)
500 pcFSSubType
= HFS_PLUS_JOURNALED_STR
;
503 *puRetLen
= strlen( pcFSSubType
) + 1;
504 if ( uLen
< *puRetLen
)
509 strcpy( psAttrVal
->fsa_string
, pcFSSubType
);
513 if (strcmp(pcAttr
, UVFS_FSATTR_VOLNAME
)==0)
515 *puRetLen
= strlen((char *)psMount
->vcbVN
)+1; // Add 1 for the NULL terminator
516 if (uLen
< *puRetLen
)
520 strcpy(psAttrVal
->fsa_string
, (char *)psMount
->vcbVN
);
524 if (strcmp(pcAttr
, UVFS_FSATTR_VOLUUID
)==0)
526 *puRetLen
= sizeof(uuid_t
);
527 if (uLen
< *puRetLen
)
531 hfs_getvoluuid( psMount
, psAttrVal
->fsa_opaque
);
535 if (strcmp(pcAttr
, UVFS_FSATTR_CAPS_FORMAT
)==0)
537 // A bitmask indicating the capabilities of the volume format
538 *puRetLen
= sizeof(uint64_t);
539 if (uLen
< *puRetLen
)
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;
559 if (strcmp(pcAttr
, UVFS_FSATTR_CAPS_INTERFACES
)==0)
561 // A bitmask indicating the interface capabilities of the file system
562 *puRetLen
= sizeof(uint64_t);
563 if (uLen
< *puRetLen
)
568 psAttrVal
->fsa_number
=
569 #if LF_HFS_NATIVE_SEARCHFS_SUPPORT
570 VOL_CAP_INT_SEARCHFS
|
572 VOL_CAP_INT_EXTENDED_ATTR
;
577 if (strcmp(pcAttr
, UVFS_FSATTR_LAST_MTIME
)==0)
579 // system lsat mounted time
580 *puRetLen
= sizeof(uint64_t);
581 if (uLen
< *puRetLen
)
585 psAttrVal
->fsa_number
= psMount
->hfs_last_mounted_mtime
;
589 if (strcmp(pcAttr
, UVFS_FSATTR_MOUNT_TIME
)==0)
592 *puRetLen
= sizeof(uint64_t);
593 if (uLen
< *puRetLen
)
597 psAttrVal
->fsa_number
= psMount
->hfs_mount_time
;
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
);
617 vnode_t psVnode
= (vnode_t
)psNode
;
618 struct hfsmount
*psMount
= psVnode
->sFSParams
.vnfs_mp
->psHfsmount
;
619 bool bNeedUnlock
= false;
621 lf_lck_mtx_lock(&psMount
->sync_mutex
);
622 psMount
->hfs_syncer_thread
= pthread_self();
626 hfs_flush(psMount
, HFS_FLUSH_JOURNAL_META
);
630 if (psMount
->hfs_global_lockowner
!= pthread_self()) {
631 hfs_lock_global(psMount
, HFS_EXCLUSIVE_LOCK
);
635 hfs_flushvolumeheader(psMount
, HFS_FVH_SKIP_TRANSACTION
| HFS_FVH_MARK_UNMOUNT
);
638 hfs_unlock_global(psMount
);
642 psMount
->hfs_syncer_thread
= NULL
;
643 lf_lck_mtx_unlock(&psMount
->sync_mutex
);
649 LFHFS_Check( int fdToCheck
, __unused UVFSVolumeId volId
,
650 __unused UVFSVolumeCredential
*volumeCreds
, check_flags_t how
)
652 return fsck_hfs(fdToCheck
, how
);
655 UVFSFSOps HFS_fsOps
= {
656 .fsops_version
= UVFS_FSOPS_VERSION_CURRENT
,
658 .fsops_init
= LFHFS_Init
,
659 .fsops_fini
= LFHFS_Fini
,
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
,
667 .fsops_getfsattr
= LFHFS_GetFSAttr
,
668 .fsops_setfsattr
= LFHFS_SetFSAttr
,
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
,
688 .fsops_getxattr
= LFHFS_GetXAttr
,
689 .fsops_setxattr
= LFHFS_SetXAttr
,
690 .fsops_listxattr
= LFHFS_ListXAttr
,
692 .fsops_scandir
= LFHFS_ScanDir
,
693 .fsops_scanids
= LFHFS_ScanIDs
697 CrashAbortFunction_FP gpsCrashAbortFunctionArray
[CRASH_ABORT_LAST
] = {0};
700 __attribute__((visibility("default")))
702 livefiles_plugin_init(UVFSFSOps
**ops
)