├── VFSFilter0 ├── VFSFilter0 │ ├── en.lproj │ │ └── InfoPlist.strings │ ├── VFSFilter0-Prefix.pch │ ├── WaitingList.cpp │ ├── RecursionEngine.cpp │ ├── VNode.cpp │ ├── ApplicationsData.h │ ├── VmPmap.h │ ├── VFSFilter0-Info.plist │ ├── VersionDependent.h │ ├── VFSFilter0.h │ ├── WaitingList.h │ ├── QvrMacPolicy.h │ ├── VFSHooks.h │ ├── Kauth.h │ ├── VmPmap.cpp │ ├── QvrMacPolicy.cpp │ ├── ApplicationsData.cpp │ ├── VFSFilter0UserClientInterface.h │ ├── RecursionEngine.h │ ├── Common.h │ ├── VFSFilter0UserClient.h │ ├── VNode.h │ ├── VFSFilter0.cpp │ ├── VNodeHook.h │ ├── Kauth.cpp │ ├── VersionDependent.cpp │ ├── CommonHashTable.h │ └── VFSFilter0UserClient.cpp └── VFSFilter0.xcodeproj │ └── project.pbxproj ├── README.md └── VFSFilter0Client ├── VFSFilterClient.xcodeproj └── project.pbxproj └── VFSFilterClient └── main.cpp /VFSFilter0/VFSFilter0/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/WaitingList.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // WaitingList.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 24/07/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include "WaitingList.h" 10 | 11 | 12 | WaitingList gFileOpenWaitingList; -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/RecursionEngine.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // RecursionEngine.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/13/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include "RecursionEngine.h" 10 | 11 | RecursionEngine RecursionEngine::Instance; -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VNode.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // VNode.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 21/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include "VNode.h" 10 | #include "VersionDependent.h" 11 | 12 | //-------------------------------------------------------------------- 13 | 14 | VNodeMap VNodeMap::InstanceForAppData; 15 | VNodeMap VNodeMap::InstanceForVnodeIO; 16 | VNodeMap VNodeMap::InstanceForHookedVnodes; 17 | VNodeMap VNodeMap::InstanceForShadowToVnode; 18 | IOLock* VNodeMap::Lock; 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | errno_t 23 | QvrAdjustVnodeSizeByBackingVnode( 24 | __in vnode_t vnode, 25 | __in vnode_t backingVnode, 26 | __in vfs_context_t context 27 | ) 28 | { 29 | off_t newSize; 30 | off_t oldSize; 31 | errno_t error; 32 | 33 | error = QvrVnodeGetSize( vnode, &oldSize, context ); 34 | if( error ) 35 | return error; 36 | 37 | error = QvrVnodeGetSize( backingVnode, &newSize, gSuperUserContext ); 38 | if( error ) 39 | return error; 40 | 41 | if( newSize > oldSize ) 42 | error = QvrVnodeSetsize( vnode, newSize, 0x0, context ); 43 | 44 | return error; 45 | } 46 | 47 | //-------------------------------------------------------------------- 48 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/ApplicationsData.h: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationsData.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/19/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__ApplicationsData__ 10 | #define __VFSFilter0__ApplicationsData__ 11 | 12 | #include "Common.h" 13 | #include "RecursionEngine.h" 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | typedef enum ApplicationDataType{ 23 | ADT_CreateNew = 0, 24 | ADT_OpenExisting, 25 | 26 | ADT_TypesCount 27 | } ADT; 28 | 29 | //-------------------------------------------------------------------- 30 | 31 | class ApplicationData{ 32 | 33 | public: 34 | const char* redirectTo; 35 | const char* applicationShortName; 36 | bool redirectIO; 37 | }; 38 | 39 | //-------------------------------------------------------------------- 40 | 41 | const ApplicationData* 42 | QvrGetApplicationDataByName( 43 | __in const OSSymbol* name, 44 | __in ADT type 45 | ); 46 | 47 | const ApplicationData* 48 | QvrGetApplicationDataByContext( 49 | __in vfs_context_t context, 50 | __in ADT type 51 | ); 52 | 53 | //-------------------------------------------------------------------- 54 | 55 | const OSSymbol* 56 | QvrGetProcessNameByPid( 57 | __in pid_t pid 58 | ); 59 | 60 | const OSSymbol* 61 | QvrGetProcessNameByContext( 62 | __in vfs_context_t context 63 | ); 64 | 65 | //-------------------------------------------------------------------- 66 | 67 | /* 68 | class ApplicationsSet { 69 | 70 | protected: 71 | 72 | OSDictionary* byName; 73 | OSArray* byPid; 74 | 75 | public: 76 | 77 | 78 | }; 79 | */ 80 | 81 | 82 | #endif /* defined(__VFSFilter0__ApplicationsData__) */ 83 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VmPmap.h: -------------------------------------------------------------------------------- 1 | // 2 | // VmPmap.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 4/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VmPmap__ 10 | #define __VFSFilter0__VmPmap__ 11 | 12 | #include "Common.h" 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | #include 22 | 23 | //-------------------------------------------------------------------- 24 | 25 | #define INTEL_PGBYTES I386_PGBYTES 26 | #define INTEL_PGSHIFT I386_PGSHIFT 27 | #define INTEL_OFFMASK (I386_PGBYTES - 1) 28 | #define PG_FRAME 0x000FFFFFFFFFF000ULL 29 | 30 | //-------------------------------------------------------------------- 31 | 32 | typedef uint64_t pmap_paddr_t; 33 | typedef void* pmap_t; 34 | 35 | //-------------------------------------------------------------------- 36 | 37 | extern 38 | void 39 | bcopy_phys( 40 | addr64_t src64, 41 | addr64_t dst64, 42 | vm_size_t bytes); 43 | 44 | extern 45 | ppnum_t 46 | pmap_find_phys(pmap_t pmap, addr64_t va); 47 | 48 | extern pmap_t kernel_pmap; 49 | 50 | //-------------------------------------------------------------------- 51 | 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | //-------------------------------------------------------------------- 58 | 59 | addr64_t 60 | QvrVirtToPhys( 61 | __in vm_offset_t addr 62 | ); 63 | 64 | 65 | unsigned int 66 | QvrWriteWiredSrcToWiredDst( 67 | __in vm_offset_t src, 68 | __in vm_offset_t dst, 69 | __in vm_size_t len 70 | ); 71 | 72 | //-------------------------------------------------------------------- 73 | 74 | #endif /* defined(__VFSFilter0__VmPmap__) */ 75 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | KEXT 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | IOKitPersonalities 26 | 27 | VFSFilter0 28 | 29 | CFBundleIdentifier 30 | com.${PRODUCT_NAME} 31 | IOClass 32 | com_VFSFilter0 33 | IOKitDebug 34 | 0 35 | IOMatchCategory 36 | com_VFSFilter0 37 | IOProviderClass 38 | IOResources 39 | IOResourceMatch 40 | IOKit 41 | IOUserClientClass 42 | VFSFilter0UserClient 43 | 44 | 45 | NSHumanReadableCopyright 46 | Copyright © 2015 Slava Imameev. All rights reserved. 47 | OSBundleLibraries 48 | 49 | com.apple.kpi.bsd 50 | 9.8.0 51 | com.apple.kpi.dsep 52 | 9.8.0 53 | com.apple.kpi.iokit 54 | 9.8.0 55 | com.apple.kpi.libkern 56 | 9.8.0 57 | com.apple.kpi.mach 58 | 9.8.0 59 | com.apple.kpi.unsupported 60 | 9.8.0 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VersionDependent.h: -------------------------------------------------------------------------------- 1 | // 2 | // VersionDependent.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 21/06/2015. 6 | // Copyright (c) 2015 slava. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VersionDependent__ 10 | #define __VFSFilter0__VersionDependent__ 11 | 12 | #include "Common.h" 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | // 17 | // the structure defines an offset from the start of a v_op vector for a function 18 | // implementing a corresponding vnode operation 19 | // 20 | typedef struct _QvrVnodeOpvOffsetDesc { 21 | struct vnodeop_desc *opve_op; /* which operation this is, NULL for the terminating entry */ 22 | vm_offset_t offset; /* offset in bytes from the start of v_op, (-1) means "unknown" */ 23 | } QvrVnodeOpvOffsetDesc; 24 | 25 | //-------------------------------------------------------------------- 26 | 27 | #define VFC_VFSVNOP_PAGEINV2 0x2000 28 | #define VFC_VFSVNOP_PAGEOUTV2 0x4000 29 | 30 | #define CN_SKIPNAMECACHE 0x40000000 /* skip cache during lookup(), allow FS to handle all components */ 31 | 32 | //-------------------------------------------------------------------- 33 | 34 | errno_t 35 | QvrVnodeGetSize(vnode_t vp, off_t *sizep, vfs_context_t ctx); 36 | 37 | errno_t 38 | QvrVnodeSetsize(vnode_t vp, off_t size, int ioflag, vfs_context_t ctx); 39 | 40 | VOPFUNC* 41 | QvrGetVnodeOpVector( 42 | __in vnode_t vn 43 | ); 44 | 45 | QvrVnodeOpvOffsetDesc* 46 | QvrRetriveVnodeOpvOffsetDescByVnodeOpDesc( 47 | __in struct vnodeop_desc *opve_op 48 | ); 49 | 50 | VOPFUNC 51 | QvrGetVnop( 52 | __in vnode_t vn, 53 | __in struct vnodeop_desc *opve_op 54 | ); 55 | 56 | const char* 57 | GetVnodeNamePtr( 58 | __in vnode_t vn 59 | ); 60 | 61 | int 62 | QvrGetVnodeVfsFlags( 63 | __in vnode_t vnode 64 | ); 65 | 66 | //-------------------------------------------------------------------- 67 | 68 | #endif /* defined(__VFSFilter0__VersionDependent__) */ 69 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0.h: -------------------------------------------------------------------------------- 1 | #ifndef __VFSFilter0__VFSFilter0__ 2 | #define __VFSFilter0__VFSFilter0__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Common.h" 9 | #include "VFSFilter0UserClient.h" 10 | #include "RecursionEngine.h" 11 | 12 | //-------------------------------------------------------------------- 13 | 14 | class VFSFilter0UserClient; 15 | 16 | //-------------------------------------------------------------------- 17 | 18 | // 19 | // the I/O Kit driver class 20 | // 21 | class com_VFSFilter0 : public IOService 22 | { 23 | OSDeclareDefaultStructors(com_VFSFilter0) 24 | 25 | public: 26 | virtual bool start(IOService *provider); 27 | virtual void stop( IOService * provider ); 28 | 29 | virtual IOReturn newUserClient( __in task_t owningTask, 30 | __in void*, 31 | __in UInt32 type, 32 | __in IOUserClient **handler ); 33 | 34 | IOReturn registerUserClient( __in VFSFilter0UserClient* client ); 35 | IOReturn unregisterUserClient( __in VFSFilter0UserClient* client ); 36 | static bool IsUserClient( __in_opt vfs_context_t context = NULL ); 37 | 38 | virtual void sendVFSDataToClient( __in struct _VFSData* data ); 39 | 40 | static com_VFSFilter0* getInstance(){ return com_VFSFilter0::Instance; }; 41 | 42 | VFSFilter0UserClient* getClient(); 43 | void putClient(); 44 | 45 | protected: 46 | 47 | virtual bool init(); 48 | virtual void free(); 49 | 50 | private: 51 | 52 | proc_t getUserClientProc(); 53 | 54 | private: 55 | 56 | // 57 | // the object is retained 58 | // 59 | volatile VFSFilter0UserClient* userClient; 60 | 61 | bool pendingUnregistration; 62 | UInt32 clientInvocations; 63 | 64 | static com_VFSFilter0* Instance; 65 | 66 | }; 67 | 68 | //-------------------------------------------------------------------- 69 | 70 | bool IsUserClient(); 71 | 72 | //-------------------------------------------------------------------- 73 | 74 | #endif//__VFSFilter0__VFSFilter0__ 75 | 76 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/WaitingList.h: -------------------------------------------------------------------------------- 1 | // 2 | // WaitingList.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 24/07/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__WaitingList__ 10 | #define __VFSFilter0__WaitingList__ 11 | 12 | #include "Common.h" 13 | #include "RecursionEngine.h" 14 | 15 | //-------------------------------------------------------------------- 16 | 17 | class WaitingList{ 18 | 19 | public: 20 | 21 | WaitingList() 22 | { 23 | lock = IOLockAlloc(); 24 | assert( lock ); 25 | } 26 | 27 | ~WaitingList() 28 | { 29 | while( ! list.isEmpty() ) 30 | { 31 | void* key; 32 | 33 | if( list.removeFirstEntryAndReturnItsData( &key ) ) 34 | thread_wakeup(key); 35 | } 36 | 37 | IOLockFree(lock); 38 | } 39 | 40 | int enter( __in void* key ) 41 | { 42 | return list.addDataByKey( key, this ); 43 | } 44 | 45 | void wait( __in void* key ) 46 | { 47 | assert( preemption_enabled() ); 48 | 49 | bool wait = false; 50 | 51 | IOLockLock( lock ); 52 | {// start of the lock 53 | 54 | if( list.getDataByKey(key) ) 55 | wait = (THREAD_WAITING == assert_wait( key, THREAD_UNINT )); 56 | 57 | }// end of the lock 58 | IOLockUnlock( lock ); 59 | 60 | if( wait ) 61 | thread_block( THREAD_CONTINUE_NULL ); 62 | } 63 | 64 | void signal( __in void* key ) 65 | { 66 | IOLockLock( lock ); 67 | {// start of the lock 68 | 69 | if( list.getDataByKey(key) ) 70 | { 71 | thread_wakeup(key); 72 | list.removeKey(key); 73 | } 74 | 75 | }// end of the lock 76 | IOLockUnlock( lock ); 77 | } 78 | 79 | private: 80 | DataMap list; 81 | IOLock* lock; 82 | }; 83 | 84 | //-------------------------------------------------------------------- 85 | 86 | extern WaitingList gFileOpenWaitingList; 87 | 88 | //-------------------------------------------------------------------- 89 | 90 | #endif /* defined(__VFSFilter0__WaitingList__) */ 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MacOSX-VFS-redirector 2 | 3 | ## License 4 | 5 | The license model is a BSD Open Source License. This is a non-viral license, only asking that if you use it, you acknowledge the authors, in this case Slava Imameev. 6 | 7 | ## Design 8 | 9 | The filter is based on MacOSX-FileSystem-Filter. The filter design description can be found here https://github.com/slavaim/MacOSX-FileSystem-Filter . 10 | 11 | The filter redirects file creation, open requests, rename and data IO (read, write) from an application to a shadow directory where shadow copies for files are created. The shadow directory path can cross mount points. An application under control doesn't aware about redirection and believes it works with original files by using unmodified paths. Applications under control are registered in gApplicationsData array. The array is declared in https://github.com/slavaim/MacOSX-VFS-redirector/blob/master/VFSFilter0/VFSFilter0/ApplicationsData.cpp . 12 | 13 | The filter employs a user mode client for data modification and shadow file creation. See processing for VFSDataType_PreOperationCallback in https://github.com/slavaim/MacOSX-VFS-redirector/blob/master/VFSFilter0Client/VFSFilterClient/main.cpp . 14 | 15 | The filter's core is https://github.com/slavaim/MacOSX-VFS-redirector/blob/master/VFSFilter0/VFSFilter0/VFSHooks.cpp . It contains VFS hooks to intercept file creation and open, redirect IO and call a user client. 16 | 17 | The filter was tested on Mac OS X Yosemite (10.10) and Mac OS X El Capitan (10.11). A Sierra (10.12) support was added with https://github.com/slavaim/MacOSX-VFS-redirector/commit/48b7868d64b76b5da72bfce890180a0da323f028 commit. The vnode structure definition has changed in Sierra. A preprocessor condition in VersionDependent.cpp 18 | ``` 19 | #if !defined(MAC_OS_X_VERSION_10_11) || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_11 20 | ``` 21 | is used to choose the target OS version during compilation. If the condition is evaluated as true a kext will support Yosemite (10.10) and El Capitan (10.11), else a kext for Sierra (10.12) is being built. There is an alternative macOS version independent hooker implementation that uses a vnode structure inference, for details take a look at https://github.com/slavaim/MacOSX-FileSystem-Filter/blob/master/FsdFilter/FltFakeFSD.cpp#L1902 . 22 | 23 | ## Filter loading 24 | 25 | The filter module is loaded by kextload command. The user client connects to the filter IOKit object to receive callbacks and modify data. 26 | 27 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/QvrMacPolicy.h: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Created by Slava 4 | * Copyright 2015 Slava Imameev. All rights reserved. 5 | * 6 | */ 7 | 8 | /* 9 | the file contains an implementation for MAC plugin module ( Mandatory Access Control (MAC) framework ), 10 | MAC is a Mac OS X clone clone for TrustedBSD. For more information see 11 | http://www.trustedbsd.org 12 | */ 13 | 14 | #ifndef _QVRMACPOLICY_H 15 | #define _QVRMACPOLICY_H 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | extern "C" { 22 | #include 23 | } 24 | #include "Common.h" 25 | 26 | class QvrMacPolicy : public OSObject 27 | { 28 | 29 | OSDeclareDefaultStructors( QvrMacPolicy ) 30 | 31 | private: 32 | 33 | bool registered; 34 | 35 | struct mac_policy_conf* mpc; 36 | mac_policy_handle_t handlep; 37 | void* xd; 38 | 39 | protected: 40 | 41 | virtual void free(); 42 | virtual bool initWithPolicyConf( __in struct mac_policy_conf *mpc, __in void* xd ); 43 | 44 | public: 45 | 46 | // a caller must guarantee the validity of provided pointer until the object is destroyed 47 | static QvrMacPolicy* createPolicyObject( __in struct mac_policy_conf *mpc, __in void* xd ); 48 | 49 | kern_return_t registerMacPolicy(); 50 | kern_return_t unRegisterMacPolicy(); 51 | }; 52 | 53 | /* 54 | * Flags to control which MAC subsystems are enforced 55 | * on a per-process/thread/credential basis. 56 | The defenitions were borrowed from the XNU kernel sources, look at /security/mac.h file. 57 | */ 58 | #define MAC_SYSTEM_ENFORCE 0x0001 /* system management */ 59 | #define MAC_PROC_ENFORCE 0x0002 /* process management */ 60 | #define MAC_MACH_ENFORCE 0x0004 /* mach interfaces */ 61 | #define MAC_VM_ENFORCE 0x0008 /* VM interfaces */ 62 | #define MAC_FILE_ENFORCE 0x0010 /* file operations */ 63 | #define MAC_SOCKET_ENFORCE 0x0020 /* socket operations */ 64 | #define MAC_PIPE_ENFORCE 0x0040 /* pipes */ 65 | #define MAC_VNODE_ENFORCE 0x0080 /* vnode operations */ 66 | #define MAC_NET_ENFORCE 0x0100 /* network management */ 67 | #define MAC_MBUF_ENFORCE 0x0200 /* network traffic */ 68 | #define MAC_POSIXSEM_ENFORCE 0x0400 /* posix semaphores */ 69 | #define MAC_POSIXSHM_ENFORCE 0x0800 /* posix shared memory */ 70 | #define MAC_SYSVMSG_ENFORCE 0x1000 /* SysV message queues */ 71 | #define MAC_SYSVSEM_ENFORCE 0x2000 /* SysV semaphores */ 72 | #define MAC_SYSVSHM_ENFORCE 0x4000 /* SysV shared memory */ 73 | #define MAC_ALL_ENFORCE 0x7fff /* enforce everything */ 74 | 75 | #endif // _QVRMACPOLICY_H -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSHooks.h: -------------------------------------------------------------------------------- 1 | // 2 | // VFSHooks.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 3/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VFSHooks__ 10 | #define __VFSFilter0__VFSHooks__ 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | #include "Common.h" 28 | 29 | //-------------------------------------------------------------------- 30 | 31 | typedef enum _QvrVnodeType{ 32 | QvrVnodeOthers = 0, 33 | QvrVnodeWatched, 34 | QvrVnodeRedirected, 35 | 36 | // 37 | // always the last 38 | // 39 | QvrVnodeMaximum 40 | } QvrVnodeType; 41 | 42 | //-------------------------------------------------------------------- 43 | 44 | IOReturn 45 | VFSHookInit(); 46 | 47 | void 48 | VFSHookRelease(); 49 | 50 | bool 51 | QvrHookVnodeVop( 52 | __inout vnode_t vnode, 53 | __in QvrVnodeType type 54 | ); 55 | 56 | int 57 | QvrVnopLookupHookEx2( 58 | __inout struct vnop_lookup_args *ap 59 | ); 60 | 61 | int 62 | QvrVnopCreateHookEx2( 63 | __inout struct vnop_create_args *ap 64 | ); 65 | 66 | int 67 | QvrVnopCloseHookEx2( 68 | __inout struct vnop_close_args *ap 69 | ); 70 | 71 | int 72 | QvrVnopReadHookEx2( 73 | __in struct vnop_read_args *ap 74 | ); 75 | int 76 | QvrVnopOpenHookEx2( 77 | __in struct vnop_open_args *ap 78 | ); 79 | int 80 | QvrVnopPageinHookEx2( 81 | __in struct vnop_pagein_args *ap 82 | ); 83 | 84 | int 85 | QvrVnopWriteHookEx2( 86 | __in struct vnop_write_args *ap 87 | ); 88 | 89 | int 90 | QvrVnopPageoutHookEx2( 91 | __in struct vnop_pageout_args *ap 92 | ); 93 | 94 | int 95 | QvrVnopRenameHookEx2( 96 | __in struct vnop_rename_args *ap 97 | ); 98 | 99 | int 100 | QvrVnopExchangeHookEx2( 101 | __in struct vnop_exchange_args *ap 102 | ); 103 | 104 | int 105 | QvrFsdReclaimHookEx2(struct vnop_reclaim_args *ap); 106 | 107 | int 108 | QvrVnopInactiveHookEx2(struct vnop_inactive_args *ap); 109 | 110 | int 111 | QvrVnopGetattrHookEx2( 112 | __in struct vnop_getattr_args *ap 113 | ); 114 | 115 | //-------------------------------------------------------------------- 116 | 117 | #endif /* defined(__VFSFilter0__VFSHooks__) */ 118 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/Kauth.h: -------------------------------------------------------------------------------- 1 | // 2 | // Kauth.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 3/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__Kauth__ 10 | #define __VFSFilter0__Kauth__ 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #include 20 | #include 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | 27 | #include "Common.h" 28 | #include "QvrMacPolicy.h" 29 | 30 | //-------------------------------------------------------------------- 31 | 32 | class com_VFSFilter0; 33 | 34 | class QvrIOKitKAuthVnodeGate : public OSObject 35 | { 36 | OSDeclareDefaultStructors( QvrIOKitKAuthVnodeGate ) 37 | 38 | private: 39 | 40 | // 41 | // the callback is called when a vnode is being created or have been created depending on the type of a file open, 42 | // also the callback is called when the vnode is being accessed 43 | // 44 | static int VnodeAuthorizeCallback( kauth_cred_t credential, // reference to the actor's credentials 45 | void *idata, // cookie supplied when listener is registered 46 | kauth_action_t action, // requested action 47 | uintptr_t arg0, // the VFS context 48 | uintptr_t arg1, // the vnode in question 49 | uintptr_t arg2, // parent vnode, or NULL 50 | uintptr_t arg3); // pointer to an errno value 51 | 52 | static int MacVnodeCheckLookup( kauth_cred_t cred, 53 | struct vnode *dvp, 54 | struct label *dlabel, 55 | struct componentname *cnp 56 | ); 57 | 58 | // 59 | // KAUTH_SCOPE_VNODE listener, used for the acess permissions check 60 | // 61 | kauth_listener_t VnodeListener; 62 | 63 | // 64 | // a driver's class 65 | // 66 | com_VFSFilter0* provider; 67 | 68 | QvrMacPolicy* macPolicy; 69 | 70 | struct mac_policy_ops mpoServiceProtection; 71 | struct mac_policy_conf mpcServiceProtection; 72 | 73 | protected: 74 | 75 | virtual bool init(); 76 | virtual void free(); 77 | 78 | public: 79 | 80 | virtual IOReturn RegisterVnodeScopeCallback(void); 81 | 82 | static QvrIOKitKAuthVnodeGate* withCallbackRegistration( __in com_VFSFilter0* provider ); 83 | 84 | virtual void sendVFSDataToClient( __in struct _VFSData* data ); 85 | 86 | }; 87 | 88 | //-------------------------------------------------------------------- 89 | 90 | extern QvrIOKitKAuthVnodeGate* gVnodeGate; 91 | 92 | //-------------------------------------------------------------------- 93 | 94 | #endif /* defined(__VFSFilter0__Kauth__) */ 95 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VmPmap.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // VmPmap.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 4/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include "VmPmap.h" 10 | 11 | //-------------------------------------------------------------------- 12 | 13 | #if !defined(__i386__) && !defined(__x86_64__) 14 | #error "Unsupported architecture" 15 | #endif 16 | 17 | addr64_t 18 | QvrVirtToPhys( 19 | __in vm_offset_t addr 20 | ) 21 | /* 22 | Converts a kernel virtual address to a physical which is suitable for 23 | a PTE which can be used in a page table, 24 | the function's ancestor is 25 | addr64_t kvtophys( vm_offset_t addr ) 26 | */ 27 | { 28 | pmap_paddr_t pa; 29 | 30 | // 31 | // always use the kernel's pmap 32 | // 33 | 34 | // 35 | // TO DO - pmap_find_phys was moved to the unsupported library in the KPI which 36 | // is the only available interface for 64 bit kext(!) 37 | // 38 | // TO DO - kernel_pmap was moved to the unsupported library in the KPI which 39 | // is the only available interface for 64 bit kext(!) 40 | // 41 | // look at the IOMappedWrite32 (64) functions family implementations from IOLib 42 | // 43 | pa = ((pmap_paddr_t)pmap_find_phys(kernel_pmap, addr)) << INTEL_PGSHIFT; 44 | if (pa) 45 | pa |= (addr & INTEL_OFFMASK); 46 | 47 | return ((addr64_t)pa); 48 | } 49 | 50 | //-------------------------------------------------------------------- 51 | 52 | unsigned int 53 | QvrWriteWiredSrcToWiredDst( 54 | __in vm_offset_t src, 55 | __in vm_offset_t dst, 56 | __in vm_size_t len 57 | ) 58 | /* 59 | the source and the destanation must be WIRED and in the kernel map! The function 60 | creates a temporary mapping for the src and destanation thus defeating any protection, 61 | the function's ancestor is 62 | unsigned kdp_vm_write( caddr_t src, caddr_t dst, unsigned len) 63 | */ 64 | { 65 | addr64_t cur_virt_src, cur_virt_dst; 66 | addr64_t cur_phys_src, cur_phys_dst; 67 | unsigned long resid, cnt, cnt_src, cnt_dst; 68 | 69 | cur_virt_src = (addr64_t)((unsigned long)src); 70 | cur_virt_dst = (addr64_t)((unsigned long)dst); 71 | 72 | resid = len; 73 | 74 | while (resid != 0) { 75 | if ((cur_phys_dst = QvrVirtToPhys( cur_virt_dst )) == 0) 76 | break; 77 | 78 | if ((cur_phys_src = QvrVirtToPhys( cur_virt_src )) == 0) 79 | break; 80 | 81 | // 82 | // Copy as many bytes as possible without crossing a page 83 | // 84 | cnt_src = (unsigned long)(PAGE_SIZE - (cur_phys_src & PAGE_MASK)); 85 | cnt_dst = (unsigned long)(PAGE_SIZE - (cur_phys_dst & PAGE_MASK)); 86 | 87 | if (cnt_src > cnt_dst) 88 | cnt = cnt_dst; 89 | else 90 | cnt = cnt_src; 91 | if (cnt > resid) 92 | cnt = resid; 93 | 94 | // 95 | // bcopy_phys calls pmap_get_mapwindow with a read allowed PTE for a src 96 | // and a write allowed PTE for the destanation 97 | // 98 | bcopy_phys(cur_phys_src, cur_phys_dst, cnt); /* Copy stuff over */ 99 | 100 | cur_virt_src +=cnt; 101 | cur_virt_dst +=cnt; 102 | resid -= cnt; 103 | } 104 | 105 | return (len - resid); 106 | } 107 | 108 | //-------------------------------------------------------------------- 109 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/QvrMacPolicy.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Created by Slava 4 | * Copyright 2015 Slava Imameev. All rights reserved. 5 | * 6 | */ 7 | 8 | /* 9 | the file contains an implementation for MAC plugin module ( Mandatory Access Control (MAC) framework ), 10 | MAC is a Mac OS X clone clone for TrustedBSD. For more information see 11 | http://www.trustedbsd.org 12 | */ 13 | 14 | #include "QvrMacPolicy.h" 15 | 16 | //-------------------------------------------------------------------- 17 | 18 | #define super OSObject 19 | OSDefineMetaClassAndStructors( QvrMacPolicy, OSObject) 20 | 21 | //-------------------------------------------------------------------- 22 | 23 | QvrMacPolicy* QvrMacPolicy::createPolicyObject( __in struct mac_policy_conf *mpc, __in void* xd ) 24 | { 25 | assert( preemption_enabled() ); 26 | 27 | QvrMacPolicy* newObject = new QvrMacPolicy(); 28 | assert( newObject ); 29 | if( !newObject ){ 30 | DBG_PRINT_ERROR(("new DldMacPolicy() failed\n")); 31 | return NULL; 32 | } 33 | 34 | if( !newObject->initWithPolicyConf( mpc, xd ) ){ 35 | 36 | DBG_PRINT_ERROR(("newObject->initWithPolicyConf( mpc, xd ) failed\n")); 37 | newObject->release(); 38 | return NULL; 39 | } 40 | 41 | return newObject; 42 | } 43 | 44 | //-------------------------------------------------------------------- 45 | 46 | bool QvrMacPolicy::initWithPolicyConf( __in struct mac_policy_conf *mpc, __in void* xd ) 47 | { 48 | if( !this->init() ){ 49 | 50 | DBG_PRINT_ERROR(("this->init() failed\n")); 51 | return false; 52 | } 53 | 54 | this->mpc = mpc; 55 | this->xd = xd; 56 | 57 | return true; 58 | } 59 | 60 | //-------------------------------------------------------------------- 61 | 62 | 63 | void QvrMacPolicy::free() 64 | { 65 | assert( ! this->registered ); 66 | super::free(); 67 | } 68 | 69 | //-------------------------------------------------------------------- 70 | 71 | kern_return_t QvrMacPolicy::registerMacPolicy() 72 | { 73 | kern_return_t err; 74 | 75 | assert( preemption_enabled() ); 76 | assert( !this->registered ); 77 | 78 | if( this->registered ) 79 | return KERN_SUCCESS; 80 | 81 | err = mac_policy_register( this->mpc, &this->handlep, this->xd ); 82 | this->registered = ( KERN_SUCCESS == err ); 83 | 84 | assert( KERN_SUCCESS == err ); 85 | if( KERN_SUCCESS != err ){ 86 | 87 | DBG_PRINT_ERROR(("registerMacPolicy failed with err = %d\n", err)); 88 | } 89 | 90 | return err; 91 | } 92 | 93 | //-------------------------------------------------------------------- 94 | 95 | kern_return_t QvrMacPolicy::unRegisterMacPolicy() 96 | { 97 | kern_return_t err = ENOENT; 98 | 99 | assert( preemption_enabled() ); 100 | 101 | if( this->registered ){ 102 | 103 | err = mac_policy_unregister( this->handlep ); 104 | this->registered = !( KERN_SUCCESS == err ); 105 | 106 | assert( KERN_SUCCESS == err ); 107 | if( KERN_SUCCESS != err ){ 108 | 109 | DBG_PRINT_ERROR(("unRegisterMacPolicy failed with err = %d\n", err)); 110 | } 111 | } 112 | 113 | return err; 114 | } 115 | 116 | //-------------------------------------------------------------------- 117 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/ApplicationsData.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationsData.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/19/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "ApplicationsData.h" 11 | 12 | //-------------------------------------------------------------------- 13 | 14 | #define WordDirectory "/work1/my_word" 15 | 16 | const static ApplicationData gApplicationsData[2][3] = 17 | { 18 | // 19 | // CreateNew 20 | // 21 | { 22 | { 23 | .applicationShortName = "Microsoft Word", 24 | .redirectTo = WordDirectory, 25 | .redirectIO = true 26 | }, 27 | 28 | { 29 | .applicationShortName = "Preview", 30 | .redirectTo = "/work1/my_preview", 31 | .redirectIO = true 32 | }, 33 | 34 | { 35 | .applicationShortName = "AdobeReader", 36 | .redirectTo = "/work1/my_adobe", 37 | .redirectIO = true 38 | }, 39 | }, 40 | 41 | // 42 | // OpenExisting 43 | // 44 | { 45 | { 46 | .applicationShortName = "Microsoft Word", 47 | .redirectTo = WordDirectory, 48 | .redirectIO = false 49 | }, 50 | 51 | { 52 | .applicationShortName = "Preview", 53 | .redirectTo = "/work1/my_preview", 54 | .redirectIO = false 55 | }, 56 | 57 | { 58 | .applicationShortName = "AdobeReader", 59 | .redirectTo = "/work1/my_adobe", 60 | .redirectIO = false 61 | }, 62 | } 63 | }; 64 | 65 | //-------------------------------------------------------------------- 66 | 67 | const OSSymbol* 68 | QvrGetProcessNameByPid( 69 | __in pid_t pid 70 | ) 71 | /* 72 | a caller must release the returned object 73 | */ 74 | { 75 | char p_comm[MAXCOMLEN + 1]; 76 | 77 | //bzero( p_comm, sizeof(p_comm) ); 78 | proc_name( pid, p_comm, sizeof( p_comm ) ); 79 | 80 | const OSSymbol* name = OSSymbol::withCString( p_comm ); 81 | 82 | return name; 83 | } 84 | 85 | const OSSymbol* 86 | QvrGetProcessNameByContext( 87 | __in vfs_context_t context 88 | ) 89 | /* 90 | a caller must release the returned object 91 | */ 92 | { 93 | return QvrGetProcessNameByPid( vfs_context_pid( context ) ); 94 | } 95 | 96 | //-------------------------------------------------------------------- 97 | 98 | const ApplicationData* 99 | QvrGetApplicationDataByName( 100 | __in const OSSymbol* name, 101 | __in ADT type 102 | ) 103 | { 104 | for( int i = 0; i < __countof( gApplicationsData[type] ); ++i ){ 105 | 106 | if( name->isEqualTo( gApplicationsData[type][ i ].applicationShortName ) ) 107 | return &gApplicationsData[type][ i ]; 108 | } 109 | 110 | return NULL; 111 | } 112 | 113 | const ApplicationData* 114 | QvrGetApplicationDataByContext( 115 | __in vfs_context_t context, 116 | __in ADT type 117 | ) 118 | { 119 | const OSSymbol* name = QvrGetProcessNameByContext( context ); 120 | assert( name ); 121 | if( ! name ) 122 | return NULL; 123 | 124 | const ApplicationData* data = QvrGetApplicationDataByName( name, type ); 125 | 126 | name->release(); 127 | 128 | return data; 129 | } 130 | 131 | //-------------------------------------------------------------------- 132 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0UserClientInterface.h: -------------------------------------------------------------------------------- 1 | // 2 | // VFSFilter0UserClientInterface.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/6/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef VFSFilter0_VFSFilter0UserClientInterface_h 10 | #define VFSFilter0_VFSFilter0UserClientInterface_h 11 | 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | #define VFS_DATA_TYPE_VER 0x1 23 | 24 | //-------------------------------------------------------------------- 25 | 26 | typedef enum { 27 | VFSOpcode_Unknown = 0, 28 | VFSOpcode_Lookup, 29 | VFSOpcode_Create, 30 | VFSOpcode_Read, 31 | VFSOpcode_Write, 32 | VFSOpcode_Rename, 33 | VFSOpcode_Exchange, 34 | VFSOpcode_Open, 35 | VFSOpcode_Filter // a fake opcode used for filtering 36 | } VFSOpcode; 37 | 38 | //-------------------------------------------------------------------- 39 | 40 | typedef enum { 41 | VFSDataType_Unknown = 0, // an invalid value 42 | VFSDataType_Audit, 43 | VFSDataType_PreOperationCallback, 44 | 45 | VFSDataType_Count // a number of types, must be the last member of the enum 46 | } VFSDataType; 47 | 48 | //-------------------------------------------------------------------- 49 | 50 | typedef struct _VFSDataHeader{ 51 | int32_t Version; // VFS_DATA_TYPE_VER 52 | VFSDataType Type; 53 | } VFSDataHeader; 54 | 55 | //-------------------------------------------------------------------- 56 | 57 | typedef struct _VFSFilter0UserClientAudit{ 58 | VFSOpcode opcode; 59 | errno_t error; 60 | char path[MAXPATHLEN]; 61 | 62 | // 63 | // this one must always be the last as being 64 | // cut to save space 65 | // 66 | char redirectedPath[MAXPATHLEN]; 67 | } VFSFilter0UserClientAudit; 68 | 69 | //-------------------------------------------------------------------- 70 | 71 | typedef struct _VFSFilter0PreOperationCallback{ 72 | VFSOpcode op; 73 | int64_t id; 74 | 75 | union { 76 | 77 | struct { 78 | UInt16 calledFromCreate; // a bool value {0,1} 79 | char redirectedPath[MAXPATHLEN]; 80 | char path[MAXPATHLEN]; 81 | 82 | // 83 | // this one must always be the last as being 84 | // cut to save space 85 | // 86 | char shadowFilePath[MAXPATHLEN]; 87 | } Lookup; 88 | 89 | struct { 90 | char from[MAXPATHLEN]; 91 | 92 | // 93 | // this one must always be the last as being 94 | // cut to save space 95 | // 96 | char to[MAXPATHLEN]; 97 | } Exchange; 98 | 99 | struct { 100 | // 101 | // an actual operation that requested filtering 102 | // 103 | VFSOpcode op; 104 | 105 | // 106 | // a path to a file, the file might not exist as in case of VFSOpcode_Create, 107 | // this one must always be the last as being cut to save space 108 | // 109 | char path[MAXPATHLEN]; 110 | } Filter; 111 | 112 | } Parameters; 113 | 114 | } VFSFilter0PreOperationCallback; 115 | 116 | //-------------------------------------------------------------------- 117 | 118 | typedef struct _VFSFilter0UserClientData{ 119 | 120 | VFSDataHeader Header; 121 | 122 | union{ 123 | VFSFilter0UserClientAudit Audit; 124 | VFSFilter0PreOperationCallback PreOperationCallback; 125 | } Data; 126 | 127 | } VFSFilter0UserClientData; 128 | 129 | //-------------------------------------------------------------------- 130 | 131 | typedef struct _VFSClientReply{ 132 | 133 | int64_t id; 134 | 135 | union{ 136 | struct{ 137 | int8_t isControlledFile; 138 | } Filter; 139 | } Data; 140 | 141 | } VFSClientReply; 142 | 143 | //-------------------------------------------------------------------- 144 | 145 | enum { 146 | kt_kVnodeWatcherUserClientOpen, 147 | kt_kVnodeWatcherUserClientClose, 148 | kt_kVnodeWatcherUserClientReply, 149 | 150 | kt_kVnodeWatcherUserClientNMethods,// the number of methods available to a client 151 | kt_kStopListeningToMessages = 0xff, 152 | }; 153 | 154 | //-------------------------------------------------------------------- 155 | 156 | #ifdef __cplusplus 157 | } 158 | #endif 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/RecursionEngine.h: -------------------------------------------------------------------------------- 1 | // 2 | // RecursionEngine.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/13/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__RecursionEngine__ 10 | #define __VFSFilter0__RecursionEngine__ 11 | 12 | #include "Common.h" 13 | 14 | class DataMap{ 15 | 16 | private: 17 | 18 | class Entry{ 19 | public: 20 | LIST_ENTRY listEntry; 21 | void* key; 22 | void* data; 23 | }; 24 | 25 | private: 26 | 27 | LIST_ENTRY head; 28 | IOLock* lock; 29 | 30 | private: 31 | 32 | Entry* findEntryByKey( __in void* key, __in bool acquireLock ) 33 | { 34 | Entry* foundEntry = NULL; 35 | 36 | if( acquireLock ) 37 | IOLockLock( lock ); 38 | { // start ofthe lock 39 | for( LIST_ENTRY* le = head.Flink; le != &head; le = le->Flink ) 40 | { 41 | Entry* entry = CONTAINING_RECORD( le, Entry, listEntry ); 42 | if( entry->key == key ) 43 | { 44 | foundEntry = entry; 45 | break; 46 | } 47 | } // end for 48 | } // end of the lock 49 | if( acquireLock ) 50 | IOLockUnlock( lock ); 51 | 52 | return foundEntry; 53 | } 54 | 55 | public: 56 | 57 | DataMap() 58 | { 59 | InitializeListHead( &head ); 60 | lock = IOLockAlloc(); 61 | assert( lock ); 62 | // TO DO , in kernel we can't through an exception if allocation failed 63 | // redesign with init() function 64 | }; 65 | 66 | virtual ~DataMap() 67 | { 68 | assert( IsListEmpty( &head ) ); 69 | IOLockFree( lock ); 70 | } 71 | 72 | // 73 | // both key and data can't be NULL, 74 | // the function checks for duplicate entries 75 | // and updates the data field 76 | // 77 | virtual bool addDataByKey( __in void* key, __in void* data ) 78 | { 79 | assert( data ); 80 | assert( key ); 81 | 82 | bool freeEntry = false; 83 | Entry* entry = reinterpret_cast( IOMalloc( sizeof(*entry) ) ); 84 | assert( entry ); 85 | if( ! entry ) 86 | return false; 87 | 88 | bzero( entry, sizeof(*entry) ); 89 | 90 | entry->key = key; 91 | entry->data = data; 92 | 93 | IOLockLock( lock ); 94 | { 95 | Entry* existingEntry = findEntryByKey( key, false ); 96 | if( existingEntry ) 97 | existingEntry->data = data; 98 | else 99 | InsertHeadList( &head, &entry->listEntry ); 100 | 101 | freeEntry = ( NULL != existingEntry ); 102 | } 103 | IOLockUnlock( lock ); 104 | 105 | if( freeEntry ) 106 | IOFree( entry, sizeof( *entry ) ); 107 | 108 | return true; 109 | }; 110 | 111 | virtual void removeKey( __in void* key ) 112 | { 113 | Entry* entryToRemove = NULL; 114 | 115 | IOLockLock( lock ); 116 | { 117 | entryToRemove = findEntryByKey( key, false ); 118 | if( entryToRemove ) 119 | RemoveEntryList( &entryToRemove->listEntry ); 120 | } 121 | IOLockUnlock( lock ); 122 | 123 | if( entryToRemove ) 124 | IOFree( entryToRemove, sizeof(*entryToRemove) ); 125 | }; 126 | 127 | 128 | virtual void* removeFirstEntryAndReturnItsData( __out_opt void** key = NULL) 129 | { 130 | Entry* entryToRemove = NULL; 131 | 132 | IOLockLock( lock ); 133 | { 134 | if( !IsListEmpty( &head ) ){ 135 | 136 | entryToRemove = CONTAINING_RECORD( head.Flink, Entry, listEntry ); 137 | RemoveEntryList( &entryToRemove->listEntry ); 138 | } 139 | } 140 | IOLockUnlock( lock ); 141 | 142 | void* data = NULL; 143 | 144 | if( entryToRemove ){ 145 | 146 | data = entryToRemove->data; 147 | if( key ) 148 | *key = entryToRemove->key; 149 | 150 | IOFree( entryToRemove, sizeof(*entryToRemove) ); 151 | } 152 | 153 | return data; 154 | }; 155 | 156 | // 157 | // returns NULL if a key is not found, the validite of the returned pointer is a caller's responsibility 158 | // 159 | virtual void* getDataByKey( __in void* key ) 160 | { 161 | void* data = NULL; 162 | 163 | IOLockLock( lock ); 164 | { 165 | Entry* entry = findEntryByKey( key, false ); 166 | if( entry ) 167 | data = entry->data; 168 | } 169 | IOLockUnlock( lock ); 170 | 171 | return data; 172 | } 173 | 174 | virtual bool isEmpty() 175 | { 176 | return IsListEmpty( &head ); 177 | } 178 | 179 | }; 180 | 181 | class RecursionEngine: public DataMap{ 182 | 183 | public: 184 | static RecursionEngine Instance; 185 | 186 | public: 187 | 188 | static void* CookieForRecursiveCall() { return RecursionEngine::Instance.getDataByKey( current_thread() ); } 189 | 190 | static bool IsRecursiveCall( __in void* cookie ) { return NULL != RecursionEngine::Instance.getDataByKey( current_thread() ); } 191 | static void EnterRecursiveCall( __in void* cookie ) { RecursionEngine::Instance.addDataByKey( current_thread(), cookie ); } 192 | static void LeaveRecursiveCall( __in void* cookie ) { RecursionEngine::Instance.removeKey( current_thread() ); } 193 | 194 | static bool IsRecursiveCall() { return IsRecursiveCall( current_thread() ); } 195 | static void EnterRecursiveCall() { EnterRecursiveCall( current_thread() ); } 196 | static void LeaveRecursiveCall() { LeaveRecursiveCall( current_thread() ); } 197 | 198 | }; 199 | 200 | #endif /* defined(__VFSFilter0__RecursionEngine__) */ 201 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/Common.h: -------------------------------------------------------------------------------- 1 | // 2 | // Common.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 3/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef VFSFilter0_Common_h 10 | #define VFSFilter0_Common_h 11 | 12 | #include 13 | #include 14 | 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | //-------------------------------------------------------------------- 34 | 35 | #define __in 36 | #define __out 37 | #define __inout 38 | #define __in_opt 39 | #define __out_opt 40 | #define __opt 41 | 42 | //-------------------------------------------------------------------- 43 | 44 | // 45 | // ASL - Apple System Logger, 46 | // the macro requires a boolean variable isError being defined in the outer scope, 47 | // the macro uses only ASL, in case of stampede the ASL silently drops data on the floor 48 | // 49 | // 50 | // TO DO - IOSleep called after IOLog allows the system to replenish the log buffer 51 | // by retrieving the existing entries usin syslogd 52 | // 53 | #define DLD_COMM_LOG_EXT_TO_ASL( _S_ ) do{\ 54 | IOLog(" [%-7d] QvrKrnLog:" ); \ 55 | IOLog("%s %s(%u):%s: ", isError?"ERROR!!":"", __FILE__ , __LINE__, __PRETTY_FUNCTION__ );\ 56 | IOLog _S_ ; \ 57 | }while(0); 58 | 59 | // 60 | // a common log 61 | // 62 | #if !defined(_DLD_LOG) 63 | 64 | #define DBG_PRINT( _S_ ) do{ void(0); }while(0);// { kprintf _S_ ; } 65 | 66 | #else 67 | 68 | #define DBG_PRINT( _S_ ) do{ bool isError = false; DLD_COMM_LOG_EXT_TO_ASL( _S_ ); }while(0); 69 | 70 | #endif 71 | 72 | 73 | // 74 | // an errors log 75 | // 76 | #if !defined(_DLD_LOG_ERRORS) 77 | 78 | #define DBG_PRINT_ERROR( _S_ ) do{ void(0); }while(0);//DBG_PRINT( _S_ ) 79 | 80 | #else 81 | 82 | #define DBG_PRINT_ERROR( _S_ ) do{ bool isError = true; DLD_COMM_LOG_EXT_TO_ASL( _S_ ); }while(0); 83 | 84 | #endif 85 | 86 | //-------------------------------------------------------------------- 87 | 88 | // 89 | // Double linked list manipulation functions, the same as on Windows 90 | // 91 | 92 | typedef struct _LIST_ENTRY { 93 | struct _LIST_ENTRY *Flink; 94 | struct _LIST_ENTRY *Blink; 95 | } LIST_ENTRY, *PLIST_ENTRY; 96 | 97 | inline 98 | void 99 | InitializeListHead( 100 | __inout PLIST_ENTRY ListHead 101 | ) 102 | { 103 | ListHead->Flink = ListHead->Blink = ListHead; 104 | } 105 | 106 | inline 107 | bool 108 | IsListEmpty( 109 | __in const LIST_ENTRY * ListHead 110 | ) 111 | { 112 | return (bool)(ListHead->Flink == ListHead); 113 | } 114 | 115 | inline 116 | bool 117 | RemoveEntryList( 118 | __in PLIST_ENTRY Entry 119 | ) 120 | { 121 | PLIST_ENTRY Blink; 122 | PLIST_ENTRY Flink; 123 | 124 | Flink = Entry->Flink; 125 | Blink = Entry->Blink; 126 | Blink->Flink = Flink; 127 | Flink->Blink = Blink; 128 | return (bool)(Flink == Blink); 129 | } 130 | 131 | inline 132 | PLIST_ENTRY 133 | RemoveHeadList( 134 | __in PLIST_ENTRY ListHead 135 | ) 136 | { 137 | PLIST_ENTRY Flink; 138 | PLIST_ENTRY Entry; 139 | 140 | Entry = ListHead->Flink; 141 | Flink = Entry->Flink; 142 | ListHead->Flink = Flink; 143 | Flink->Blink = ListHead; 144 | return Entry; 145 | } 146 | 147 | inline 148 | PLIST_ENTRY 149 | RemoveTailList( 150 | __in PLIST_ENTRY ListHead 151 | ) 152 | { 153 | PLIST_ENTRY Blink; 154 | PLIST_ENTRY Entry; 155 | 156 | Entry = ListHead->Blink; 157 | Blink = Entry->Blink; 158 | ListHead->Blink = Blink; 159 | Blink->Flink = ListHead; 160 | return Entry; 161 | } 162 | 163 | inline 164 | void 165 | InsertTailList( 166 | __in PLIST_ENTRY ListHead, 167 | __in PLIST_ENTRY Entry 168 | ) 169 | { 170 | PLIST_ENTRY Blink; 171 | 172 | Blink = ListHead->Blink; 173 | Entry->Flink = ListHead; 174 | Entry->Blink = Blink; 175 | Blink->Flink = Entry; 176 | ListHead->Blink = Entry; 177 | } 178 | 179 | 180 | inline 181 | void 182 | InsertHeadList( 183 | __in PLIST_ENTRY ListHead, 184 | __in PLIST_ENTRY Entry 185 | ) 186 | { 187 | PLIST_ENTRY Flink; 188 | 189 | Flink = ListHead->Flink; 190 | Entry->Flink = Flink; 191 | Entry->Blink = ListHead; 192 | Flink->Blink = Entry; 193 | ListHead->Flink = Entry; 194 | } 195 | 196 | inline 197 | void 198 | AppendTailList( 199 | __in PLIST_ENTRY ListHead, 200 | __in PLIST_ENTRY ListToAppend 201 | ) 202 | { 203 | PLIST_ENTRY ListEnd = ListHead->Blink; 204 | 205 | ListHead->Blink->Flink = ListToAppend; 206 | ListHead->Blink = ListToAppend->Blink; 207 | ListToAppend->Blink->Flink = ListHead; 208 | ListToAppend->Blink = ListEnd; 209 | } 210 | 211 | //--------------------------------------------------------------------- 212 | 213 | // 214 | // Calculate the address of the base of the structure given its type, and an 215 | // address of a field within the structure. 216 | // 217 | 218 | #define CONTAINING_RECORD(address, type, field) ((type *)( \ 219 | (char*)(address) - \ 220 | reinterpret_cast(&((type *)0)->field))) 221 | 222 | 223 | #define __countof( X ) ( sizeof( X ) / sizeof( X[0] ) ) 224 | 225 | //--------------------------------------------------------------------- 226 | 227 | extern vfs_context_t gSuperUserContext; // TO DO redesign! 228 | 229 | 230 | //-------------------------------------------------------------------- 231 | 232 | // 233 | // a type for the vnode operations 234 | // 235 | typedef int (*VOPFUNC)(void *) ; 236 | typedef int (*VFSFUNC)(void *) ; 237 | 238 | //-------------------------------------------------------------------- 239 | 240 | 241 | #endif // VFSFilter0_Common_h 242 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0UserClient.h: -------------------------------------------------------------------------------- 1 | // 2 | // VFSFilter0UserClient.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/6/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VFSFilter0UserClient__ 10 | #define __VFSFilter0__VFSFilter0UserClient__ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "Common.h" 17 | #include "VFSFilter0.h" 18 | #include "VFSFilter0UserClientInterface.h" 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | class com_VFSFilter0; 23 | 24 | typedef struct _VFSAudit{ 25 | vnode_t vn; 26 | char* path; 27 | char* redirectedPath; 28 | VFSOpcode op; 29 | errno_t error; 30 | } VFSAudit; 31 | 32 | typedef struct _VFSPreOperationCallback{ 33 | VFSOpcode op; 34 | int64_t id; 35 | 36 | union{ 37 | 38 | struct { 39 | // 40 | // either path or vnode must be provided 41 | // 42 | char* path; 43 | vnode_t vn; 44 | 45 | char* redirectedPath; 46 | 47 | char* shadowFilePath; // optional 48 | 49 | bool calledFromCreate; 50 | } Lookup; 51 | 52 | struct { 53 | 54 | char* from; 55 | char* to; 56 | 57 | } Rename; 58 | 59 | struct { 60 | 61 | char* from; 62 | char* to; 63 | 64 | } Exchange; 65 | 66 | struct { 67 | 68 | VFSOpcode op; 69 | char* path; 70 | 71 | } Filter; 72 | 73 | } Parameters; 74 | 75 | } VFSPreOperationCallback; 76 | 77 | typedef struct _VFSData{ 78 | 79 | VFSDataHeader Header; 80 | 81 | struct{ 82 | bool WasEnqueued; 83 | } Status; 84 | 85 | union{ 86 | VFSAudit Audit; 87 | VFSPreOperationCallback PreOperationCallback; 88 | } Data; 89 | 90 | } VFSData; 91 | 92 | //-------------------------------------------------------------------- 93 | 94 | __inline 95 | void 96 | VFSInitData( 97 | __in VFSData* Data, 98 | __in VFSDataType Type 99 | ) 100 | { 101 | bzero( Data, sizeof(*Data) ); 102 | 103 | Data->Header.Version = VFS_DATA_TYPE_VER; 104 | Data->Header.Type = Type; 105 | } 106 | 107 | //-------------------------------------------------------------------- 108 | 109 | typedef struct _QvrPreOperationCallbackData{ 110 | 111 | VFSOpcode op; 112 | 113 | union { 114 | 115 | struct { 116 | __in char* pathToLookup; 117 | __in_opt char* shadowFilePath; 118 | __in char* redirectedFilePath; 119 | __in bool calledFromCreate; 120 | } Lookup; 121 | 122 | struct { 123 | __in char* from; 124 | __in char* to; 125 | } Exchange; 126 | 127 | struct { 128 | struct { 129 | __in VFSOpcode op; 130 | __in char* path; 131 | } in; 132 | 133 | struct { 134 | __out bool isControlledFile; 135 | #if MACH_ASSERT 136 | __out bool replyWasReceived; 137 | __out bool noClient; 138 | #endif // DBG 139 | } out; 140 | 141 | } Filter; 142 | 143 | } Parameters; 144 | 145 | } QvrPreOperationCallback; 146 | 147 | void 148 | QvrPreOperationCallbackAndWaitForReply( __in QvrPreOperationCallback* data ); 149 | 150 | //-------------------------------------------------------------------- 151 | 152 | // the user client class 153 | class VFSFilter0UserClient : public IOUserClient 154 | { 155 | OSDeclareDefaultStructors(VFSFilter0UserClient) 156 | 157 | private: 158 | task_t fClient; 159 | proc_t fClientProc; 160 | com_VFSFilter0* fProvider; 161 | IODataQueue* fDataQueue; 162 | IOMemoryDescriptor* fSharedMemory; 163 | kauth_listener_t fListener; 164 | IOLock* queueLock; 165 | 166 | public: 167 | virtual bool start(IOService *provider); 168 | virtual void stop(IOService *provider); 169 | virtual IOReturn open(void); 170 | virtual IOReturn clientClose(void); 171 | virtual IOReturn close(void); 172 | virtual bool terminate(IOOptionBits options); 173 | virtual IOReturn startLogging(void); 174 | virtual IOReturn stopLogging(void); 175 | virtual void free(); 176 | 177 | virtual bool initWithTask( task_t owningTask, void *securityID, UInt32 type); 178 | 179 | virtual IOReturn registerNotificationPort( mach_port_t port, UInt32 type, UInt32 refCon); 180 | 181 | virtual IOReturn clientMemoryForType(UInt32 type, IOOptionBits *options, 182 | IOMemoryDescriptor **memory); 183 | 184 | virtual IOExternalMethod *getTargetAndMethodForIndex(IOService **target, 185 | UInt32 index); 186 | 187 | virtual void sendVFSDataToClient( __in VFSData* data ); 188 | 189 | virtual void preOperationCallbackAndWaitForReply( __in QvrPreOperationCallback* inData ); 190 | 191 | virtual IOReturn reply( __in void *vInBuffer, //VFSClientReply 192 | __out void *vOutBuffer, 193 | __in void *vInSize, 194 | __in void *vOutSizeP, 195 | void *, void *); 196 | 197 | virtual proc_t getProc(){ return fClientProc; } 198 | }; 199 | 200 | //-------------------------------------------------------------------- 201 | 202 | #endif /* defined(__VFSFilter0__VFSFilter0UserClient__) */ 203 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VNode.h: -------------------------------------------------------------------------------- 1 | // 2 | // VNode.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 21/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VNode__ 10 | #define __VFSFilter0__VNode__ 11 | 12 | #include "Common.h" 13 | #include "RecursionEngine.h" 14 | #include "ApplicationsData.h" 15 | 16 | //-------------------------------------------------------------------- 17 | 18 | errno_t 19 | QvrAdjustVnodeSizeByBackingVnode( 20 | __in vnode_t vnode, 21 | __in vnode_t backingVnode, 22 | __in vfs_context_t context 23 | ); 24 | 25 | //-------------------------------------------------------------------- 26 | 27 | typedef struct{ 28 | char dirPath[ MAXPATHLEN + 1 ]; 29 | } VnodeData; 30 | 31 | /* 32 | this one should be redesigned 33 | */ 34 | class VNodeMap: public DataMap{ 35 | 36 | public: 37 | 38 | static void Init() 39 | { 40 | Lock = IOLockAlloc(); 41 | assert( Lock ); 42 | } 43 | 44 | private: 45 | static VNodeMap InstanceForAppData; // vnode to ApplicationData 46 | static VNodeMap InstanceForVnodeIO; // vnode to vnodeIO ( i.e. a backing vnode ) 47 | static VNodeMap InstanceForShadowToVnode; // shadow vnode to vnode ( i.e. a reverse mappong ) 48 | static VNodeMap InstanceForHookedVnodes; // vnodes for which QvrHookVnodeVop was called 49 | static IOLock* Lock; 50 | 51 | public: 52 | 53 | //--------------------------------------------------------------------- 54 | 55 | static bool addHookedVnode( __in vnode_t vn ){ return InstanceForHookedVnodes.addDataByKey( vn, (void*)vn ); } 56 | static void removeHookedVnode( __in vnode_t vn ){ InstanceForHookedVnodes.removeKey( vn ); } 57 | static bool isVnodeHooked( __in vnode_t vn ){ return 0x0 != InstanceForHookedVnodes.getDataByKey( vn ); } 58 | 59 | //--------------------------------------------------------------------- 60 | 61 | static bool addVnodeAppData( __in vnode_t vn, __in const ApplicationData* data ){ return InstanceForAppData.addDataByKey( vn, (void*)data ); } 62 | static void removeVnodeAppData( __in vnode_t vn ){ InstanceForAppData.removeKey( vn ); } 63 | static const ApplicationData* getVnodeAppData( __in vnode_t vn ){ return (const ApplicationData*)InstanceForAppData.getDataByKey( vn ); } 64 | 65 | //--------------------------------------------------------------------- 66 | 67 | static void addVnodeShadowReverse( __in vnode_t vnodeShadow, __in vnode_t vn ) 68 | { 69 | assert( vnodeShadow != vn ); 70 | 71 | vnode_t oldVn = NULLVP; 72 | 73 | IOLockLock( VNodeMap::Lock ); 74 | { 75 | vnode_t currentVnode = getVnodeShadowReverse( vnodeShadow ); 76 | bool add = false; 77 | 78 | if( NULLVP == currentVnode ){ 79 | 80 | add = true; 81 | 82 | } else if( vn != currentVnode ){ 83 | 84 | InstanceForShadowToVnode.removeKey( vnodeShadow ); 85 | oldVn = currentVnode; 86 | add = true; 87 | } 88 | 89 | if( add && InstanceForShadowToVnode.addDataByKey( vnodeShadow, (void*)vn ) ) 90 | vnode_get( vn ); 91 | } 92 | IOLockUnlock( VNodeMap::Lock ); 93 | 94 | if( oldVn ) 95 | vnode_put( oldVn ); 96 | } 97 | 98 | static void removeShadowReverse( __in vnode_t vnodeShadow ) 99 | { 100 | vnode_t vn = NULLVP; 101 | 102 | IOLockLock( VNodeMap::Lock ); 103 | { 104 | vn= getVnodeShadowReverse( vnodeShadow ); 105 | if( vn ) 106 | InstanceForShadowToVnode.removeKey( vnodeShadow ); 107 | } 108 | IOLockUnlock( VNodeMap::Lock ); 109 | 110 | // 111 | // release a reference taken by addVnodeShadowReverse 112 | // 113 | if( vn ) 114 | vnode_put( vn ); 115 | } 116 | 117 | static const vnode_t getVnodeShadowReverseRef( __in vnode_t vnodeShadow ) 118 | /*the returned vnode is referenced, a caller must release it by vnode_put()*/ 119 | { 120 | vnode_t vn; 121 | 122 | IOLockLock( VNodeMap::Lock ); 123 | { 124 | vn = getVnodeShadowReverse( vnodeShadow ); 125 | if( vn ) 126 | vnode_get( vn ); 127 | } 128 | IOLockUnlock( VNodeMap::Lock ); 129 | 130 | return vn; 131 | } 132 | 133 | static void releaseAllShadowReverse() 134 | { 135 | while( ! InstanceForShadowToVnode.isEmpty() ){ 136 | 137 | vnode_t vn = NULLVP; 138 | 139 | IOLockLock( VNodeMap::Lock ); 140 | { 141 | vn = (vnode_t)InstanceForShadowToVnode.removeFirstEntryAndReturnItsData(); 142 | } 143 | IOLockUnlock( VNodeMap::Lock ); 144 | 145 | if( vn ) 146 | vnode_put( vn ); 147 | } 148 | } 149 | 150 | static void addVnodeIO( __in vnode_t vn, __in vnode_t vnodeIO ) 151 | { 152 | assert( vn != vnodeIO ); 153 | IOLockLock( VNodeMap::Lock ); 154 | { 155 | if( NULLVP == getVnodeIO( vn ) ){ 156 | 157 | if( InstanceForVnodeIO.addDataByKey( vn, (void*)vnodeIO ) ) 158 | vnode_get( vnodeIO ); 159 | 160 | } else { 161 | 162 | assert( vnodeIO == getVnodeIO( vn ) ); 163 | } 164 | } 165 | IOLockUnlock( VNodeMap::Lock ); 166 | } 167 | 168 | static void removeVnodeIO( __in vnode_t vn ) 169 | { 170 | vnode_t vnodeIO = NULLVP; 171 | 172 | IOLockLock( VNodeMap::Lock ); 173 | { 174 | vnodeIO = getVnodeIO( vn ); 175 | if( vnodeIO ) 176 | InstanceForVnodeIO.removeKey( vn ); 177 | } 178 | IOLockUnlock( VNodeMap::Lock ); 179 | 180 | // 181 | // release a reference taken by addVnodeIO 182 | // 183 | if( vnodeIO ) 184 | vnode_put( vnodeIO ); 185 | } 186 | 187 | static const vnode_t getVnodeIORef( __in vnode_t vn ) 188 | /*the returned vnode is referenced, a caller must release it by vnode_put()*/ 189 | { 190 | vnode_t vnodeIO; 191 | 192 | IOLockLock( VNodeMap::Lock ); 193 | { 194 | vnodeIO = getVnodeIO( vn ); 195 | if( vnodeIO ) 196 | vnode_get( vnodeIO ); 197 | } 198 | IOLockUnlock( VNodeMap::Lock ); 199 | 200 | return vnodeIO; 201 | } 202 | 203 | static void releaseAllVnodeIO() 204 | { 205 | while( ! InstanceForVnodeIO.isEmpty() ){ 206 | 207 | vnode_t vnodeIO = NULLVP; 208 | 209 | IOLockLock( VNodeMap::Lock ); 210 | { 211 | vnodeIO = (vnode_t)InstanceForVnodeIO.removeFirstEntryAndReturnItsData(); 212 | } 213 | IOLockUnlock( VNodeMap::Lock ); 214 | 215 | if( vnodeIO ) 216 | vnode_put( vnodeIO ); 217 | } 218 | } 219 | 220 | //--------------------------------------------------------------------- 221 | 222 | private: 223 | 224 | static const vnode_t getVnodeIO( __in vnode_t vn ) 225 | /*the returned vnode is not referenced*/ 226 | { 227 | return (vnode_t)InstanceForVnodeIO.getDataByKey( vn ); 228 | } 229 | 230 | static const vnode_t getVnodeShadowReverse( __in vnode_t vnodeShadow ) 231 | /*the returned vnode is not referenced*/ 232 | { 233 | return (vnode_t)InstanceForShadowToVnode.getDataByKey( vnodeShadow ); 234 | } 235 | }; 236 | 237 | //-------------------------------------------------------------------- 238 | 239 | #endif /* defined(__VFSFilter0__VNode__) */ 240 | -------------------------------------------------------------------------------- /VFSFilter0Client/VFSFilterClient.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F95DA3841B230B5C004C965C /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95DA3831B230B5C004C965C /* main.cpp */; }; 11 | F95DA3861B230B5C004C965C /* VFSFilterClient.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = F95DA3851B230B5C004C965C /* VFSFilterClient.1 */; }; 12 | F95DA38D1B231BB6004C965C /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F95DA38C1B231BB6004C965C /* IOKit.framework */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXCopyFilesBuildPhase section */ 16 | F95DA37E1B230B5C004C965C /* CopyFiles */ = { 17 | isa = PBXCopyFilesBuildPhase; 18 | buildActionMask = 2147483647; 19 | dstPath = /usr/share/man/man1/; 20 | dstSubfolderSpec = 0; 21 | files = ( 22 | F95DA3861B230B5C004C965C /* VFSFilterClient.1 in CopyFiles */, 23 | ); 24 | runOnlyForDeploymentPostprocessing = 1; 25 | }; 26 | /* End PBXCopyFilesBuildPhase section */ 27 | 28 | /* Begin PBXFileReference section */ 29 | F95DA3801B230B5C004C965C /* VFSFilterClient */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = VFSFilterClient; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | F95DA3831B230B5C004C965C /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = ""; }; 31 | F95DA3851B230B5C004C965C /* VFSFilterClient.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = VFSFilterClient.1; sourceTree = ""; }; 32 | F95DA38C1B231BB6004C965C /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | F95DA37D1B230B5C004C965C /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | F95DA38D1B231BB6004C965C /* IOKit.framework in Frameworks */, 41 | ); 42 | runOnlyForDeploymentPostprocessing = 0; 43 | }; 44 | /* End PBXFrameworksBuildPhase section */ 45 | 46 | /* Begin PBXGroup section */ 47 | F95DA3771B230B5C004C965C = { 48 | isa = PBXGroup; 49 | children = ( 50 | F95DA38C1B231BB6004C965C /* IOKit.framework */, 51 | F95DA3821B230B5C004C965C /* VFSFilterClient */, 52 | F95DA3811B230B5C004C965C /* Products */, 53 | ); 54 | sourceTree = ""; 55 | }; 56 | F95DA3811B230B5C004C965C /* Products */ = { 57 | isa = PBXGroup; 58 | children = ( 59 | F95DA3801B230B5C004C965C /* VFSFilterClient */, 60 | ); 61 | name = Products; 62 | sourceTree = ""; 63 | }; 64 | F95DA3821B230B5C004C965C /* VFSFilterClient */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | F95DA3831B230B5C004C965C /* main.cpp */, 68 | F95DA3851B230B5C004C965C /* VFSFilterClient.1 */, 69 | ); 70 | path = VFSFilterClient; 71 | sourceTree = ""; 72 | }; 73 | /* End PBXGroup section */ 74 | 75 | /* Begin PBXNativeTarget section */ 76 | F95DA37F1B230B5C004C965C /* VFSFilterClient */ = { 77 | isa = PBXNativeTarget; 78 | buildConfigurationList = F95DA3891B230B5C004C965C /* Build configuration list for PBXNativeTarget "VFSFilterClient" */; 79 | buildPhases = ( 80 | F95DA37C1B230B5C004C965C /* Sources */, 81 | F95DA37D1B230B5C004C965C /* Frameworks */, 82 | F95DA37E1B230B5C004C965C /* CopyFiles */, 83 | ); 84 | buildRules = ( 85 | ); 86 | dependencies = ( 87 | ); 88 | name = VFSFilterClient; 89 | productName = VFSFilterClient; 90 | productReference = F95DA3801B230B5C004C965C /* VFSFilterClient */; 91 | productType = "com.apple.product-type.tool"; 92 | }; 93 | /* End PBXNativeTarget section */ 94 | 95 | /* Begin PBXProject section */ 96 | F95DA3781B230B5C004C965C /* Project object */ = { 97 | isa = PBXProject; 98 | attributes = { 99 | LastUpgradeCheck = 0510; 100 | ORGANIZATIONNAME = SlavaImameev; 101 | }; 102 | buildConfigurationList = F95DA37B1B230B5C004C965C /* Build configuration list for PBXProject "VFSFilterClient" */; 103 | compatibilityVersion = "Xcode 3.2"; 104 | developmentRegion = English; 105 | hasScannedForEncodings = 0; 106 | knownRegions = ( 107 | en, 108 | ); 109 | mainGroup = F95DA3771B230B5C004C965C; 110 | productRefGroup = F95DA3811B230B5C004C965C /* Products */; 111 | projectDirPath = ""; 112 | projectRoot = ""; 113 | targets = ( 114 | F95DA37F1B230B5C004C965C /* VFSFilterClient */, 115 | ); 116 | }; 117 | /* End PBXProject section */ 118 | 119 | /* Begin PBXSourcesBuildPhase section */ 120 | F95DA37C1B230B5C004C965C /* Sources */ = { 121 | isa = PBXSourcesBuildPhase; 122 | buildActionMask = 2147483647; 123 | files = ( 124 | F95DA3841B230B5C004C965C /* main.cpp in Sources */, 125 | ); 126 | runOnlyForDeploymentPostprocessing = 0; 127 | }; 128 | /* End PBXSourcesBuildPhase section */ 129 | 130 | /* Begin XCBuildConfiguration section */ 131 | F95DA3871B230B5C004C965C /* Debug */ = { 132 | isa = XCBuildConfiguration; 133 | buildSettings = { 134 | ALWAYS_SEARCH_USER_PATHS = NO; 135 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 136 | CLANG_CXX_LIBRARY = "libc++"; 137 | CLANG_ENABLE_MODULES = YES; 138 | CLANG_ENABLE_OBJC_ARC = YES; 139 | CLANG_WARN_BOOL_CONVERSION = YES; 140 | CLANG_WARN_CONSTANT_CONVERSION = YES; 141 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 142 | CLANG_WARN_EMPTY_BODY = YES; 143 | CLANG_WARN_ENUM_CONVERSION = YES; 144 | CLANG_WARN_INT_CONVERSION = YES; 145 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 146 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 147 | COPY_PHASE_STRIP = NO; 148 | GCC_C_LANGUAGE_STANDARD = gnu99; 149 | GCC_DYNAMIC_NO_PIC = NO; 150 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 151 | GCC_OPTIMIZATION_LEVEL = 0; 152 | GCC_PREPROCESSOR_DEFINITIONS = ( 153 | "DEBUG=1", 154 | "$(inherited)", 155 | ); 156 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 157 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 158 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 159 | GCC_WARN_UNDECLARED_SELECTOR = YES; 160 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 161 | GCC_WARN_UNUSED_FUNCTION = YES; 162 | GCC_WARN_UNUSED_VARIABLE = YES; 163 | MACOSX_DEPLOYMENT_TARGET = 10.9; 164 | ONLY_ACTIVE_ARCH = YES; 165 | SDKROOT = macosx10.9; 166 | }; 167 | name = Debug; 168 | }; 169 | F95DA3881B230B5C004C965C /* Release */ = { 170 | isa = XCBuildConfiguration; 171 | buildSettings = { 172 | ALWAYS_SEARCH_USER_PATHS = NO; 173 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 174 | CLANG_CXX_LIBRARY = "libc++"; 175 | CLANG_ENABLE_MODULES = YES; 176 | CLANG_ENABLE_OBJC_ARC = YES; 177 | CLANG_WARN_BOOL_CONVERSION = YES; 178 | CLANG_WARN_CONSTANT_CONVERSION = YES; 179 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 180 | CLANG_WARN_EMPTY_BODY = YES; 181 | CLANG_WARN_ENUM_CONVERSION = YES; 182 | CLANG_WARN_INT_CONVERSION = YES; 183 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 184 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 185 | COPY_PHASE_STRIP = YES; 186 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 187 | ENABLE_NS_ASSERTIONS = NO; 188 | GCC_C_LANGUAGE_STANDARD = gnu99; 189 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 190 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 191 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 192 | GCC_WARN_UNDECLARED_SELECTOR = YES; 193 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 194 | GCC_WARN_UNUSED_FUNCTION = YES; 195 | GCC_WARN_UNUSED_VARIABLE = YES; 196 | MACOSX_DEPLOYMENT_TARGET = 10.9; 197 | SDKROOT = macosx10.9; 198 | }; 199 | name = Release; 200 | }; 201 | F95DA38A1B230B5C004C965C /* Debug */ = { 202 | isa = XCBuildConfiguration; 203 | buildSettings = { 204 | PRODUCT_NAME = "$(TARGET_NAME)"; 205 | }; 206 | name = Debug; 207 | }; 208 | F95DA38B1B230B5C004C965C /* Release */ = { 209 | isa = XCBuildConfiguration; 210 | buildSettings = { 211 | PRODUCT_NAME = "$(TARGET_NAME)"; 212 | }; 213 | name = Release; 214 | }; 215 | /* End XCBuildConfiguration section */ 216 | 217 | /* Begin XCConfigurationList section */ 218 | F95DA37B1B230B5C004C965C /* Build configuration list for PBXProject "VFSFilterClient" */ = { 219 | isa = XCConfigurationList; 220 | buildConfigurations = ( 221 | F95DA3871B230B5C004C965C /* Debug */, 222 | F95DA3881B230B5C004C965C /* Release */, 223 | ); 224 | defaultConfigurationIsVisible = 0; 225 | defaultConfigurationName = Release; 226 | }; 227 | F95DA3891B230B5C004C965C /* Build configuration list for PBXNativeTarget "VFSFilterClient" */ = { 228 | isa = XCConfigurationList; 229 | buildConfigurations = ( 230 | F95DA38A1B230B5C004C965C /* Debug */, 231 | F95DA38B1B230B5C004C965C /* Release */, 232 | ); 233 | defaultConfigurationIsVisible = 0; 234 | defaultConfigurationName = Release; 235 | }; 236 | /* End XCConfigurationList section */ 237 | }; 238 | rootObject = F95DA3781B230B5C004C965C /* Project object */; 239 | } 240 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "VFSFilter0.h" 9 | #include "VFSHooks.h" 10 | #include "Kauth.h" 11 | #include "VNode.h" 12 | #include "VNodeHook.h" 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | com_VFSFilter0* com_VFSFilter0::Instance; 17 | 18 | //-------------------------------------------------------------------- 19 | 20 | // 21 | // the standard IOKit declarations 22 | // 23 | #undef super 24 | #define super IOService 25 | 26 | OSDefineMetaClassAndStructors(com_VFSFilter0, IOService) 27 | 28 | // 29 | // gSuperUserContext might contain an invalid thread and process pointer if derived from a process 30 | // that terminates!!!! 31 | // 32 | vfs_context_t gSuperUserContext; 33 | 34 | //-------------------------------------------------------------------- 35 | 36 | bool 37 | com_VFSFilter0::start( 38 | __in IOService *provider 39 | ) 40 | { 41 | 42 | //__asm__ volatile( "int $0x3" ); 43 | 44 | VNodeMap::Init(); 45 | 46 | if( kIOReturnSuccess != VFSHookInit() ){ 47 | 48 | DBG_PRINT_ERROR( ( "VFSHookInit() failed\n" ) ); 49 | goto __exit_on_error; 50 | } 51 | 52 | if( ! QvrVnodeHooksHashTable::CreateStaticTableWithSize( 8, true ) ){ 53 | 54 | DBG_PRINT_ERROR( ( "QvrVnodeHooksHashTable::CreateStaticTableWithSize() failed\n" ) ); 55 | goto __exit_on_error; 56 | } 57 | 58 | 59 | // 60 | // gSuperUserContext must have a valid thread and process pointer 61 | // TO DO redesign this! Indefinit holding a thread or task object is a bad behaviour. 62 | // 63 | thread_reference( current_thread() ); 64 | task_reference( current_task() ); 65 | 66 | gSuperUserContext = vfs_context_create(NULL); // vfs_context_kernel() 67 | 68 | // 69 | // create an object for the vnodes KAuth callback and register the callback, 70 | // the callback might be called immediatelly just after registration! 71 | // 72 | gVnodeGate = QvrIOKitKAuthVnodeGate::withCallbackRegistration( this ); 73 | assert( NULL != gVnodeGate ); 74 | if( NULL == gVnodeGate ){ 75 | 76 | DBG_PRINT_ERROR( ( "QvrIOKitKAuthVnodeGate::withDefaultSettings() failed\n" ) ); 77 | goto __exit_on_error; 78 | } 79 | 80 | Instance = this; 81 | 82 | // 83 | // register with IOKit to allow the class matching 84 | // 85 | registerService(); 86 | 87 | return true; 88 | 89 | __exit_on_error: 90 | 91 | // 92 | // all cleanup will be done in stop() and free() 93 | // 94 | this->release(); 95 | return false; 96 | } 97 | 98 | //-------------------------------------------------------------------- 99 | 100 | IOReturn 101 | com_VFSFilter0::newUserClient( 102 | __in task_t owningTask, 103 | __in void*, 104 | __in UInt32 type, 105 | __in IOUserClient **handler 106 | ) 107 | { 108 | return kIOReturnNoResources; 109 | } 110 | 111 | //-------------------------------------------------------------------- 112 | 113 | void 114 | com_VFSFilter0::stop( 115 | __in IOService * provider 116 | ) 117 | { 118 | super::stop( provider ); 119 | } 120 | 121 | //-------------------------------------------------------------------- 122 | 123 | bool com_VFSFilter0::init() 124 | { 125 | if(! super::init() ) 126 | return false; 127 | 128 | return true; 129 | } 130 | 131 | //-------------------------------------------------------------------- 132 | 133 | // 134 | // actually this will not be called as the module should be unloadable in release build 135 | // 136 | void com_VFSFilter0::free() 137 | { 138 | if( gVnodeGate ){ 139 | 140 | gVnodeGate->release(); 141 | gVnodeGate = NULL; 142 | } 143 | 144 | QvrVnodeHooksHashTable::DeleteStaticTable(); 145 | 146 | VFSHookRelease(); 147 | 148 | super::free(); 149 | } 150 | 151 | //-------------------------------------------------------------------- 152 | 153 | IOReturn com_VFSFilter0::unregisterUserClient( __in VFSFilter0UserClient* client ) 154 | { 155 | bool unregistered; 156 | VFSFilter0UserClient* currentClient; 157 | 158 | currentClient = (VFSFilter0UserClient*)this->userClient; 159 | assert( currentClient == client ); 160 | if( currentClient != client ){ 161 | 162 | DBG_PRINT_ERROR(("currentClient != client\n")); 163 | return kIOReturnError; 164 | } 165 | 166 | this->pendingUnregistration = true; 167 | 168 | unregistered = OSCompareAndSwapPtr( (void*)currentClient, NULL, &this->userClient ); 169 | assert( unregistered && NULL == this->userClient ); 170 | if( !unregistered ){ 171 | 172 | DBG_PRINT_ERROR(("!unregistered\n")); 173 | 174 | this->pendingUnregistration = false; 175 | return kIOReturnError; 176 | } 177 | 178 | do { // wait for any existing client invocations to return 179 | 180 | struct timespec ts = { 1, 0 }; // one second 181 | (void)msleep( &this->clientInvocations, // wait channel 182 | NULL, // mutex 183 | PUSER, // priority 184 | "com_VFSFilter0::unregisterUserClient()", // wait message 185 | &ts ); // sleep interval 186 | 187 | } while( this->clientInvocations != 0 ); 188 | 189 | currentClient->release(); 190 | this->pendingUnregistration = false; 191 | 192 | return unregistered? kIOReturnSuccess: kIOReturnError; 193 | } 194 | 195 | //-------------------------------------------------------------------- 196 | 197 | IOReturn com_VFSFilter0::registerUserClient( __in VFSFilter0UserClient* client ) 198 | { 199 | bool registered; 200 | 201 | if( this->pendingUnregistration ){ 202 | 203 | DBG_PRINT_ERROR(("com_VFSFilter0 : pendingUnregistration\n")); 204 | return kIOReturnError; 205 | } 206 | 207 | registered = OSCompareAndSwapPtr( NULL, (void*)client, &this->userClient ); 208 | assert( registered ); 209 | if( !registered ){ 210 | 211 | DBG_PRINT_ERROR(("com_VFSFilter0 : a client was not registered\n")); 212 | return kIOReturnError; 213 | } 214 | 215 | client->retain(); 216 | 217 | return registered? kIOReturnSuccess: kIOReturnError; 218 | } 219 | 220 | //-------------------------------------------------------------------- 221 | 222 | VFSFilter0UserClient* com_VFSFilter0::getClient() 223 | /* 224 | if non NULL is returned the putClient() must be called 225 | */ 226 | { 227 | VFSFilter0UserClient* currentClient; 228 | 229 | // 230 | // if ther is no user client, then nobody call for logging 231 | // 232 | if( NULL == this->userClient || this->pendingUnregistration ) 233 | return NULL; 234 | 235 | OSIncrementAtomic( &this->clientInvocations ); 236 | 237 | currentClient = (VFSFilter0UserClient*)this->userClient; 238 | 239 | // 240 | // if the current client is NULL or can't be atomicaly exchanged 241 | // with the same value then the unregistration is in progress, 242 | // the call to OSCompareAndSwapPtr( NULL, NULL, &this->userClient ) 243 | // checks the this->userClient for NULL atomically 244 | // 245 | if( !currentClient || 246 | !OSCompareAndSwapPtr( currentClient, currentClient, &this->userClient ) || 247 | OSCompareAndSwapPtr( NULL, NULL, &this->userClient ) ){ 248 | 249 | // 250 | // the unregistration is in the progress and waiting for all 251 | // invocations to return 252 | // 253 | assert( this->pendingUnregistration ); 254 | if( 0x1 == OSDecrementAtomic( &this->clientInvocations ) ){ 255 | 256 | // 257 | // this was the last invocation 258 | // 259 | wakeup( &this->clientInvocations ); 260 | } 261 | 262 | return NULL; 263 | } 264 | 265 | return currentClient; 266 | } 267 | 268 | void com_VFSFilter0::putClient() 269 | { 270 | // 271 | // do not exchange or add any condition before OSDecrementAtomic as it must be always done! 272 | // 273 | if( 0x1 == OSDecrementAtomic( &this->clientInvocations ) && NULL == this->userClient ){ 274 | 275 | // 276 | // this was the last invocation 277 | // 278 | wakeup( &this->clientInvocations ); 279 | } 280 | } 281 | 282 | //-------------------------------------------------------------------- 283 | 284 | 285 | void com_VFSFilter0::sendVFSDataToClient( __in VFSData* data ) 286 | { 287 | VFSFilter0UserClient* currentClient = getClient(); 288 | if( !currentClient ) 289 | return; 290 | 291 | currentClient->sendVFSDataToClient( data ); 292 | 293 | putClient(); 294 | } 295 | 296 | //-------------------------------------------------------------------- 297 | 298 | proc_t com_VFSFilter0::getUserClientProc() 299 | { 300 | VFSFilter0UserClient* currentClient = getClient(); 301 | if( !currentClient ) 302 | return NULL; 303 | 304 | proc_t proc = currentClient->getProc(); 305 | 306 | putClient(); 307 | 308 | return proc; 309 | } 310 | 311 | //-------------------------------------------------------------------- 312 | 313 | bool com_VFSFilter0::IsUserClient( __in_opt vfs_context_t context ) 314 | { 315 | proc_t proc; 316 | 317 | if( context ) 318 | proc = vfs_context_proc( context ); 319 | else 320 | proc = current_proc(); 321 | 322 | if( proc ) 323 | return proc == getInstance()->getUserClientProc(); 324 | 325 | return false; 326 | } 327 | 328 | bool IsUserClient(){ return com_VFSFilter0::IsUserClient(); } 329 | 330 | //-------------------------------------------------------------------- 331 | 332 | 333 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VNodeHook.h: -------------------------------------------------------------------------------- 1 | // 2 | // VNodeHook.h 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/22/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #ifndef __VFSFilter0__VNodeHook__ 10 | #define __VFSFilter0__VNodeHook__ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #include "Common.h" 32 | #include "CommonHashTable.h" 33 | 34 | //-------------------------------------------------------------------- 35 | 36 | #define QVR_VOP_UNKNOWN_OFFSET ((vm_offset_t)(-1)) 37 | 38 | //-------------------------------------------------------------------- 39 | 40 | typedef enum _QvrVopEnum{ 41 | QvrVopEnum_Unknown = 0x0, 42 | 43 | QvrVopEnum_access, 44 | QvrVopEnum_advlock, 45 | QvrVopEnum_allocate, 46 | QvrVopEnum_blktooff, 47 | QvrVopEnum_blockmap, 48 | QvrVopEnum_bwrite, 49 | QvrVopEnum_close, 50 | QvrVopEnum_copyfile, 51 | QvrVopEnum_create, 52 | QvrVopEnum_default, 53 | QvrVopEnum_exchange, 54 | QvrVopEnum_fsync, 55 | QvrVopEnum_getattr, 56 | QvrVopEnum_getxattr, 57 | QvrVopEnum_inactive, 58 | QvrVopEnum_ioctl, 59 | QvrVopEnum_link, 60 | QvrVopEnum_listxattr, 61 | QvrVopEnum_lookup, 62 | QvrVopEnum_kqfilt_add, 63 | QvrVopEnum_kqfilt_remove, 64 | QvrVopEnum_mkdir, 65 | QvrVopEnum_mknod, 66 | QvrVopEnum_mmap, 67 | QvrVopEnum_mnomap, 68 | QvrVopEnum_offtoblock, 69 | QvrVopEnum_open, 70 | QvrVopEnum_pagein, 71 | QvrVopEnum_pageout, 72 | QvrVopEnum_pathconf, 73 | QvrVopEnum_read, 74 | QvrVopEnum_readdir, 75 | QvrVopEnum_readdirattr, 76 | QvrVopEnum_readlink, 77 | QvrVopEnum_reclaim, 78 | QvrVopEnum_remove, 79 | QvrVopEnum_removexattr, 80 | QvrVopEnum_rename, 81 | QvrVopEnum_revoke, 82 | QvrVopEnum_rmdir, 83 | QvrVopEnum_searchfs, 84 | QvrVopEnum_select, 85 | QvrVopEnum_setattr, 86 | QvrVopEnum_setxattr, 87 | QvrVopEnum_strategy, 88 | QvrVopEnum_symlink, 89 | QvrVopEnum_whiteout, 90 | QvrVopEnum_write, 91 | QvrVopEnum_getnamedstreamHook, 92 | QvrVopEnum_makenamedstreamHook, 93 | QvrVopEnum_removenamedstreamHook, 94 | 95 | QvrVopEnum_Max 96 | } QvrVopEnum; 97 | 98 | //-------------------------------------------------------------------- 99 | 100 | class QvrVnodeHookEntry: public OSObject 101 | { 102 | 103 | OSDeclareDefaultStructors( QvrVnodeHookEntry ) 104 | 105 | #if defined( DBG ) 106 | friend class QvrVnodeHooksHashTable; 107 | #endif//DBG 108 | 109 | private: 110 | 111 | // 112 | // the number of vnodes which we are aware of for this v_op vector 113 | // 114 | SInt32 vNodeCounter; 115 | 116 | // 117 | // the value is used to mark the origVop's entry as 118 | // corresponding to not hooked function ( i.e. skipped deliberately ) 119 | // 120 | static VOPFUNC vopNotHooked; 121 | 122 | // 123 | // an original functions array, 124 | // for not present functons the values are set to NULL( notional assertion, 125 | // it was niether checked no taken into account by the code ), 126 | // for functions which hooking was skipped deliberately the 127 | // value is set to vopNotHooked 128 | // 129 | VOPFUNC origVop[ QvrVopEnum_Max ]; 130 | 131 | #if defined( DBG ) 132 | bool inHash; 133 | #endif 134 | 135 | protected: 136 | 137 | virtual bool init(); 138 | virtual void free(); 139 | 140 | public: 141 | 142 | // 143 | // allocates the new entry 144 | // 145 | static QvrVnodeHookEntry* newEntry() 146 | { 147 | QvrVnodeHookEntry* entry; 148 | 149 | assert( preemption_enabled() ); 150 | 151 | entry = new QvrVnodeHookEntry(); 152 | assert( entry ) ; 153 | if( !entry ) 154 | return NULL; 155 | 156 | // 157 | // the init is very simple and must alvays succeed 158 | // 159 | entry->init(); 160 | 161 | return entry; 162 | } 163 | 164 | 165 | VOPFUNC 166 | getOrignalVop( __in QvrVopEnum indx ){ 167 | 168 | assert( indx < QvrVopEnum_Max ); 169 | return this->origVop[ (int)indx ]; 170 | } 171 | 172 | void 173 | setOriginalVop( __in QvrVopEnum indx, __in VOPFUNC orig ){ 174 | 175 | assert( indx < QvrVopEnum_Max ); 176 | assert( NULL == this->origVop[ (int)indx ] ); 177 | 178 | this->origVop[ (int)indx ] = orig; 179 | } 180 | 181 | void 182 | setOriginalVopAsNotHooked( __in QvrVopEnum indx ){ 183 | 184 | this->setOriginalVop( indx, this->vopNotHooked ); 185 | } 186 | 187 | bool 188 | isHooked( __in QvrVopEnum indx ){ 189 | 190 | // 191 | // NULL is invalid, vopNotHooked means not hooked deliberately 192 | // 193 | return ( this->vopNotHooked != this->origVop[ (int)indx ] ); 194 | } 195 | 196 | // 197 | // returns te value before the increment 198 | // 199 | SInt32 200 | incrementVnodeCounter(){ 201 | 202 | assert( this->vNodeCounter < 0x80000000 ); 203 | return OSIncrementAtomic( &this->vNodeCounter ); 204 | } 205 | 206 | // 207 | // returns te value before the decrement 208 | // 209 | SInt32 210 | decrementVnodeCounter(){ 211 | 212 | assert( this->vNodeCounter > 0x0 && this->vNodeCounter < 0x80000000); 213 | return OSDecrementAtomic( &this->vNodeCounter ); 214 | } 215 | 216 | SInt32 217 | getVnodeCounter(){ 218 | 219 | return this->vNodeCounter; 220 | } 221 | 222 | }; 223 | 224 | //-------------------------------------------------------------------- 225 | 226 | class QvrVnodeHooksHashTable 227 | { 228 | 229 | private: 230 | 231 | ght_hash_table_t* HashTable; 232 | IORWLock* RWLock; 233 | 234 | #if defined(DBG) 235 | thread_t ExclusiveThread; 236 | #endif//DBG 237 | 238 | // 239 | // returns an allocated hash table object 240 | // 241 | static QvrVnodeHooksHashTable* withSize( int size, bool non_block ); 242 | 243 | // 244 | // free must be called before the hash table object is deleted 245 | // 246 | void free(); 247 | 248 | // 249 | // as usual for IOKit the desctructor and constructor do nothing 250 | // as it is impossible to return an error from the constructor 251 | // in the kernel mode 252 | // 253 | QvrVnodeHooksHashTable() 254 | { 255 | 256 | this->HashTable = NULL; 257 | this->RWLock = NULL; 258 | #if defined(DBG) 259 | this->ExclusiveThread = NULL; 260 | #endif//DBG 261 | 262 | } 263 | 264 | // 265 | // the destructor checks that the free() has been called 266 | // 267 | ~QvrVnodeHooksHashTable() 268 | { 269 | 270 | assert( !this->HashTable && !this->RWLock ); 271 | }; 272 | 273 | public: 274 | 275 | static bool CreateStaticTableWithSize( int size, bool non_block ); 276 | static void DeleteStaticTable(); 277 | 278 | // 279 | // adds an entry to the hash table, the entry is referenced so the caller must 280 | // dereference the entry if it has been referenced 281 | // 282 | bool AddEntry( __in VOPFUNC* v_op, __in QvrVnodeHookEntry* entry ); 283 | 284 | // 285 | // removes the entry from the hash and returns the removed entry, NULL if there 286 | // is no entry for an object, the returned entry is referenced 287 | // 288 | QvrVnodeHookEntry* RemoveEntry( __in VOPFUNC* v_op ); 289 | 290 | // 291 | // returns an entry from the hash table, the returned entry is referenced 292 | // if the refrence's value is "true" 293 | // 294 | QvrVnodeHookEntry* RetrieveEntry( __in VOPFUNC* v_op, __in bool reference = true ); 295 | 296 | 297 | void 298 | LockShared() 299 | { assert( this->RWLock ); 300 | assert( preemption_enabled() ); 301 | 302 | IORWLockRead( this->RWLock ); 303 | }; 304 | 305 | 306 | void 307 | UnLockShared() 308 | { assert( this->RWLock ); 309 | assert( preemption_enabled() ); 310 | 311 | IORWLockUnlock( this->RWLock ); 312 | }; 313 | 314 | 315 | void 316 | LockExclusive() 317 | { 318 | assert( this->RWLock ); 319 | assert( preemption_enabled() ); 320 | 321 | #if defined(DBG) 322 | assert( current_thread() != this->ExclusiveThread ); 323 | #endif//DBG 324 | 325 | IORWLockWrite( this->RWLock ); 326 | 327 | #if defined(DBG) 328 | assert( NULL == this->ExclusiveThread ); 329 | this->ExclusiveThread = current_thread(); 330 | #endif//DBG 331 | 332 | }; 333 | 334 | 335 | void 336 | UnLockExclusive() 337 | { 338 | assert( this->RWLock ); 339 | assert( preemption_enabled() ); 340 | 341 | #if defined(DBG) 342 | assert( current_thread() == this->ExclusiveThread ); 343 | this->ExclusiveThread = NULL; 344 | #endif//DBG 345 | 346 | IORWLockUnlock( this->RWLock ); 347 | }; 348 | 349 | static QvrVnodeHooksHashTable* sVnodeHooksHashTable; 350 | }; 351 | 352 | //-------------------------------------------------------------------- 353 | 354 | // 355 | // success doen't mean that a vnode's operations table has 356 | // been hooked, it can be skipped as ia not interested for us 357 | // 358 | extern 359 | IOReturn 360 | QvrHookVnodeVop( 361 | __inout vnode_t vnode, 362 | __inout bool* isVopHooked 363 | ); 364 | 365 | extern 366 | VOPFUNC 367 | QvrGetOriginalVnodeOp( 368 | __in vnode_t vnode, 369 | __in QvrVopEnum indx 370 | ); 371 | 372 | extern 373 | void 374 | QvrUnHookVnodeVop( 375 | __inout vnode_t vnode 376 | ); 377 | 378 | extern 379 | void 380 | QvrHookVnodeVopAndParent( 381 | __inout vnode_t vnode 382 | ); 383 | 384 | extern 385 | void 386 | QvrUnHookVnodeVopAndParent( 387 | __inout vnode_t vnode 388 | ); 389 | 390 | //-------------------------------------------------------------------- 391 | 392 | #endif /* defined(__VFSFilter0__VNodeHook__) */ 393 | -------------------------------------------------------------------------------- /VFSFilter0Client/VFSFilterClient/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // main.cpp 3 | // VFSFilterClient 4 | // 5 | // Created by slava on 6/06/2015. 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "../../VFSFilter0/VFSFilter0/VFSFilter0UserClientInterface.h" 21 | 22 | int cp(const char *to, const char *from); 23 | 24 | const char* 25 | OpcodeToString( 26 | VFSOpcode op 27 | ) 28 | { 29 | switch( op ){ 30 | case VFSOpcode_Unknown: 31 | return "Unknown"; 32 | case VFSOpcode_Lookup: 33 | return "Lookup"; 34 | case VFSOpcode_Create: 35 | return "Create"; 36 | case VFSOpcode_Read: 37 | return "Read"; 38 | case VFSOpcode_Write: 39 | return "Write"; 40 | case VFSOpcode_Rename: 41 | return "Rename"; 42 | case VFSOpcode_Exchange: 43 | return "Exchange"; 44 | } 45 | 46 | return "Wrong Opcode Value"; 47 | } 48 | 49 | void 50 | VFSFilter0NotificationHandler(void* ctx) 51 | { 52 | io_connect_t connection = *(io_connect_t*)ctx; 53 | kern_return_t kr; 54 | VFSFilter0UserClientData vdata; 55 | UInt32 dataSize; 56 | IODataQueueMemory *queueMappedMemory; 57 | vm_size_t queueMappedMemorySize; 58 | mach_vm_address_t address = 0; 59 | mach_vm_size_t size = 0; 60 | unsigned int msgType = 1; // family-defined port type (arbitrary) 61 | mach_port_t recvPort; 62 | 63 | // allocate a Mach port to receive notifications from the IODataQueue 64 | if (!(recvPort = IODataQueueAllocateNotificationPort())) { 65 | fprintf(stderr, "failed to allocate notification port\n"); 66 | return; 67 | } 68 | 69 | // this will call registerNotificationPort() inside our user client class 70 | kr = IOConnectSetNotificationPort(connection, msgType, recvPort, 0); 71 | if (kr != kIOReturnSuccess) { 72 | fprintf(stderr, "failed to register notification port (%d)\n", kr); 73 | mach_port_destroy(mach_task_self(), recvPort); 74 | return; 75 | } 76 | 77 | // this will call clientMemoryForType() inside our user client class 78 | kr = IOConnectMapMemory(connection, kIODefaultMemoryType, 79 | mach_task_self(), &address, &size, kIOMapAnywhere); 80 | if (kr != kIOReturnSuccess) { 81 | fprintf(stderr, "failed to map memory (%d)\n", kr); 82 | mach_port_destroy(mach_task_self(), recvPort); 83 | return; 84 | } 85 | 86 | queueMappedMemory = (IODataQueueMemory *)address; 87 | queueMappedMemorySize = size; 88 | 89 | printf("reading the driver output ...\n"); 90 | 91 | while (IODataQueueWaitForAvailableData(queueMappedMemory, recvPort) == kIOReturnSuccess) { 92 | 93 | while (IODataQueueDataAvailable(queueMappedMemory)) { 94 | 95 | dataSize = sizeof(vdata); 96 | kr = IODataQueueDequeue(queueMappedMemory, &vdata, &dataSize); 97 | if (kr != kIOReturnSuccess) { 98 | 99 | fprintf(stderr, "*** error in receiving data (%d)\n", kr); 100 | continue; 101 | } 102 | 103 | if( VFSDataType_Audit == vdata.Header.Type ){ 104 | 105 | printf("%s : \"%s\" -> \"%s\" \n", 106 | OpcodeToString(vdata.Data.Audit.opcode), 107 | vdata.Data.Audit.path, 108 | vdata.Data.Audit.redirectedPath); 109 | 110 | } else if( VFSDataType_PreOperationCallback == vdata.Header.Type ){ 111 | 112 | // 113 | // reply 114 | // 115 | VFSClientReply reply = {0}; 116 | 117 | reply.id = vdata.Data.PreOperationCallback.id; 118 | 119 | switch( vdata.Data.PreOperationCallback.op ){ 120 | 121 | case VFSOpcode_Lookup: 122 | { 123 | // 124 | // copy to the protected storage 125 | // 126 | //printf("copy( to = %s, from = %s ) \n", vdata.Data.PreOperationCallback.Parameters.Lookup.redirectedPath, vdata.Data.PreOperationCallback.Parameters.Lookup.path ); 127 | cp( vdata.Data.PreOperationCallback.Parameters.Lookup.redirectedPath, vdata.Data.PreOperationCallback.Parameters.Lookup.path ); 128 | 129 | break; 130 | } 131 | 132 | case VFSOpcode_Exchange: 133 | { 134 | printf("exchangedata( %s, %s )\n", vdata.Data.PreOperationCallback.Parameters.Exchange.from, vdata.Data.PreOperationCallback.Parameters.Exchange.to); 135 | 136 | int error = exchangedata( vdata.Data.PreOperationCallback.Parameters.Exchange.from, 137 | vdata.Data.PreOperationCallback.Parameters.Exchange.to, 138 | 0 ); 139 | 140 | printf("exchangedata() returned %d\n", error); 141 | 142 | break; 143 | } 144 | 145 | case VFSOpcode_Filter: 146 | { 147 | //printf("filter( %s )\n", vdata.Data.PreOperationCallback.Parameters.Filter.path); 148 | reply.Data.Filter.isControlledFile = 1; 149 | break; 150 | 151 | } 152 | 153 | default: 154 | fprintf(stderr, "**** oooops in PreOperationCallback\n"); 155 | break; 156 | 157 | } // end switch 158 | 159 | kern_return_t status = IOConnectCallStructMethod(connection, 160 | kt_kVnodeWatcherUserClientReply, 161 | &reply, 162 | sizeof(reply), 163 | NULL, 164 | 0x0); 165 | if (status != KERN_SUCCESS) { 166 | 167 | fprintf(stderr, "*** IOConnectCallStructMethod returned an error (%d)\n", status); 168 | } 169 | 170 | } 171 | } // end while (IODataQueueDataAvailable(queueMappedMemory)) 172 | } // end while (IODataQueueWaitForAvailableData 173 | 174 | exit: 175 | 176 | kr = IOConnectUnmapMemory(connection, kIODefaultMemoryType, 177 | mach_task_self(), address); 178 | if (kr != kIOReturnSuccess) 179 | fprintf(stderr, "failed to unmap memory (%d)\n", kr); 180 | 181 | mach_port_destroy(mach_task_self(), recvPort); 182 | 183 | return; 184 | } 185 | 186 | 187 | int main(int argc, const char * argv[]) 188 | { 189 | kern_return_t kr; 190 | int ret; 191 | int opt; 192 | 193 | setbuf(stdout, NULL); 194 | 195 | // 196 | // load the driver 197 | // TO DO 198 | 199 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, 200 | IOServiceMatching("com_VFSFilter0")); 201 | 202 | if (!service){ 203 | fprintf(stderr, "IOServiceGetMatchingService failed to retrieve matching objects\n" ); 204 | return -1; 205 | } 206 | 207 | io_connect_t connection; 208 | 209 | kern_return_t status = IOServiceOpen(service, 210 | mach_task_self(), 211 | 0, 212 | &connection); 213 | 214 | IOObjectRelease(service); 215 | 216 | if (status != kIOReturnSuccess){ 217 | fprintf(stderr, "IOServiceOpen failed to open com_VFSFilter0 driver\n" ); 218 | return -1; 219 | } 220 | 221 | kr = IOConnectCallScalarMethod(connection, kt_kVnodeWatcherUserClientOpen, NULL, 0, NULL, NULL); 222 | if (kr != KERN_SUCCESS) { 223 | IOServiceClose(connection); 224 | 225 | fprintf(stderr, "com_VFSFilter0 service is busy\n"); 226 | return -1; 227 | } 228 | 229 | pthread_t dataQueueThread; 230 | 231 | ret = pthread_create(&dataQueueThread, (pthread_attr_t *)0, 232 | (void *(*)(void *))VFSFilter0NotificationHandler, (void *)&connection); 233 | if (ret) 234 | perror("pthread_create"); 235 | else 236 | pthread_join(dataQueueThread, (void **)&kr); 237 | 238 | (void)IOServiceClose(connection); 239 | 240 | return 0; 241 | } 242 | 243 | 244 | int cp(const char *to, const char *from) 245 | { 246 | int fd_to, fd_from; 247 | char buf[4096]; 248 | ssize_t nread; 249 | int saved_errno; 250 | 251 | fd_from = open(from, O_RDONLY); 252 | if (fd_from < 0) 253 | return -1; 254 | 255 | ////////////////// 256 | /* 257 | fd_to = open(to, O_RDONLY); 258 | if (fd_to > 0){ 259 | close(fd_from); 260 | close(fd_to); 261 | return 0; 262 | } 263 | */ 264 | /////////////// 265 | 266 | printf("copying data from %s to %s\n", from, to); 267 | 268 | fd_to = open(to, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0666); 269 | if (fd_to < 0) 270 | goto out_error; 271 | 272 | while (nread = read(fd_from, buf, sizeof buf), nread > 0) 273 | { 274 | char *out_ptr = buf; 275 | ssize_t nwritten; 276 | 277 | do { 278 | nwritten = write(fd_to, out_ptr, nread); 279 | 280 | if (nwritten >= 0) 281 | { 282 | nread -= nwritten; 283 | out_ptr += nwritten; 284 | } 285 | else if (errno != EINTR) 286 | { 287 | goto out_error; 288 | } 289 | } while (nread > 0); 290 | } 291 | 292 | if (nread == 0) 293 | { 294 | if (close(fd_to) < 0) 295 | { 296 | fd_to = -1; 297 | goto out_error; 298 | } 299 | close(fd_from); 300 | 301 | /* Success! */ 302 | return 0; 303 | } 304 | 305 | out_error: 306 | saved_errno = errno; 307 | 308 | close(fd_from); 309 | if (fd_to >= 0) 310 | close(fd_to); 311 | 312 | errno = saved_errno; 313 | return -1; 314 | } 315 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/Kauth.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Kauth.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 3/06/2015. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include "Kauth.h" 10 | #include "VFSHooks.h" 11 | #include "VFSFilter0.h" 12 | #include "VNodeHook.h" 13 | #include "ApplicationsData.h" 14 | #include "VNode.h" 15 | #include "WaitingList.h" 16 | #include "VersionDependent.h" 17 | 18 | //-------------------------------------------------------------------- 19 | 20 | QvrIOKitKAuthVnodeGate* gVnodeGate; 21 | 22 | //-------------------------------------------------------------------- 23 | 24 | #define super OSObject 25 | OSDefineMetaClassAndStructors( QvrIOKitKAuthVnodeGate, OSObject) 26 | 27 | //-------------------------------------------------------------------- 28 | 29 | IOReturn 30 | QvrIOKitKAuthVnodeGate::RegisterVnodeScopeCallback(void) 31 | { 32 | // 33 | // register our listener 34 | // 35 | this->VnodeListener = kauth_listen_scope( KAUTH_SCOPE_VNODE, // for the vnode scope 36 | QvrIOKitKAuthVnodeGate::VnodeAuthorizeCallback, // using this callback 37 | this ); // give a cookie to callback 38 | 39 | if( NULL == this->VnodeListener ){ 40 | 41 | DBG_PRINT_ERROR( ( "kauth_listen_scope failed\n" ) ); 42 | return kIOReturnInternalError; 43 | 44 | } 45 | 46 | return kIOReturnSuccess; 47 | } 48 | 49 | //-------------------------------------------------------------------- 50 | 51 | int 52 | QvrIOKitKAuthVnodeGate::VnodeAuthorizeCallback( 53 | kauth_cred_t credential, // reference to the actor's credentials 54 | void *idata, // cookie supplied when listener is registered 55 | kauth_action_t action, // requested action 56 | uintptr_t arg0, // the VFS context 57 | uintptr_t arg1, // the vnode in question 58 | uintptr_t arg2, // parent vnode, or NULL 59 | uintptr_t arg3) // pointer to an errno value 60 | { 61 | QvrIOKitKAuthVnodeGate* _this; 62 | vnode_t vnode = (vnode_t)arg1; 63 | 64 | assert( preemption_enabled() ); 65 | 66 | // 67 | // if this is a dead vnode then skip it 68 | // 69 | if( vnode_isrecycled( vnode ) ) 70 | return KAUTH_RESULT_DEFER; 71 | 72 | _this = (QvrIOKitKAuthVnodeGate*)idata; 73 | 74 | // 75 | // VNON vnode is created by devfs_devfd_lookup() for /dev/fd/X vnodes that 76 | // are not of any interest for us 77 | // VSOCK is created for UNIX sockets 78 | // etc. 79 | // 80 | enum vtype vnodeType = vnode_vtype( vnode ); 81 | if( VREG != vnodeType && 82 | VDIR != vnodeType ) 83 | return KAUTH_RESULT_DEFER; 84 | 85 | const ApplicationData* appData = QvrGetApplicationDataByContext( (vfs_context_t)arg0, ADT_OpenExisting ); 86 | if( appData ){ 87 | 88 | QvrHookVnodeVopAndParent( vnode ); 89 | cache_purge( vnode ); 90 | } 91 | 92 | const ApplicationData* appDataForVnode = VNodeMap::getVnodeAppData( vnode ); 93 | 94 | // 95 | // check that a trusted application is trying to open a protected file 96 | // 97 | bool allowAccess = true; //( appDataForVnode == NULL ) || ( appData == appDataForVnode ); 98 | 99 | return allowAccess? KAUTH_RESULT_DEFER : KAUTH_RESULT_DENY; 100 | } 101 | 102 | //-------------------------------------------------------------------- 103 | 104 | int 105 | QvrIOKitKAuthVnodeGate::MacVnodeCheckLookup( kauth_cred_t cred, 106 | struct vnode *dvp, 107 | struct label *dlabel, 108 | struct componentname *cnp 109 | ) 110 | { 111 | const ApplicationData* appData = QvrGetApplicationDataByContext( vfs_context_current(), ADT_OpenExisting ); 112 | 113 | if( ! appData || RecursionEngine::IsRecursiveCall() ) 114 | return 0; 115 | 116 | // 117 | // Force lookup to go to the filesystem, vnode_lookup() and 118 | // cache_purge( vnode ) can't be called here 119 | // as the caller holds nonrecursive namei lock 120 | // that is reacquired by vnode_lookup and cache_purge, 121 | // FYI a callstack with a held lock and a deadlock 122 | // in cache_purge 123 | /* 124 | 0xffffff809d883820 0xffffff800501a30f machine_switch_context((thread_t) old = 0xffffff80132babf0, (thread_continue_t) continuation = 0x0000000000000000, (thread_t) new = 0xffffff801367da80) 125 | 0xffffff809d8838b0 0xffffff8004f52ddc thread_invoke((thread_t) self = 0xffffff80132babf0, (thread_t) thread = , , (ast_t) reason = <>, ) 126 | 0xffffff809d8838f0 0xffffff8004f5084f thread_block_reason((thread_continue_t) continuation = <>, , (void *) parameter = <>, , (ast_t) reason = , ) 127 | 0xffffff809d883960 0xffffff8005016a08 lck_rw_lock_exclusive_gen((lck_rw_t *) lck = 0xffffff801021a200) 128 | 0xffffff809d883990 0xffffff80051333d8 name_cache_lock [inlined](void) 129 | 0xffffff809d883990 0xffffff80051333cc cache_purge((vnode_t) vp = 0xffffff80130591e0) 130 | 0xffffff809d8839e0 0xffffff7f876eb696 QvrIOKitKAuthVnodeGate::MacVnodeCheckLookup(ucred*, vnode*, label*, componentname*)((kauth_cred_t) cred = 0xffffff8012f5eea0, (vnode *) dvp = 0xffffff80102f6960, (label *) dlabel = 0x0000000000000000, (componentname *) cnp = 0xffffff809d883e88) 131 | 0xffffff809d883a20 0xffffff800553889e mac_vnode_check_lookup((vfs_context_t) ctx = <>, , (vnode *) dvp = 0xffffff80102f6960, (componentname *) cnp = 0xffffff809d883e88) 132 | 0xffffff809d883aa0 0xffffff8005132352 cache_lookup_path((nameidata *) ndp = 0xffffff809d883d38, (componentname *) cnp = , , (vnode_t) dp = <>, , (vfs_context_t) ctx = 0xffffff80133bf3f0, (int *) dp_authorized = 0xffffff809d883b04, (vnode_t) last_dp = 0x0000000000000000) 133 | 0xffffff809d883b80 0xffffff800513efed lookup((nameidata *) ndp = 0xffffff809d883d38) 134 | 0xffffff809d883cb0 0xffffff800513ea95 namei((nameidata *) ndp = 0xffffff809d883d38) 135 | 0xffffff809d883d00 0xffffff8005152005 nameiat((nameidata *) ndp = 0xffffff809d883d38, (int) dirfd = <>, ) 136 | 0xffffff809d883f00 0xffffff8005129fd6 getattrlistat_internal((vfs_context_t) ctx = 0xffffff80133bf3f0, (user_addr_t) path = , , (attrlist *) alp = 0xffffff809d883f28, (user_addr_t) attributeBuffer = 3220249968, (size_t) bufferSize = 918, (uint64_t) options = 37, (uio_seg) segflg = , , (uio_seg) pathsegflg = <>, , (int) fd = , ) 137 | 0xffffff809d883f50 0xffffff8005123227 getattrlist((proc_t) p = <>, , (getattrlist_args *) uap = <>, , (int32_t *) retval = <>, ) 138 | 0xffffff809d883fb0 0xffffff800544d924 unix_syscall((x86_saved_state_t *) state = 0xffffff8013299020) 139 | */ 140 | // 141 | // N.B. the same is true for every function that calls namei() e.g. vnode_lookup() will deadlock if called in MacVnodeCheckLookup 142 | // 143 | 144 | // 145 | // instead of cache_purge use a flag that disables cache_lookup_locked() calling, 146 | // CN_SKIPNAMECACHE is a private flag and its value must be verified for each Mac OS X major release 147 | // 148 | cnp->cn_flags |= CN_SKIPNAMECACHE; 149 | 150 | return 0; 151 | } 152 | 153 | //-------------------------------------------------------------------- 154 | 155 | QvrIOKitKAuthVnodeGate* QvrIOKitKAuthVnodeGate::withCallbackRegistration( 156 | __in com_VFSFilter0* _provider 157 | ) 158 | /* 159 | the caller must call the release() function for the returned object when it is not longer needed 160 | */ 161 | { 162 | IOReturn RC; 163 | QvrIOKitKAuthVnodeGate* pKAuthVnodeGate; 164 | 165 | pKAuthVnodeGate = new QvrIOKitKAuthVnodeGate(); 166 | assert( pKAuthVnodeGate ); 167 | if( !pKAuthVnodeGate ){ 168 | 169 | DBG_PRINT_ERROR( ( "QvrIOKitKAuthVnodeGate::withCallbackRegistration QvrIOKitKAuthVnodeGate allocation failed\n" ) ); 170 | return NULL; 171 | } 172 | 173 | // 174 | // IOKit base classes initialization 175 | // 176 | if( !pKAuthVnodeGate->init() ){ 177 | 178 | DBG_PRINT_ERROR( ( "QvrIOKitKAuthVnodeGate::withCallbackRegistration init() failed\n" ) ); 179 | pKAuthVnodeGate->release(); 180 | return NULL; 181 | } 182 | 183 | pKAuthVnodeGate->macPolicy = QvrMacPolicy::createPolicyObject( &pKAuthVnodeGate->mpcServiceProtection, NULL ); 184 | assert( pKAuthVnodeGate->macPolicy ); 185 | if( !pKAuthVnodeGate->macPolicy ){ 186 | 187 | DBG_PRINT_ERROR( ( "QvrMacPolicy::createPolicyObject() failed\n" ) ); 188 | pKAuthVnodeGate->release(); 189 | return NULL; 190 | } 191 | 192 | pKAuthVnodeGate->provider = _provider; 193 | 194 | // 195 | // register the callback, it will be active immediatelly after registration, i.e. before control leaves the function 196 | // 197 | RC = pKAuthVnodeGate->RegisterVnodeScopeCallback(); 198 | assert( kIOReturnSuccess == RC ); 199 | if( kIOReturnSuccess != RC ){ 200 | 201 | DBG_PRINT_ERROR( ( "pKAuthVnodeGate->RegisterVnodeScopeCallback() failed with the 0x%X error\n", RC ) ); 202 | pKAuthVnodeGate->release(); 203 | return NULL; 204 | } 205 | 206 | pKAuthVnodeGate->macPolicy->registerMacPolicy(); 207 | 208 | return pKAuthVnodeGate; 209 | } 210 | 211 | //-------------------------------------------------------------------- 212 | 213 | bool QvrIOKitKAuthVnodeGate::init() 214 | { 215 | if(! super::init() ) 216 | return false; 217 | 218 | mpoServiceProtection.mpo_vnode_check_lookup = &QvrIOKitKAuthVnodeGate::MacVnodeCheckLookup; 219 | 220 | mpcServiceProtection.mpc_name = "MYFILTER"; 221 | mpcServiceProtection.mpc_fullname = "My Filter MAC"; 222 | mpcServiceProtection.mpc_field_off = NULL; /* no label slot */ 223 | mpcServiceProtection.mpc_labelnames = NULL; /* no policy label names */ 224 | mpcServiceProtection.mpc_labelname_count = 0; /* count of label names is 0 */ 225 | mpcServiceProtection.mpc_ops = &mpoServiceProtection; /* policy operations */ 226 | mpcServiceProtection.mpc_loadtime_flags = 0; 227 | mpcServiceProtection.mpc_runtime_flags = 0; 228 | 229 | return true; 230 | } 231 | 232 | //-------------------------------------------------------------------- 233 | 234 | void QvrIOKitKAuthVnodeGate::free() 235 | { 236 | if( macPolicy ){ 237 | macPolicy->unRegisterMacPolicy(); 238 | macPolicy->release(); 239 | } 240 | 241 | super::free(); 242 | } 243 | 244 | //-------------------------------------------------------------------- 245 | 246 | void QvrIOKitKAuthVnodeGate::sendVFSDataToClient( __in struct _VFSData* data ) 247 | { 248 | provider->sendVFSDataToClient( data ); 249 | } 250 | 251 | //-------------------------------------------------------------------- 252 | 253 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VersionDependent.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // VersionDependent.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 21/06/2015. 6 | // Copyright (c) 2015 slava. All rights reserved. 7 | // 8 | 9 | #include "Common.h" 10 | #include "VersionDependent.h" 11 | #include "VNodeHook.h" 12 | #include 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | errno_t 17 | QvrVnodeGetSize(vnode_t vp, off_t *sizep, vfs_context_t ctx) 18 | { 19 | struct vnode_attr va; 20 | int error; 21 | 22 | VATTR_INIT(&va); 23 | VATTR_WANTED(&va, va_data_size); 24 | error = vnode_getattr(vp, &va, ctx); 25 | if (!error) 26 | *sizep = va.va_data_size; 27 | return(error); 28 | } 29 | 30 | //-------------------------------------------------------------------- 31 | 32 | errno_t 33 | QvrVnodeSetsize(vnode_t vp, off_t size, int ioflag, vfs_context_t ctx) 34 | { 35 | struct vnode_attr va; 36 | 37 | VATTR_INIT(&va); 38 | VATTR_SET(&va, va_data_size, size); 39 | va.va_vaflags = ioflag & 0xffff; 40 | return(vnode_setattr(vp, &va, ctx)); 41 | } 42 | 43 | //-------------------------------------------------------------------- 44 | 45 | // 46 | // The following code is full of lie. For a commercial release a version 47 | // independent hooker must be implemented. Look at 48 | // https://github.com/slavaim/MacOSX-FileSystem-Filter/blob/master/FsdFilter/FltFakeFSD.cpp#L1902 49 | // for an implementation for vnode structure layout. 50 | // 51 | 52 | #if !defined(MAC_OS_X_VERSION_10_10) 53 | #error "Looks like you are compiling with the old SDK" 54 | #endif 55 | 56 | // start of macOS version switching 57 | #if !defined(MAC_OS_X_VERSION_10_11) || MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_11 58 | 59 | //////////////////////////////////////////////////// 60 | // 61 | // compile for 10.11 or older version 62 | // 63 | ///////////////////////////////////////////////////// 64 | 65 | /* 66 | * This structure describes the vnode operation taking place. 67 | */ 68 | struct vnodeop_desc_Yosemite { 69 | int vdesc_offset; /* offset in vector--first for speed */ 70 | const char *vdesc_name; /* a readable name for debugging */ 71 | int vdesc_flags; /* VDESC_* flags */ 72 | 73 | /* 74 | * These ops are used by bypass routines to map and locate arguments. 75 | * Creds and procs are not needed in bypass routines, but sometimes 76 | * they are useful to (for example) transport layers. 77 | * Nameidata is useful because it has a cred in it. 78 | */ 79 | int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ 80 | int vdesc_vpp_offset; /* return vpp location */ 81 | int vdesc_cred_offset; /* cred location, if any */ 82 | int vdesc_proc_offset; /* proc location, if any */ 83 | int vdesc_componentname_offset; /* if any */ 84 | int vdesc_context_offset; /* context location, if any */ 85 | /* 86 | * Finally, we've got a list of private data (about each operation) 87 | * for each transport layer. (Support to manage this list is not 88 | * yet part of BSD.) 89 | */ 90 | caddr_t *vdesc_transports; 91 | }; 92 | 93 | LIST_HEAD(buflists_Yosemite, buf); 94 | SLIST_HEAD(klist_Yosemite, knote); 95 | 96 | typedef struct { 97 | unsigned long opaque[2]; 98 | } lck_mtx_t_Yosemite; 99 | 100 | 101 | struct vnode_Yosemite { 102 | lck_mtx_t_Yosemite v_lock; /* vnode mutex */ 103 | TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ 104 | TAILQ_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ 105 | LIST_HEAD(, namecache) v_nclinks; /* name cache entries that name this vnode */ 106 | LIST_HEAD(, namecache) v_ncchildren; /* name cache entries that regard us as their parent */ 107 | vnode_t v_defer_reclaimlist; /* in case we have to defer the reclaim to avoid recursion */ 108 | uint32_t v_listflag; /* flags protected by the vnode_list_lock (see below) */ 109 | uint32_t v_flag; /* vnode flags (see below) */ 110 | uint16_t v_lflag; /* vnode local and named ref flags */ 111 | uint8_t v_iterblkflags; /* buf iterator flags */ 112 | uint8_t v_references; /* number of times io_count has been granted */ 113 | int32_t v_kusecount; /* count of in-kernel refs */ 114 | int32_t v_usecount; /* reference count of users */ 115 | int32_t v_iocount; /* iocounters */ 116 | void * v_owner; /* act that owns the vnode */ 117 | uint16_t v_type; /* vnode type */ 118 | uint16_t v_tag; /* type of underlying data */ 119 | uint32_t v_id; /* identity of vnode contents */ 120 | union { 121 | struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ 122 | struct socket *vu_socket; /* unix ipc (VSOCK) */ 123 | struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ 124 | struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ 125 | struct ubc_info *vu_ubcinfo; /* valid for (VREG) */ 126 | } v_un; 127 | struct buflists_Yosemite v_cleanblkhd; /* clean blocklist head */ 128 | struct buflists_Yosemite v_dirtyblkhd; /* dirty blocklist head */ 129 | struct klist_Yosemite v_knotes; /* knotes attached to this vnode */ 130 | /* 131 | * the following 4 fields are protected 132 | * by the name_cache_lock held in 133 | * excluive mode 134 | */ 135 | kauth_cred_t v_cred; /* last authorized credential */ 136 | kauth_action_t v_authorized_actions; /* current authorized actions for v_cred */ 137 | int v_cred_timestamp; /* determine if entry is stale for MNTK_AUTH_OPAQUE */ 138 | int v_nc_generation; /* changes when nodes are removed from the name cache */ 139 | /* 140 | * back to the vnode lock for protection 141 | */ 142 | int32_t v_numoutput; /* num of writes in progress */ 143 | int32_t v_writecount; /* reference count of writers */ 144 | const char *v_name; /* name component of the vnode */ 145 | vnode_t v_parent; /* pointer to parent vnode */ 146 | struct lockf *v_lockf; /* advisory lock list head */ 147 | int (**v_op)(void *); /* vnode operations vector */ 148 | mount_t v_mount; /* ptr to vfs we are in */ 149 | void * v_data; /* private data for fs */ 150 | #if CONFIG_MACF 151 | struct label *v_label; /* MAC security label */ 152 | #endif 153 | #if CONFIG_TRIGGERS 154 | vnode_resolve_t v_resolve; /* trigger vnode resolve info (VDIR only) */ 155 | #endif /* CONFIG_TRIGGERS */ 156 | }; 157 | 158 | typedef struct vnode_Yosemite vnode_internal; 159 | 160 | #define VNOP_OFFSET( vnodeop_desc ) (sizeof(VOPFUNC)*((vnodeop_desc_Yosemite*)&vnodeop_desc)->vdesc_offset) 161 | 162 | struct mount_header_Yosemite { 163 | TAILQ_ENTRY(mount) mnt_list; /* mount list */ 164 | int32_t mnt_count; /* reference on the mount */ 165 | lck_mtx_t_Yosemite mnt_mlock; /* mutex that protects mount point */ 166 | struct vfsops *mnt_op; /* operations on fs */ 167 | struct vfstable *mnt_vtable; /* configuration info */ 168 | }; 169 | 170 | struct vfstable_header_Yosemite { 171 | struct vfsops *vfc_vfsops; /* filesystem operations vector */ 172 | char vfc_name[MFSNAMELEN]; /* filesystem type name */ 173 | int vfc_typenum; /* historic filesystem type number */ 174 | int vfc_refcount; /* number mounted of this type */ 175 | int vfc_flags; /* permanent flags */ 176 | int (*vfc_mountroot)(mount_t, vnode_t, vfs_context_t); /* if != NULL, routine to mount root */ 177 | struct vfstable *vfc_next; /* next in list */ 178 | int32_t vfc_reserved1; 179 | int32_t vfc_reserved2; 180 | int vfc_vfsflags; /* for optional types */ 181 | void * vfc_descptr; /* desc table allocated address */ 182 | int vfc_descsize; /* size allocated for desc table */ 183 | struct sysctl_oid *vfc_sysctl; /* dynamically registered sysctl node */ 184 | }; 185 | 186 | typedef struct mount_header_Yosemite mount_header_internal; 187 | typedef struct vfstable_header_Yosemite vfstable_header_internal; 188 | 189 | //////////////////////////////////////////////////// 190 | // 191 | // end of 10.11 or older version definitions 192 | // 193 | ///////////////////////////////////////////////////// 194 | 195 | #else 196 | 197 | //////////////////////////////////////////////////// 198 | // 199 | // compile for Sierra(10.12) 200 | // 201 | ///////////////////////////////////////////////////// 202 | 203 | /* 204 | * This structure describes the vnode operation taking place. 205 | */ 206 | struct vnodeop_desc_Sierra { 207 | int vdesc_offset; /* offset in vector--first for speed */ 208 | const char *vdesc_name; /* a readable name for debugging */ 209 | int vdesc_flags; /* VDESC_* flags */ 210 | 211 | /* 212 | * These ops are used by bypass routines to map and locate arguments. 213 | * Creds and procs are not needed in bypass routines, but sometimes 214 | * they are useful to (for example) transport layers. 215 | * Nameidata is useful because it has a cred in it. 216 | */ 217 | int *vdesc_vp_offsets; /* list ended by VDESC_NO_OFFSET */ 218 | int vdesc_vpp_offset; /* return vpp location */ 219 | int vdesc_cred_offset; /* cred location, if any */ 220 | int vdesc_proc_offset; /* proc location, if any */ 221 | int vdesc_componentname_offset; /* if any */ 222 | int vdesc_context_offset; /* context location, if any */ 223 | /* 224 | * Finally, we've got a list of private data (about each operation) 225 | * for each transport layer. (Support to manage this list is not 226 | * yet part of BSD.) 227 | */ 228 | caddr_t *vdesc_transports; 229 | }; 230 | 231 | LIST_HEAD(buflists_Sierra, buf); 232 | SLIST_HEAD(klist_Sierra, knote); 233 | 234 | typedef struct { 235 | unsigned long opaque[2]; 236 | } lck_mtx_t_Sierra; 237 | 238 | struct vnode_Sierra { 239 | lck_mtx_t_Sierra v_lock; /* vnode mutex */ 240 | TAILQ_ENTRY(vnode) v_freelist; /* vnode freelist */ 241 | TAILQ_ENTRY(vnode) v_mntvnodes; /* vnodes for mount point */ 242 | TAILQ_HEAD(, namecache) v_ncchildren; /* name cache entries that regard us as their parent */ 243 | LIST_HEAD(, namecache) v_nclinks; /* name cache entries that name this vnode */ 244 | vnode_t v_defer_reclaimlist; /* in case we have to defer the reclaim to avoid recursion */ 245 | uint32_t v_listflag; /* flags protected by the vnode_list_lock (see below) */ 246 | uint32_t v_flag; /* vnode flags (see below) */ 247 | uint16_t v_lflag; /* vnode local and named ref flags */ 248 | uint8_t v_iterblkflags; /* buf iterator flags */ 249 | uint8_t v_references; /* number of times io_count has been granted */ 250 | int32_t v_kusecount; /* count of in-kernel refs */ 251 | int32_t v_usecount; /* reference count of users */ 252 | int32_t v_iocount; /* iocounters */ 253 | void * v_owner; /* act that owns the vnode */ 254 | uint16_t v_type; /* vnode type */ 255 | uint16_t v_tag; /* type of underlying data */ 256 | uint32_t v_id; /* identity of vnode contents */ 257 | union { 258 | struct mount *vu_mountedhere;/* ptr to mounted vfs (VDIR) */ 259 | struct socket *vu_socket; /* unix ipc (VSOCK) */ 260 | struct specinfo *vu_specinfo; /* device (VCHR, VBLK) */ 261 | struct fifoinfo *vu_fifoinfo; /* fifo (VFIFO) */ 262 | struct ubc_info *vu_ubcinfo; /* valid for (VREG) */ 263 | } v_un; 264 | struct buflists_Sierra v_cleanblkhd; /* clean blocklist head */ 265 | struct buflists_Sierra v_dirtyblkhd; /* dirty blocklist head */ 266 | struct klist_Sierra v_knotes; /* knotes attached to this vnode */ 267 | /* 268 | * the following 4 fields are protected 269 | * by the name_cache_lock held in 270 | * excluive mode 271 | */ 272 | kauth_cred_t v_cred; /* last authorized credential */ 273 | kauth_action_t v_authorized_actions; /* current authorized actions for v_cred */ 274 | int v_cred_timestamp; /* determine if entry is stale for MNTK_AUTH_OPAQUE */ 275 | int v_nc_generation; /* changes when nodes are removed from the name cache */ 276 | /* 277 | * back to the vnode lock for protection 278 | */ 279 | int32_t v_numoutput; /* num of writes in progress */ 280 | int32_t v_writecount; /* reference count of writers */ 281 | const char *v_name; /* name component of the vnode */ 282 | vnode_t v_parent; /* pointer to parent vnode */ 283 | struct lockf *v_lockf; /* advisory lock list head */ 284 | int (**v_op)(void *); /* vnode operations vector */ 285 | mount_t v_mount; /* ptr to vfs we are in */ 286 | void * v_data; /* private data for fs */ 287 | #if CONFIG_MACF 288 | struct label *v_label; /* MAC security label */ 289 | #endif 290 | #if CONFIG_TRIGGERS 291 | vnode_resolve_t v_resolve; /* trigger vnode resolve info (VDIR only) */ 292 | #endif /* CONFIG_TRIGGERS */ 293 | }; 294 | 295 | typedef struct vnode_Sierra vnode_internal; 296 | 297 | #define VNOP_OFFSET( vnodeop_desc ) (sizeof(VOPFUNC)*((vnodeop_desc_Sierra*)&vnodeop_desc)->vdesc_offset) 298 | 299 | struct mount_header_Sierra { 300 | TAILQ_ENTRY(mount) mnt_list; /* mount list */ 301 | int32_t mnt_count; /* reference on the mount */ 302 | lck_mtx_t_Sierra mnt_mlock; /* mutex that protects mount point */ 303 | struct vfsops *mnt_op; /* operations on fs */ 304 | struct vfstable *mnt_vtable; /* configuration info */ 305 | }; 306 | 307 | struct vfstable_header_Sierra { 308 | struct vfsops *vfc_vfsops; /* filesystem operations vector */ 309 | char vfc_name[MFSNAMELEN]; /* filesystem type name */ 310 | int vfc_typenum; /* historic filesystem type number */ 311 | int vfc_refcount; /* number mounted of this type */ 312 | int vfc_flags; /* permanent flags */ 313 | int (*vfc_mountroot)(mount_t, vnode_t, vfs_context_t); /* if != NULL, routine to mount root */ 314 | struct vfstable *vfc_next; /* next in list */ 315 | int32_t vfc_reserved1; 316 | int32_t vfc_reserved2; 317 | int vfc_vfsflags; /* for optional types */ 318 | void * vfc_descptr; /* desc table allocated address */ 319 | int vfc_descsize; /* size allocated for desc table */ 320 | struct sysctl_oid *vfc_sysctl; /* dynamically registered sysctl node */ 321 | }; 322 | 323 | typedef struct mount_header_Sierra mount_header_internal; 324 | typedef struct vfstable_header_Sierra vfstable_header_internal; 325 | 326 | //////////////////////////////////////////////////// 327 | // 328 | // end of 10.12 version definitions 329 | // 330 | ///////////////////////////////////////////////////// 331 | 332 | #endif // end of macOS version switching 333 | 334 | //-------------------------------------------------------------------- 335 | 336 | // 337 | // the gDldFakeVnodeopEntries array must be synchronized with the gQvrVnodeOpvOffsetDesc array, 338 | // i.e. the arrays must contain the same number and types of entries excluding the terminating ones 339 | // and the vnop_default_desc entry 340 | // 341 | static QvrVnodeOpvOffsetDesc gQvrVnodeOpvOffsetDesc[] = { 342 | 343 | { &vnop_lookup_desc, VNOP_OFFSET(vnop_lookup_desc) }, /* lookup */ 344 | { &vnop_create_desc, VNOP_OFFSET(vnop_create_desc) }, /* create */ 345 | { &vnop_open_desc, VNOP_OFFSET(vnop_open_desc) }, /* open */ 346 | { &vnop_close_desc, VNOP_OFFSET(vnop_close_desc) }, /* close */ 347 | { &vnop_inactive_desc, VNOP_OFFSET(vnop_inactive_desc) }, /* inactive */ 348 | { &vnop_reclaim_desc, VNOP_OFFSET(vnop_reclaim_desc) }, /* reclaim */ 349 | { &vnop_read_desc, VNOP_OFFSET(vnop_read_desc) }, /* read */ 350 | { &vnop_write_desc, VNOP_OFFSET(vnop_write_desc) }, /* write */ 351 | { &vnop_pagein_desc, VNOP_OFFSET(vnop_pagein_desc) }, /* Pagein */ 352 | { &vnop_pageout_desc, VNOP_OFFSET(vnop_pageout_desc) }, /* Pageout */ 353 | { &vnop_mmap_desc, VNOP_OFFSET(vnop_mmap_desc) }, /* mmap */ 354 | { &vnop_rename_desc, VNOP_OFFSET(vnop_rename_desc) }, /* rename */ 355 | { &vnop_exchange_desc, VNOP_OFFSET(vnop_exchange_desc) }, /* exchange */ 356 | { &vnop_getattr_desc, VNOP_OFFSET(vnop_getattr_desc) }, /* getattr */ 357 | { (struct vnodeop_desc*)NULL, QVR_VOP_UNKNOWN_OFFSET } 358 | }; 359 | 360 | // 361 | // exclude the terminating entry from the valid entries number 362 | // 363 | static int gNumberOfOpvOffsetDescs = sizeof( gQvrVnodeOpvOffsetDesc )/sizeof( gQvrVnodeOpvOffsetDesc[0] ) - 0x1; 364 | 365 | //-------------------------------------------------------------------- 366 | 367 | QvrVnodeOpvOffsetDesc* 368 | QvrRetriveVnodeOpvOffsetDescByVnodeOpDesc( 369 | __in struct vnodeop_desc *opve_op 370 | ) 371 | { 372 | for( int i = 0x0; NULL != gQvrVnodeOpvOffsetDesc[ i ].opve_op; ++i ){ 373 | 374 | if( opve_op == gQvrVnodeOpvOffsetDesc[ i ].opve_op ) 375 | return &gQvrVnodeOpvOffsetDesc[ i ]; 376 | 377 | }// end for 378 | 379 | return ( QvrVnodeOpvOffsetDesc* )NULL; 380 | } 381 | 382 | //-------------------------------------------------------------------- 383 | 384 | VOPFUNC* 385 | QvrGetVnodeOpVector( 386 | __in vnode_t vn 387 | ) 388 | { 389 | return ((vnode_internal*)vn)->v_op; 390 | } 391 | 392 | //-------------------------------------------------------------------- 393 | 394 | VOPFUNC 395 | QvrGetVnop( 396 | __in vnode_t vn, 397 | __in struct vnodeop_desc *opve_op 398 | ) 399 | { 400 | QvrVnodeOpvOffsetDesc* desc = QvrRetriveVnodeOpvOffsetDescByVnodeOpDesc( opve_op ); 401 | assert( desc ); 402 | if( ! desc ) 403 | return NULL; 404 | 405 | VOPFUNC* vnopVector = QvrGetVnodeOpVector( vn ); 406 | 407 | return vnopVector[ desc->offset / sizeof( VOPFUNC ) ]; 408 | } 409 | 410 | //-------------------------------------------------------------------- 411 | 412 | const char* 413 | GetVnodeNamePtr( 414 | __in vnode_t vn 415 | ) 416 | { 417 | return ((vnode_internal*)vn)->v_name; 418 | } 419 | 420 | //-------------------------------------------------------------------- 421 | 422 | int 423 | QvrGetVnodeVfsFlags( 424 | __in vnode_t vnode 425 | ) 426 | { 427 | // vp->v_mount->mnt_vtable->vfc_vfsflags & VFC_VFSVNOP_PAGEINV2 428 | 429 | mount_header_internal* v_mount = (mount_header_internal*)vnode_mount( vnode ); 430 | assert( v_mount ); 431 | vfstable_header_internal* mnt_vtable = (vfstable_header_internal*)v_mount->mnt_vtable; 432 | assert( mnt_vtable ); 433 | 434 | return mnt_vtable->vfc_vfsflags; 435 | } 436 | 437 | //-------------------------------------------------------------------- 438 | 439 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/CommonHashTable.h: -------------------------------------------------------------------------------- 1 | /* 2 | based on Simon Kagstrom's C hash table implementation 3 | Copyright (C) 2001-2005, Simon Kagstrom 4 | Modified by Slava Imameev 5 | */ 6 | 7 | #include "Common.h" 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | #ifdef __cplusplus 17 | } 18 | #endif 19 | 20 | /** 21 | * @file 22 | * libghthash is a generic hash table used for storing arbitrary 23 | * data. 24 | * 25 | * Libghthash really stores pointers to data - the hash 26 | * table knows nothing about the actual type of the data. 27 | * 28 | * A simple example to get started can be found in the 29 | * example/simple.c file found in the distribution. 30 | * hash_test.c provides a more comlpete example. 31 | * 32 | * Some basic properties of the hash table are: 33 | * 34 | * - Both the data stored and the keys are of void type, which 35 | * means that you can store any kind of data. 36 | * 37 | * - The only functions you probably will need to start is: 38 | * - ght_create(), which creates a new hash table. 39 | * - ght_insert(), which inserts a new entry into a table. 40 | * - ght_get(), which searches for an entry. 41 | * - ght_remove(), which removes and entry. 42 | * - ght_finalize(), which destroys a hash table. 43 | * 44 | * - Inserting entries is done without first creating a key, 45 | * i.e. you insert with the data, the datasize, the key and the 46 | * key size directly. 47 | * 48 | * - The hash table copies the key data when inserting new 49 | * entries. This means that you should not malloc() the key 50 | * before inserting a new entry. 51 | * 52 | */ 53 | #ifndef DLDCOMMONHASHTABLE_H 54 | #define DLDCOMMONHASHTABLE_H 55 | 56 | 57 | #define GHT_HEURISTICS_NONE 0 58 | #define GHT_HEURISTICS_TRANSPOSE 1 59 | #define GHT_HEURISTICS_MOVE_TO_FRONT 2 60 | #define GHT_AUTOMATIC_REHASH 4 61 | 62 | #ifndef TRUE 63 | #define TRUE 1 64 | #endif 65 | 66 | #ifndef FALSE 67 | #define FALSE 0 68 | #endif 69 | 70 | typedef enum _GHT_STATUS_CODE{ 71 | GHT_OK = 0x0, 72 | GHT_ALREADY_IN_HASH = (int)(-2), 73 | GHT_ERROR = (int)(-1) 74 | } GHT_STATUS_CODE; 75 | 76 | /** unsigned 32 bit integer. */ 77 | typedef unsigned int ght_uint32_t; 78 | 79 | /** 80 | * The structure for hash keys. You should not care about this 81 | * structure unless you plan to write your own hash functions. 82 | */ 83 | typedef struct s_hash_key 84 | { 85 | unsigned int i_size; /**< The size in bytes of the key p_key */ 86 | const void* p_key; /**< A pointer to the key. */ 87 | } ght_hash_key_t; 88 | 89 | /* 90 | * The structure for hash entries. 91 | * 92 | * LOCK: Should be possible to do somewhat atomically 93 | */ 94 | typedef struct s_hash_entry 95 | { 96 | #if defined( DBG ) 97 | // 98 | // a shadow copy of the key 99 | // 100 | ght_hash_key_t key_shadow; 101 | 102 | #endif//DBG 103 | // 104 | // put the most usable fields on a single cache line 105 | // 106 | struct s_hash_entry* p_next; 107 | struct s_hash_entry* p_prev; 108 | ght_hash_key_t key; 109 | void* p_data; 110 | 111 | struct s_hash_entry* p_older; 112 | struct s_hash_entry* p_newer; 113 | 114 | // 115 | // size of the alocation = sizeof(ght_hash_entry_t) + key_size 116 | // 117 | size_t size; 118 | 119 | } ght_hash_entry_t; 120 | 121 | /* 122 | * The structure used in iterations. You should not care about the 123 | * contents of this, it will be filled and updated by ght_first() and 124 | * ght_next(). 125 | */ 126 | typedef struct 127 | { 128 | ght_hash_entry_t *p_entry; /* The current entry */ 129 | ght_hash_entry_t *p_next; /* The next entry */ 130 | } ght_iterator_t; 131 | 132 | /** 133 | * Definition of the hash function pointers. @c ght_fn_hash_t should be 134 | * used when implementing new hash functions. Look at the supplied 135 | * hash functions, like @c ght_one_at_a_time_hash(), for examples of hash 136 | * functions. 137 | * 138 | * @param p_key the key to calculate the hash value for. 139 | * 140 | * @return a 32 bit hash value. 141 | * 142 | * @see @c ght_one_at_a_time_hash(), @c ght_rotating_hash(), 143 | * @c ght_crc_hash() 144 | */ 145 | typedef ght_uint32_t (*ght_fn_hash_t)(ght_hash_key_t *p_key); 146 | 147 | __BEGIN_DECLS 148 | 149 | /** 150 | * Definition of the allocation function pointers. This is simply the 151 | * same definition as @c mac_kalloc(). 152 | * 153 | * @param size the size to allocate. This will always be 154 | * sizeof(ght_hash_entry_t) + key_size. 155 | * 156 | * @param how - one of the flags 157 | * M_WAITOK 0x0000 158 | * M_NOWAIT 0x0001 159 | * 160 | * @return a pointer to the allocated region, or NULL if the 161 | * allocation failed. 162 | */ 163 | typedef void *(*ght_fn_alloc_t)(vm_size_t size, int how); 164 | 165 | /** 166 | * Definition of the deallocation function pointers. This is simply the 167 | * same definition as @c mac_kfree(). 168 | * 169 | * @param ptr a pointer to the region to free. 170 | */ 171 | typedef void (*ght_fn_free_t)(void *ptr, vm_size_t size ); 172 | 173 | __END_DECLS 174 | 175 | /** 176 | * Definition of bounded bucket free callback function pointers. 177 | * 178 | * The keys is passed back as const, since it was accepted by ght_insert() 179 | * as const, but if the callback function knows that a non-const pointer 180 | * was passed in, it can cast it back to non-const. 181 | */ 182 | typedef void (*ght_fn_bucket_free_callback_t)(void *data, const void *key); 183 | 184 | /** 185 | * The hash table structure. 186 | */ 187 | typedef struct 188 | { 189 | unsigned int i_items; /**< The current number of items in the table */ 190 | unsigned int i_size; /**< The number of buckets */ 191 | ght_fn_hash_t fn_hash; /**< The hash function used */ 192 | ght_fn_alloc_t fn_alloc; /**< The function used for allocating entries */ 193 | ght_fn_free_t fn_free; /**< The function used for freeing entries */ 194 | ght_fn_bucket_free_callback_t fn_bucket_free; /**< The function called when a bucket overflows */ 195 | int i_heuristics; /**< The type of heuristics used */ 196 | int i_automatic_rehash; /**< TRUE if automatic rehashing is used */ 197 | 198 | /* private: */ 199 | ght_hash_entry_t **pp_entries; 200 | int *p_nr; /* The number of entries in each bucket */ 201 | int i_size_mask; /* The number of bits used in the size */ 202 | unsigned int bucket_limit; 203 | 204 | bool non_block; /* TRUE if the allocations shoud not block */ 205 | 206 | ght_hash_entry_t *p_oldest; /* The entry inserted the earliest. */ 207 | ght_hash_entry_t *p_newest; /* The entry inserted the latest. */ 208 | } ght_hash_table_t; 209 | 210 | /** 211 | * Create a new hash table. The number of buckets should be about as 212 | * big as the number of elements you wish to store in the table for 213 | * good performance. The number of buckets is rounded to the next 214 | * higher power of two. 215 | * 216 | * The hash table is created with @c ght_one_at_a_time_hash() as hash 217 | * function, automatic rehashing disabled, @c malloc() as the memory 218 | * allocator and no heuristics. 219 | * 220 | * @param i_size the number of buckets in the hash table. Giving a 221 | * non-power of two here will round the size up to the next 222 | * power of two. 223 | * 224 | * @see ght_set_hash(), ght_set_heuristics(), ght_set_rehash(), 225 | * @see ght_set_alloc() 226 | * 227 | * @return a pointer to the hash table or NULL upon error. 228 | */ 229 | ght_hash_table_t* 230 | ght_create( 231 | __in unsigned int i_size, 232 | __in bool non_block 233 | ); 234 | 235 | /** 236 | * Set the allocation/freeing functions to use for a hash table. The 237 | * allocation function will only be called when a new entry is 238 | * inserted. 239 | * 240 | * The allocation size will always be sizeof(ght_hash_entry_t) + 241 | * sizeof(ght_hash_key_t) + key_size. The actual size varies with 242 | * the key size. 243 | * 244 | * If this function is not called, @c malloc() and @c free() 245 | * will be used for allocation and freeing. 246 | * 247 | * @warning Always call this function before any entries are 248 | * inserted into the table. Otherwise, the new free() might be called 249 | * on something that were allocated with another allocation function. 250 | * 251 | * @param p_ht the hash table to set the memory management functions 252 | * for. 253 | * @param fn_alloc the allocation function to use. 254 | * @param fn_free the deallocation function to use. 255 | */ 256 | void ght_set_alloc(ght_hash_table_t *p_ht, ght_fn_alloc_t fn_alloc, ght_fn_free_t fn_free); 257 | 258 | /** 259 | * Set the hash function to use for a hash table. 260 | * 261 | * @warning Always call this function before any entries are inserted 262 | * into the table. Otherwise, it will not be possible to find entries 263 | * that were inserted before this function was called. 264 | * 265 | * @param p_ht the hash table set the hash function for. 266 | * @param fn_hash the hash function. 267 | */ 268 | void ght_set_hash(ght_hash_table_t *p_ht, ght_fn_hash_t fn_hash); 269 | 270 | /** 271 | * Set the heuristics to use for the hash table. The possible values are: 272 | * 273 | * - GHT_HEURISTICS_NONE: Don't use any heuristics. 274 | * - 0: Same as above. 275 | * - GHT_HEURISTICS_TRANSPOSE: Use transposing heuristics. An 276 | * accessed element will move one step up in the bucket-list with this 277 | * method. 278 | * - GHT_HEURISTICS_MOVE_TO_FRONT: Use move-to-front 279 | * heuristics. An accessed element will be moved the front of the 280 | * bucket list with this method. 281 | * 282 | * @param p_ht the hash table set the heuristics for. 283 | * @param i_heuristics the heuristics to use. 284 | */ 285 | void ght_set_heuristics(ght_hash_table_t *p_ht, int i_heuristics); 286 | 287 | /** 288 | * Enable or disable automatic rehashing. 289 | * 290 | * With automatic rehashing, the table will rehash itself when the 291 | * number of elements in the table are twice as many as the number of 292 | * buckets. You should note that automatic rehashing will cause your 293 | * application to be really slow when the table is rehashing (which 294 | * might happen at times when you need speed), you should therefore be 295 | * careful with this in time-constrainted applications. 296 | * 297 | * @param p_ht the hash table to set rehashing for. 298 | * @param b_rehash TRUE if rehashing should be used or FALSE if it 299 | * should not be used. 300 | */ 301 | void ght_set_rehash(ght_hash_table_t *p_ht, int b_rehash); 302 | 303 | /** 304 | * Enable or disable bounded buckets. 305 | * 306 | * With bounded buckets, the hash table will act as a cache, only 307 | * holding a fixed number of elements per bucket. @a limit specifies 308 | * the limit of elements per bucket. When inserting elements with @a 309 | * ght_insert into a bounded table, the last entry in the bucket chain 310 | * will be free:d. libghthash will then call the callback function @a 311 | * fn, which allow the user of the library to dispose of the key and data. 312 | * 313 | * Bounded buckets are disabled by default. 314 | * 315 | * @param p_ht the hash table to set the bounded buckets for. 316 | * @param limit the maximum number of items in each bucket. If @a 317 | * limit is set to 0, bounded buckets are disabled. 318 | * @param fn a pointer to a callback function that is called when an 319 | * entry is free:d. The function should return 0 if the entry can be 320 | * freed, or -1 otherwise. If -1 is returned, libghthash will select 321 | * the second last entry and call the callback with that instead. 322 | */ 323 | void ght_set_bounded_buckets(ght_hash_table_t *p_ht, unsigned int limit, ght_fn_bucket_free_callback_t fn); 324 | 325 | 326 | /** 327 | * Get the size (the number of items) of the hash table. 328 | * 329 | * @param p_ht the hash table to get the size for. 330 | * 331 | * @return the number of items in the hash table. 332 | */ 333 | unsigned int ght_size(ght_hash_table_t *p_ht); 334 | 335 | /** 336 | * Get the table size (the number of buckets) of the hash table. 337 | * 338 | * @param p_ht the hash table to get the table size for. 339 | * 340 | * @return the number of buckets in the hash table. 341 | */ 342 | unsigned int ght_table_size(ght_hash_table_t *p_ht); 343 | 344 | 345 | /** 346 | * Insert an entry into the hash table. Prior to inserting anything, 347 | * make sure that the table is created with ght_create(). If an 348 | * element with the same key as this one already exists in the table, 349 | * the insertion will fail and -1 is returned. 350 | * 351 | * A typical example is shown below, where the string "blabla" 352 | * (including the '\0'-terminator) is used as a key for the integer 353 | * 15. 354 | * 355 | *
356 |  * ght_hash_table_t *p_table;
357 |  * char *p_key_data;
358 |  * int *p_data;
359 |  * int ret;
360 |  *
361 |  * [Create p_table etc...]
362 |  * p_data = malloc(sizeof(int));
363 |  * p_key_data = "blabla";
364 |  * *p_data = 15;
365 |  *
366 |  * ret = ght_insert(p_table,
367 |  *                  p_data,
368 |  *                  sizeof(char)*(strlen(p_key_data)+1), p_key_data);
369 |  * 
370 | * 371 | * @param p_ht the hash table to insert into. 372 | * @param p_entry_data the data to insert. 373 | * @param i_key_size the size of the key to associate the data with (in bytes). 374 | * @param p_key_data the key to use. The value will be copied, and it 375 | * is therefore OK to use a stack-allocated entry here. 376 | * 377 | * @returns 378 | * 0 if the element could be inserted, 379 | * -2 if the elemnt is already in the hash 380 | * -1 if there is an error ( mac_kalloc failed ) 381 | */ 382 | GHT_STATUS_CODE 383 | ght_insert( 384 | __in ght_hash_table_t *p_ht, 385 | __in void *p_entry_data, 386 | __in unsigned int i_key_size, 387 | __in const void *p_key_data 388 | ); 389 | 390 | /** 391 | * Replace an entry in the hash table. This function will return an 392 | * error if the entry to be replaced does not exist, i.e. it cannot be 393 | * used to insert new entries. Replacing an entry does not affect its 394 | * iteration order. 395 | * 396 | * @param p_ht the hash table to search in. 397 | * @param p_entry_data the new data for the key. 398 | * @param i_key_size the size of the key to search with (in bytes). 399 | * @param p_key_data the key to search for. 400 | * 401 | * @return a pointer to the old value or NULL if the operation failed. 402 | */ 403 | void *ght_replace(ght_hash_table_t *p_ht, 404 | void *p_entry_data, 405 | unsigned int i_key_size, const void *p_key_data); 406 | 407 | 408 | /** 409 | * Lookup an entry in the hash table. The entry is not removed from 410 | * the table. 411 | * 412 | * @param p_ht the hash table to search in. 413 | * @param i_key_size the size of the key to search with (in bytes). 414 | * @param p_key_data the key to search for. 415 | * 416 | * @return a pointer to the found entry or NULL if no entry could be found. 417 | */ 418 | void* 419 | ght_get( 420 | __in ght_hash_table_t *p_ht, 421 | __in unsigned int i_key_size, 422 | __in const void *p_key_data 423 | ); 424 | 425 | /** 426 | * Remove an entry from the hash table. The entry is removed from the 427 | * table, but not freed (that is, the data stored is not freed). 428 | * 429 | * @param p_ht the hash table to use. 430 | * @param i_key_size the size of the key to search with (in bytes). 431 | * @param p_key_data the key to search for. 432 | * 433 | * @return a pointer to the removed entry or NULL if the entry could be found. 434 | */ 435 | void* 436 | ght_remove( 437 | __in ght_hash_table_t *p_ht, 438 | __in unsigned int i_key_size, 439 | __in const void *p_key_data 440 | ); 441 | 442 | /** 443 | * Return the first entry in the hash table. This function should be 444 | * used for iteration and is used together with ght_next(). The order 445 | * of the entries will be from the oldest inserted entry to the newest 446 | * inserted entry. If an entry is inserted during an iteration, the entry 447 | * might or might not occur in the iteration. Note that removal during 448 | * an iteration is only safe for the current entry or an entry 449 | * which has already been iterated over. 450 | * 451 | * The use of the ght_iterator_t allows for several concurrent 452 | * iterations, where you would use one ght_iterator_t for each 453 | * iteration. In threaded environments, you should still lock access 454 | * to the hash table for insertion and removal. 455 | * 456 | * A typical example might look as follows: 457 | *
458 |  * ght_hash_table_t *p_table;
459 |  * ght_iterator_t iterator;
460 |  * void *p_key;
461 |  * void *p_e;
462 |  *
463 |  * [Create table etc...]
464 |  * for(p_e = ght_first(p_table, &iterator, &p_key); p_e; p_e = ght_next(p_table, &iterator, &p_key))
465 |  *   {
466 |  *      [Do something with the current entry p_e and it's key p_key]
467 |  *   }
468 |  * 
469 | * 470 | * @param p_ht the hash table to iterate through. 471 | * 472 | * @param p_iterator the iterator to use. The value of the structure 473 | * is filled in by this function and may be stack allocated. 474 | * 475 | * @param pp_key a pointer to the pointer of the key (NULL if none). 476 | * 477 | * @return a pointer to the first entry in the table or NULL if there 478 | * are no entries. 479 | * 480 | * 481 | * @see ght_next() 482 | */ 483 | void *ght_first(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key); 484 | 485 | /** 486 | * See ght_first() detailed description. This function augments 487 | * ght_first() by providing a facility to get the size of the keys 488 | * also. This interface is beneficial for hashtables which use 489 | * variable length keys. 490 | * 491 | * @param p_ht the hash table to iterate through. 492 | * 493 | * @param p_iterator the iterator to use. The value of the structure 494 | * is filled in by this function and may be stack allocated. 495 | * 496 | * @param pp_key a pointer to the pointer of the key (NULL if none). 497 | * 498 | * @param size a pointer to the size of the key pointer to by pp_key. 499 | * 500 | * @return a pointer to the first entry in the table or NULL if there 501 | * are no entries. 502 | * 503 | * 504 | * @see ght_next() 505 | */ 506 | 507 | void *ght_first_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size); 508 | 509 | /** 510 | * Return the next entry in the hash table. This function should be 511 | * used for iteration, and must be called after ght_first(). 512 | * 513 | * @warning calling this without first having called ght_first will 514 | * give undefined results (probably a crash), since p_iterator isn't 515 | * filled correctly. 516 | * 517 | * @param p_ht the hash table to iterate through. 518 | * 519 | * @param p_iterator the iterator to use. 520 | * 521 | * @param pp_key a pointer to the pointer of the key (NULL if none). 522 | * 523 | * @return a pointer to the next entry in the table or NULL if there 524 | * are no more entries in the table. 525 | * 526 | * @see ght_first() 527 | */ 528 | void *ght_next(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key); 529 | 530 | /** 531 | * This functions works just like ght_next() but also returns the 532 | * keysize. This is beneficial for users of the hash table which use 533 | * variable length keys. 534 | * 535 | * @warning calling this without first having called ght_first will 536 | * give undefined results (probably a crash), since p_iterator isn't 537 | * filled correctly. 538 | * 539 | * @param p_ht the hash table to iterate through. 540 | * 541 | * @param p_iterator the iterator to use. 542 | * 543 | * @param pp_key a pointer to the pointer of the key (NULL if none). 544 | * 545 | * @param size a pointer to the size of the key pointer to by pp_key. 546 | * 547 | * @return a pointer to the next entry in the table or NULL if there 548 | * are no more entries in the table. 549 | * 550 | * @see ght_first_keysize() 551 | */ 552 | 553 | void *ght_next_keysize(ght_hash_table_t *p_ht, ght_iterator_t *p_iterator, const void **pp_key, unsigned int *size); 554 | 555 | /** 556 | * Rehash the hash table. 557 | * 558 | * Rehashing will change the size of the hash table, retaining all 559 | * elements. This is very costly and should be avoided unless really 560 | * needed. If GHT_AUTOMATIC_REHASH is specified in the flag 561 | * parameter when ght_create() is called, the hash table is 562 | * automatically rehashed when the number of stored elements exceeds 563 | * two times the number of buckets in the table (making calls to this 564 | * function unessessary). 565 | * 566 | * @param p_ht the hash table to rehash. 567 | * @param i_size the new size of the table. 568 | * 569 | * @see ght_create() 570 | */ 571 | void ght_rehash(ght_hash_table_t *p_ht, unsigned int i_size); 572 | 573 | /** 574 | * Free the hash table. ght_finalize() should typically be called 575 | * at the end of the program. Note that only the metadata and the keys 576 | * of the table is freed, not the entries. If you want to free the 577 | * entries when removing the table, the entries will have to be 578 | * manually freed before ght_finalize() is called like: 579 | * 580 | *
581 |  * ght_iterator_t iterator;
582 |  * void *p_key;
583 |  * void *p_e;
584 |  *
585 |  * for(p_e = ght_first(p_table, &iterator, &p_key); p_e; p_e = ght_next(p_table, &iterator, &p_key))
586 |  *   {
587 |  *     free(p_e);
588 |  *   }
589 |  *
590 |  * ght_finalize(p_table);
591 |  * 
592 | * 593 | * @param p_ht the table to remove. 594 | */ 595 | void ght_finalize(ght_hash_table_t *p_ht); 596 | 597 | /* exported hash functions */ 598 | 599 | /** 600 | * One-at-a-time-hash. One-at-a-time-hash is a good hash function, and 601 | * is the default when ght_create() is called with NULL as the 602 | * fn_hash parameter. This was found in a DrDobbs article, see 603 | * http://burtleburtle.net/bob/hash/doobs.html 604 | * 605 | * @warning Don't call this function directly, it is only meant to be 606 | * used as a callback for the hash table. 607 | * 608 | * @see ght_fn_hash_t 609 | * @see ght_rotating_hash(), ght_crc_hash() 610 | */ 611 | ght_uint32_t ght_one_at_a_time_hash(ght_hash_key_t *p_key); 612 | 613 | /** 614 | * Rotating hash. Not so good hash function. This was found in a 615 | * DrDobbs article, see http://burtleburtle.net/bob/hash/doobs.html 616 | * 617 | * @warning Don't call this function directly, it is only meant to be 618 | * used as a callback for the hash table. 619 | * 620 | * @see ght_fn_hash_t 621 | * @see ght_one_at_a_time_hash(), ght_crc_hash() 622 | */ 623 | ght_uint32_t ght_rotating_hash(ght_hash_key_t *p_key); 624 | 625 | /** 626 | * CRC32 hash. CRC32 hash is a good hash function. This came from Dru 627 | * Lemley . 628 | * 629 | * @warning Don't call this function directly, it is only meant to be 630 | * used as a callback for the hash table. 631 | * 632 | * @see ght_fn_hash_t 633 | * @see ght_one_at_a_time_hash(), ght_rotating_hash() 634 | */ 635 | ght_uint32_t ght_crc_hash(ght_hash_key_t *p_key); 636 | 637 | #ifdef USE_PROFILING 638 | /** 639 | * Print some statistics about the table. Only available if the 640 | * library was compiled with USE_PROFILING defined. 641 | */ 642 | void ght_print(ght_hash_table_t *p_ht); 643 | #endif 644 | 645 | #endif//DLDCOMMONHASHTABLE_H 646 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | F90A1C0F1B1DA57E00C5629F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F90A1C0D1B1DA57E00C5629F /* InfoPlist.strings */; }; 11 | F90A1C121B1DA57E00C5629F /* VFSFilter0.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A1C111B1DA57E00C5629F /* VFSFilter0.cpp */; }; 12 | F90A1C1B1B1E2CC200C5629F /* VFSHooks.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A1C191B1E2CC200C5629F /* VFSHooks.cpp */; }; 13 | F90A1C1C1B1E2CC200C5629F /* VFSHooks.h in Headers */ = {isa = PBXBuildFile; fileRef = F90A1C1A1B1E2CC200C5629F /* VFSHooks.h */; }; 14 | F90A1C241B1F52E500C5629F /* VmPmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A1C221B1F52E500C5629F /* VmPmap.cpp */; }; 15 | F90A1C251B1F52E500C5629F /* VmPmap.h in Headers */ = {isa = PBXBuildFile; fileRef = F90A1C231B1F52E500C5629F /* VmPmap.h */; }; 16 | F90A1C281B1F5ADE00C5629F /* Kauth.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A1C261B1F5ADE00C5629F /* Kauth.cpp */; }; 17 | F90A1C291B1F5ADE00C5629F /* Kauth.h in Headers */ = {isa = PBXBuildFile; fileRef = F90A1C271B1F5ADE00C5629F /* Kauth.h */; }; 18 | F90A67F01B616B360011B233 /* WaitingList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A67EE1B616B360011B233 /* WaitingList.cpp */; }; 19 | F90A67F11B616B360011B233 /* WaitingList.h in Headers */ = {isa = PBXBuildFile; fileRef = F90A67EF1B616B360011B233 /* WaitingList.h */; }; 20 | F90A78FD1B2D3C0600303ABF /* RecursionEngine.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90A78FB1B2D3C0600303ABF /* RecursionEngine.cpp */; }; 21 | F90A78FE1B2D3C0600303ABF /* RecursionEngine.h in Headers */ = {isa = PBXBuildFile; fileRef = F90A78FC1B2D3C0600303ABF /* RecursionEngine.h */; }; 22 | F90D539C1B3933BD007EC6B7 /* ApplicationsData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90D539A1B3933BD007EC6B7 /* ApplicationsData.cpp */; }; 23 | F90D539D1B3933BD007EC6B7 /* ApplicationsData.h in Headers */ = {isa = PBXBuildFile; fileRef = F90D539B1B3933BD007EC6B7 /* ApplicationsData.h */; }; 24 | F90DB5571B35C88F00C19B73 /* VNode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90DB5551B35C88F00C19B73 /* VNode.cpp */; }; 25 | F90DB5581B35C88F00C19B73 /* VNode.h in Headers */ = {isa = PBXBuildFile; fileRef = F90DB5561B35C88F00C19B73 /* VNode.h */; }; 26 | F90E389E1B36E3EE00C14797 /* VersionDependent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90E389C1B36E3EE00C14797 /* VersionDependent.cpp */; }; 27 | F90E389F1B36E3EE00C14797 /* VersionDependent.h in Headers */ = {isa = PBXBuildFile; fileRef = F90E389D1B36E3EE00C14797 /* VersionDependent.h */; }; 28 | F90E3BE11B7356D300D72735 /* QvrMacPolicy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F90E3BDF1B7356D300D72735 /* QvrMacPolicy.cpp */; }; 29 | F90E3BE21B7356D300D72735 /* QvrMacPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = F90E3BE01B7356D300D72735 /* QvrMacPolicy.h */; }; 30 | F95DA3741B22F68D004C965C /* VFSFilter0UserClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F95DA3711B22F68D004C965C /* VFSFilter0UserClient.cpp */; }; 31 | F95DA3751B22F68D004C965C /* VFSFilter0UserClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F95DA3721B22F68D004C965C /* VFSFilter0UserClient.h */; }; 32 | F95DA3761B22F68D004C965C /* VFSFilter0UserClientInterface.h in Headers */ = {isa = PBXBuildFile; fileRef = F95DA3731B22F68D004C965C /* VFSFilter0UserClientInterface.h */; }; 33 | F9D20CD61B37DF6A006C9688 /* VNodeHook.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D20CD41B37DF6A006C9688 /* VNodeHook.cpp */; }; 34 | F9D20CD71B37DF6A006C9688 /* VNodeHook.h in Headers */ = {isa = PBXBuildFile; fileRef = F9D20CD51B37DF6A006C9688 /* VNodeHook.h */; }; 35 | F9D20CDA1B37E0EA006C9688 /* CommonHashTable.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F9D20CD81B37E0EA006C9688 /* CommonHashTable.cpp */; }; 36 | F9D20CDB1B37E0EA006C9688 /* CommonHashTable.h in Headers */ = {isa = PBXBuildFile; fileRef = F9D20CD91B37E0EA006C9688 /* CommonHashTable.h */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | F90A1C051B1DA57E00C5629F /* VFSFilter0.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VFSFilter0.kext; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | F90A1C091B1DA57E00C5629F /* Kernel.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Kernel.framework; path = System/Library/Frameworks/Kernel.framework; sourceTree = SDKROOT; }; 42 | F90A1C0C1B1DA57E00C5629F /* VFSFilter0-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "VFSFilter0-Info.plist"; sourceTree = ""; }; 43 | F90A1C0E1B1DA57E00C5629F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 44 | F90A1C101B1DA57E00C5629F /* VFSFilter0.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VFSFilter0.h; sourceTree = ""; }; 45 | F90A1C111B1DA57E00C5629F /* VFSFilter0.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = VFSFilter0.cpp; sourceTree = ""; }; 46 | F90A1C131B1DA57E00C5629F /* VFSFilter0-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "VFSFilter0-Prefix.pch"; sourceTree = ""; }; 47 | F90A1C191B1E2CC200C5629F /* VFSHooks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VFSHooks.cpp; sourceTree = ""; }; 48 | F90A1C1A1B1E2CC200C5629F /* VFSHooks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VFSHooks.h; sourceTree = ""; }; 49 | F90A1C211B1E341200C5629F /* Common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = ""; }; 50 | F90A1C221B1F52E500C5629F /* VmPmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VmPmap.cpp; sourceTree = ""; }; 51 | F90A1C231B1F52E500C5629F /* VmPmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VmPmap.h; sourceTree = ""; }; 52 | F90A1C261B1F5ADE00C5629F /* Kauth.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Kauth.cpp; sourceTree = ""; }; 53 | F90A1C271B1F5ADE00C5629F /* Kauth.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Kauth.h; sourceTree = ""; }; 54 | F90A67EE1B616B360011B233 /* WaitingList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WaitingList.cpp; sourceTree = ""; }; 55 | F90A67EF1B616B360011B233 /* WaitingList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WaitingList.h; sourceTree = ""; }; 56 | F90A78FB1B2D3C0600303ABF /* RecursionEngine.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RecursionEngine.cpp; sourceTree = ""; }; 57 | F90A78FC1B2D3C0600303ABF /* RecursionEngine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecursionEngine.h; sourceTree = ""; }; 58 | F90D539A1B3933BD007EC6B7 /* ApplicationsData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ApplicationsData.cpp; sourceTree = ""; }; 59 | F90D539B1B3933BD007EC6B7 /* ApplicationsData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ApplicationsData.h; sourceTree = ""; }; 60 | F90DB5551B35C88F00C19B73 /* VNode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VNode.cpp; sourceTree = ""; }; 61 | F90DB5561B35C88F00C19B73 /* VNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VNode.h; sourceTree = ""; }; 62 | F90E389C1B36E3EE00C14797 /* VersionDependent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VersionDependent.cpp; sourceTree = ""; }; 63 | F90E389D1B36E3EE00C14797 /* VersionDependent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VersionDependent.h; sourceTree = ""; }; 64 | F90E3BDF1B7356D300D72735 /* QvrMacPolicy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = QvrMacPolicy.cpp; sourceTree = ""; }; 65 | F90E3BE01B7356D300D72735 /* QvrMacPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QvrMacPolicy.h; sourceTree = ""; }; 66 | F95DA3711B22F68D004C965C /* VFSFilter0UserClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VFSFilter0UserClient.cpp; sourceTree = ""; }; 67 | F95DA3721B22F68D004C965C /* VFSFilter0UserClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VFSFilter0UserClient.h; sourceTree = ""; }; 68 | F95DA3731B22F68D004C965C /* VFSFilter0UserClientInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VFSFilter0UserClientInterface.h; sourceTree = ""; }; 69 | F9D20CD41B37DF6A006C9688 /* VNodeHook.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VNodeHook.cpp; sourceTree = ""; }; 70 | F9D20CD51B37DF6A006C9688 /* VNodeHook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VNodeHook.h; sourceTree = ""; }; 71 | F9D20CD81B37E0EA006C9688 /* CommonHashTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommonHashTable.cpp; sourceTree = ""; }; 72 | F9D20CD91B37E0EA006C9688 /* CommonHashTable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonHashTable.h; sourceTree = ""; }; 73 | /* End PBXFileReference section */ 74 | 75 | /* Begin PBXFrameworksBuildPhase section */ 76 | F90A1C001B1DA57E00C5629F /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | /* End PBXFrameworksBuildPhase section */ 84 | 85 | /* Begin PBXGroup section */ 86 | F90A1BFA1B1DA57E00C5629F = { 87 | isa = PBXGroup; 88 | children = ( 89 | F90A1C0A1B1DA57E00C5629F /* VFSFilter0 */, 90 | F90A1C071B1DA57E00C5629F /* Frameworks */, 91 | F90A1C061B1DA57E00C5629F /* Products */, 92 | ); 93 | sourceTree = ""; 94 | }; 95 | F90A1C061B1DA57E00C5629F /* Products */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | F90A1C051B1DA57E00C5629F /* VFSFilter0.kext */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | F90A1C071B1DA57E00C5629F /* Frameworks */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | F90A1C081B1DA57E00C5629F /* Other Frameworks */, 107 | ); 108 | name = Frameworks; 109 | sourceTree = ""; 110 | }; 111 | F90A1C081B1DA57E00C5629F /* Other Frameworks */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | F90A1C091B1DA57E00C5629F /* Kernel.framework */, 115 | ); 116 | name = "Other Frameworks"; 117 | sourceTree = ""; 118 | }; 119 | F90A1C0A1B1DA57E00C5629F /* VFSFilter0 */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | F90E3BDF1B7356D300D72735 /* QvrMacPolicy.cpp */, 123 | F90E3BE01B7356D300D72735 /* QvrMacPolicy.h */, 124 | F90D539A1B3933BD007EC6B7 /* ApplicationsData.cpp */, 125 | F90D539B1B3933BD007EC6B7 /* ApplicationsData.h */, 126 | F9D20CD81B37E0EA006C9688 /* CommonHashTable.cpp */, 127 | F9D20CD91B37E0EA006C9688 /* CommonHashTable.h */, 128 | F90A78FB1B2D3C0600303ABF /* RecursionEngine.cpp */, 129 | F90A78FC1B2D3C0600303ABF /* RecursionEngine.h */, 130 | F90A1C211B1E341200C5629F /* Common.h */, 131 | F90A1C261B1F5ADE00C5629F /* Kauth.cpp */, 132 | F90A1C271B1F5ADE00C5629F /* Kauth.h */, 133 | F90A1C0B1B1DA57E00C5629F /* Supporting Files */, 134 | F90A1C111B1DA57E00C5629F /* VFSFilter0.cpp */, 135 | F90A1C101B1DA57E00C5629F /* VFSFilter0.h */, 136 | F95DA3711B22F68D004C965C /* VFSFilter0UserClient.cpp */, 137 | F95DA3721B22F68D004C965C /* VFSFilter0UserClient.h */, 138 | F95DA3731B22F68D004C965C /* VFSFilter0UserClientInterface.h */, 139 | F90A1C191B1E2CC200C5629F /* VFSHooks.cpp */, 140 | F90A1C1A1B1E2CC200C5629F /* VFSHooks.h */, 141 | F90A1C221B1F52E500C5629F /* VmPmap.cpp */, 142 | F90A1C231B1F52E500C5629F /* VmPmap.h */, 143 | F90DB5551B35C88F00C19B73 /* VNode.cpp */, 144 | F90DB5561B35C88F00C19B73 /* VNode.h */, 145 | F90E389C1B36E3EE00C14797 /* VersionDependent.cpp */, 146 | F90E389D1B36E3EE00C14797 /* VersionDependent.h */, 147 | F9D20CD41B37DF6A006C9688 /* VNodeHook.cpp */, 148 | F9D20CD51B37DF6A006C9688 /* VNodeHook.h */, 149 | F90A67EE1B616B360011B233 /* WaitingList.cpp */, 150 | F90A67EF1B616B360011B233 /* WaitingList.h */, 151 | ); 152 | path = VFSFilter0; 153 | sourceTree = ""; 154 | }; 155 | F90A1C0B1B1DA57E00C5629F /* Supporting Files */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | F90A1C0C1B1DA57E00C5629F /* VFSFilter0-Info.plist */, 159 | F90A1C0D1B1DA57E00C5629F /* InfoPlist.strings */, 160 | F90A1C131B1DA57E00C5629F /* VFSFilter0-Prefix.pch */, 161 | ); 162 | name = "Supporting Files"; 163 | sourceTree = ""; 164 | }; 165 | /* End PBXGroup section */ 166 | 167 | /* Begin PBXHeadersBuildPhase section */ 168 | F90A1C011B1DA57E00C5629F /* Headers */ = { 169 | isa = PBXHeadersBuildPhase; 170 | buildActionMask = 2147483647; 171 | files = ( 172 | F90A1C291B1F5ADE00C5629F /* Kauth.h in Headers */, 173 | F9D20CD71B37DF6A006C9688 /* VNodeHook.h in Headers */, 174 | F90A1C251B1F52E500C5629F /* VmPmap.h in Headers */, 175 | F90A1C1C1B1E2CC200C5629F /* VFSHooks.h in Headers */, 176 | F90A78FE1B2D3C0600303ABF /* RecursionEngine.h in Headers */, 177 | F90E3BE21B7356D300D72735 /* QvrMacPolicy.h in Headers */, 178 | F90D539D1B3933BD007EC6B7 /* ApplicationsData.h in Headers */, 179 | F95DA3751B22F68D004C965C /* VFSFilter0UserClient.h in Headers */, 180 | F9D20CDB1B37E0EA006C9688 /* CommonHashTable.h in Headers */, 181 | F90E389F1B36E3EE00C14797 /* VersionDependent.h in Headers */, 182 | F90DB5581B35C88F00C19B73 /* VNode.h in Headers */, 183 | F90A67F11B616B360011B233 /* WaitingList.h in Headers */, 184 | F95DA3761B22F68D004C965C /* VFSFilter0UserClientInterface.h in Headers */, 185 | ); 186 | runOnlyForDeploymentPostprocessing = 0; 187 | }; 188 | /* End PBXHeadersBuildPhase section */ 189 | 190 | /* Begin PBXNativeTarget section */ 191 | F90A1C041B1DA57E00C5629F /* VFSFilter0 */ = { 192 | isa = PBXNativeTarget; 193 | buildConfigurationList = F90A1C161B1DA57E00C5629F /* Build configuration list for PBXNativeTarget "VFSFilter0" */; 194 | buildPhases = ( 195 | F90A1BFF1B1DA57E00C5629F /* Sources */, 196 | F90A1C001B1DA57E00C5629F /* Frameworks */, 197 | F90A1C011B1DA57E00C5629F /* Headers */, 198 | F90A1C021B1DA57E00C5629F /* Resources */, 199 | F90A1C031B1DA57E00C5629F /* Rez */, 200 | ); 201 | buildRules = ( 202 | ); 203 | dependencies = ( 204 | ); 205 | name = VFSFilter0; 206 | productName = VFSFilter0; 207 | productReference = F90A1C051B1DA57E00C5629F /* VFSFilter0.kext */; 208 | productType = "com.apple.product-type.kernel-extension"; 209 | }; 210 | /* End PBXNativeTarget section */ 211 | 212 | /* Begin PBXProject section */ 213 | F90A1BFB1B1DA57E00C5629F /* Project object */ = { 214 | isa = PBXProject; 215 | attributes = { 216 | LastUpgradeCheck = 0510; 217 | ORGANIZATIONNAME = slava; 218 | TargetAttributes = { 219 | F90A1C041B1DA57E00C5629F = { 220 | DevelopmentTeam = UENQ27R64K; 221 | }; 222 | }; 223 | }; 224 | buildConfigurationList = F90A1BFE1B1DA57E00C5629F /* Build configuration list for PBXProject "VFSFilter0" */; 225 | compatibilityVersion = "Xcode 3.2"; 226 | developmentRegion = English; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | ); 231 | mainGroup = F90A1BFA1B1DA57E00C5629F; 232 | productRefGroup = F90A1C061B1DA57E00C5629F /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | F90A1C041B1DA57E00C5629F /* VFSFilter0 */, 237 | ); 238 | }; 239 | /* End PBXProject section */ 240 | 241 | /* Begin PBXResourcesBuildPhase section */ 242 | F90A1C021B1DA57E00C5629F /* Resources */ = { 243 | isa = PBXResourcesBuildPhase; 244 | buildActionMask = 2147483647; 245 | files = ( 246 | F90A1C0F1B1DA57E00C5629F /* InfoPlist.strings in Resources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | /* End PBXResourcesBuildPhase section */ 251 | 252 | /* Begin PBXRezBuildPhase section */ 253 | F90A1C031B1DA57E00C5629F /* Rez */ = { 254 | isa = PBXRezBuildPhase; 255 | buildActionMask = 2147483647; 256 | files = ( 257 | ); 258 | runOnlyForDeploymentPostprocessing = 0; 259 | }; 260 | /* End PBXRezBuildPhase section */ 261 | 262 | /* Begin PBXSourcesBuildPhase section */ 263 | F90A1BFF1B1DA57E00C5629F /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | F90A78FD1B2D3C0600303ABF /* RecursionEngine.cpp in Sources */, 268 | F90D539C1B3933BD007EC6B7 /* ApplicationsData.cpp in Sources */, 269 | F9D20CDA1B37E0EA006C9688 /* CommonHashTable.cpp in Sources */, 270 | F90A1C241B1F52E500C5629F /* VmPmap.cpp in Sources */, 271 | F90E389E1B36E3EE00C14797 /* VersionDependent.cpp in Sources */, 272 | F90A1C1B1B1E2CC200C5629F /* VFSHooks.cpp in Sources */, 273 | F9D20CD61B37DF6A006C9688 /* VNodeHook.cpp in Sources */, 274 | F90A1C121B1DA57E00C5629F /* VFSFilter0.cpp in Sources */, 275 | F90DB5571B35C88F00C19B73 /* VNode.cpp in Sources */, 276 | F90A1C281B1F5ADE00C5629F /* Kauth.cpp in Sources */, 277 | F90E3BE11B7356D300D72735 /* QvrMacPolicy.cpp in Sources */, 278 | F95DA3741B22F68D004C965C /* VFSFilter0UserClient.cpp in Sources */, 279 | F90A67F01B616B360011B233 /* WaitingList.cpp in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | /* End PBXSourcesBuildPhase section */ 284 | 285 | /* Begin PBXVariantGroup section */ 286 | F90A1C0D1B1DA57E00C5629F /* InfoPlist.strings */ = { 287 | isa = PBXVariantGroup; 288 | children = ( 289 | F90A1C0E1B1DA57E00C5629F /* en */, 290 | ); 291 | name = InfoPlist.strings; 292 | sourceTree = ""; 293 | }; 294 | /* End PBXVariantGroup section */ 295 | 296 | /* Begin XCBuildConfiguration section */ 297 | F90A1C141B1DA57E00C5629F /* Debug */ = { 298 | isa = XCBuildConfiguration; 299 | buildSettings = { 300 | ALWAYS_SEARCH_USER_PATHS = NO; 301 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 302 | CLANG_CXX_LIBRARY = "libc++"; 303 | CLANG_ENABLE_MODULES = YES; 304 | CLANG_ENABLE_OBJC_ARC = YES; 305 | CLANG_WARN_BOOL_CONVERSION = YES; 306 | CLANG_WARN_CONSTANT_CONVERSION = YES; 307 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 308 | CLANG_WARN_EMPTY_BODY = YES; 309 | CLANG_WARN_ENUM_CONVERSION = YES; 310 | CLANG_WARN_INT_CONVERSION = YES; 311 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 312 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 313 | CODE_SIGN_IDENTITY = ""; 314 | COPY_PHASE_STRIP = NO; 315 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 316 | GCC_C_LANGUAGE_STANDARD = gnu99; 317 | GCC_DYNAMIC_NO_PIC = NO; 318 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 319 | GCC_OPTIMIZATION_LEVEL = 0; 320 | GCC_PREPROCESSOR_DEFINITIONS = ( 321 | "MACH_ASSERT=1", 322 | "DEBUG=1", 323 | "$(inherited)", 324 | ); 325 | "GCC_PREPROCESSOR_DEFINITIONS[arch=*]" = ( 326 | "DEBUG=1", 327 | "$(inherited)", 328 | ); 329 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | MACOSX_DEPLOYMENT_TARGET = 10.10; 337 | ONLY_ACTIVE_ARCH = YES; 338 | SDKROOT = macosx; 339 | }; 340 | name = Debug; 341 | }; 342 | F90A1C151B1DA57E00C5629F /* Release */ = { 343 | isa = XCBuildConfiguration; 344 | buildSettings = { 345 | ALWAYS_SEARCH_USER_PATHS = NO; 346 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 347 | CLANG_CXX_LIBRARY = "libc++"; 348 | CLANG_ENABLE_MODULES = YES; 349 | CLANG_ENABLE_OBJC_ARC = YES; 350 | CLANG_WARN_BOOL_CONVERSION = YES; 351 | CLANG_WARN_CONSTANT_CONVERSION = YES; 352 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 353 | CLANG_WARN_EMPTY_BODY = YES; 354 | CLANG_WARN_ENUM_CONVERSION = YES; 355 | CLANG_WARN_INT_CONVERSION = YES; 356 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 357 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 358 | CODE_SIGN_IDENTITY = ""; 359 | COPY_PHASE_STRIP = YES; 360 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 361 | ENABLE_NS_ASSERTIONS = NO; 362 | GCC_C_LANGUAGE_STANDARD = gnu99; 363 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 364 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 365 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 366 | GCC_WARN_UNDECLARED_SELECTOR = YES; 367 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 368 | GCC_WARN_UNUSED_FUNCTION = YES; 369 | GCC_WARN_UNUSED_VARIABLE = YES; 370 | MACOSX_DEPLOYMENT_TARGET = 10.10; 371 | ONLY_ACTIVE_ARCH = YES; 372 | SDKROOT = macosx; 373 | }; 374 | name = Release; 375 | }; 376 | F90A1C171B1DA57E00C5629F /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | CODE_SIGN_IDENTITY = ""; 380 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; 381 | COMBINE_HIDPI_IMAGES = YES; 382 | CURRENT_PROJECT_VERSION = 1.0.0d1; 383 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 384 | GCC_PREFIX_HEADER = "VFSFilter0/VFSFilter0-Prefix.pch"; 385 | INFOPLIST_FILE = "VFSFilter0/VFSFilter0-Info.plist"; 386 | MODULE_NAME = com.VFSFilter0; 387 | MODULE_VERSION = 1.0.0d1; 388 | PRODUCT_NAME = "$(TARGET_NAME)"; 389 | PROVISIONING_PROFILE = ""; 390 | WRAPPER_EXTENSION = kext; 391 | }; 392 | name = Debug; 393 | }; 394 | F90A1C181B1DA57E00C5629F /* Release */ = { 395 | isa = XCBuildConfiguration; 396 | buildSettings = { 397 | CODE_SIGN_IDENTITY = ""; 398 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = ""; 399 | COMBINE_HIDPI_IMAGES = YES; 400 | CURRENT_PROJECT_VERSION = 1.0.0d1; 401 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 402 | GCC_PREFIX_HEADER = "VFSFilter0/VFSFilter0-Prefix.pch"; 403 | INFOPLIST_FILE = "VFSFilter0/VFSFilter0-Info.plist"; 404 | MODULE_NAME = com.VFSFilter0; 405 | MODULE_VERSION = 1.0.0d1; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | PROVISIONING_PROFILE = ""; 408 | WRAPPER_EXTENSION = kext; 409 | }; 410 | name = Release; 411 | }; 412 | /* End XCBuildConfiguration section */ 413 | 414 | /* Begin XCConfigurationList section */ 415 | F90A1BFE1B1DA57E00C5629F /* Build configuration list for PBXProject "VFSFilter0" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | F90A1C141B1DA57E00C5629F /* Debug */, 419 | F90A1C151B1DA57E00C5629F /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | F90A1C161B1DA57E00C5629F /* Build configuration list for PBXNativeTarget "VFSFilter0" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | F90A1C171B1DA57E00C5629F /* Debug */, 428 | F90A1C181B1DA57E00C5629F /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | }; 435 | rootObject = F90A1BFB1B1DA57E00C5629F /* Project object */; 436 | } 437 | -------------------------------------------------------------------------------- /VFSFilter0/VFSFilter0/VFSFilter0UserClient.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // VFSFilter0UserClient.cpp 3 | // VFSFilter0 4 | // 5 | // Created by slava on 6/6/15. 6 | // Copyright (c) 2015 Slava Imameev. All rights reserved. 7 | // 8 | 9 | #include 10 | #include "VFSFilter0UserClient.h" 11 | #include "VNode.h" 12 | #include "WaitingList.h" 13 | 14 | //-------------------------------------------------------------------- 15 | 16 | #undef super 17 | #define super IOUserClient 18 | OSDefineMetaClassAndStructors(VFSFilter0UserClient, IOUserClient) 19 | 20 | //-------------------------------------------------------------------- 21 | 22 | static const IOExternalMethod sMethods[kt_kVnodeWatcherUserClientNMethods] = 23 | { 24 | { 25 | NULL, 26 | (IOMethod)&VFSFilter0UserClient::open, 27 | kIOUCScalarIScalarO, 28 | 0, 29 | 0 30 | }, 31 | { 32 | NULL, 33 | (IOMethod)&VFSFilter0UserClient::close, 34 | kIOUCScalarIScalarO, 35 | 0, 36 | 0 37 | }, 38 | { // kt_kVnodeWatcherUserClientReply 39 | NULL, 40 | (IOMethod)&VFSFilter0UserClient::reply, 41 | kIOUCStructIStructO, 42 | kIOUCVariableStructureSize, 43 | kIOUCVariableStructureSize 44 | }, 45 | }; 46 | 47 | //-------------------------------------------------------------------- 48 | 49 | DataMap gPreOperationDataMap; 50 | 51 | //-------------------------------------------------------------------- 52 | 53 | enum { kt_kMaximumEventsToHold = 512 }; 54 | 55 | //-------------------------------------------------------------------- 56 | 57 | bool 58 | VFSFilter0UserClient::start(IOService *provider) 59 | { 60 | fProvider = OSDynamicCast(com_VFSFilter0, provider); 61 | assert( fProvider ); 62 | if (!fProvider) 63 | return false; 64 | 65 | if (!super::start(provider)) 66 | return false; 67 | 68 | fDataQueue = IODataQueue::withCapacity( (sizeof(VFSFilter0UserClientData)) * kt_kMaximumEventsToHold + 69 | DATA_QUEUE_ENTRY_HEADER_SIZE); 70 | 71 | if (!fDataQueue) 72 | return false; 73 | 74 | fSharedMemory = fDataQueue->getMemoryDescriptor(); 75 | if (!fSharedMemory) { 76 | fDataQueue->release(); 77 | fDataQueue = NULL; 78 | return false; 79 | } 80 | 81 | fProvider->registerUserClient( this ); 82 | 83 | return true; 84 | } 85 | 86 | //-------------------------------------------------------------------- 87 | 88 | void 89 | VFSFilter0UserClient::stop(IOService *provider) 90 | { 91 | 92 | if (fDataQueue) { 93 | UInt8 message = kt_kStopListeningToMessages; 94 | fDataQueue->enqueue(&message, sizeof(message)); 95 | } 96 | 97 | if (fSharedMemory) { 98 | fSharedMemory->release(); 99 | fSharedMemory = NULL; 100 | } 101 | 102 | if (fDataQueue) { 103 | fDataQueue->release(); 104 | fDataQueue = NULL; 105 | } 106 | 107 | super::stop(provider); 108 | } 109 | 110 | //-------------------------------------------------------------------- 111 | 112 | IOReturn 113 | VFSFilter0UserClient::open(void) 114 | { 115 | if (isInactive()) 116 | return kIOReturnNotAttached; 117 | 118 | if (!fProvider->open(this)) 119 | return kIOReturnExclusiveAccess; // only one user client allowed 120 | 121 | return startLogging(); 122 | } 123 | 124 | //-------------------------------------------------------------------- 125 | 126 | IOReturn 127 | VFSFilter0UserClient::clientClose(void) 128 | { 129 | close(); 130 | terminate(0); 131 | 132 | fClient = NULL; 133 | fProvider = NULL; 134 | 135 | return kIOReturnSuccess; 136 | } 137 | 138 | //-------------------------------------------------------------------- 139 | 140 | IOReturn 141 | VFSFilter0UserClient::close(void) 142 | { 143 | if (!fProvider) 144 | return kIOReturnNotAttached; 145 | 146 | // 147 | // release all vnodes to avoid stalling on system shutdown when the system 148 | // waits for vnode iocount drops to zero on unmount 149 | // 150 | VNodeMap::releaseAllVnodeIO(); 151 | VNodeMap::releaseAllShadowReverse(); 152 | 153 | fProvider->unregisterUserClient( this ); 154 | 155 | if (fProvider->isOpen(this)) 156 | fProvider->close(this); 157 | 158 | return kIOReturnSuccess; 159 | } 160 | 161 | 162 | //-------------------------------------------------------------------- 163 | 164 | void 165 | VFSFilter0UserClient::preOperationCallbackAndWaitForReply( 166 | __in QvrPreOperationCallback* inData 167 | ) 168 | { 169 | assert( preemption_enabled() ); 170 | 171 | VFSData data; 172 | void* id = &data; 173 | bool insertedInPreOperationDataMap = false; 174 | 175 | switch( inData->op ){ 176 | 177 | case VFSOpcode_Lookup: 178 | { 179 | char* pathToLookup = inData->Parameters.Lookup.pathToLookup; 180 | char* redirectedFilePath = inData->Parameters.Lookup.redirectedFilePath; 181 | char* shadowFilePath = inData->Parameters.Lookup.shadowFilePath; 182 | bool calledFromCreate = inData->Parameters.Lookup.calledFromCreate; 183 | 184 | // 185 | // prepare a notification for a client 186 | // 187 | 188 | VFSInitData( &data, VFSDataType_PreOperationCallback ); 189 | 190 | data.Data.PreOperationCallback.op = VFSOpcode_Lookup; 191 | data.Data.PreOperationCallback.id = (int64_t)id; 192 | data.Data.PreOperationCallback.Parameters.Lookup.path = pathToLookup; 193 | data.Data.PreOperationCallback.Parameters.Lookup.redirectedPath = redirectedFilePath; 194 | data.Data.PreOperationCallback.Parameters.Lookup.shadowFilePath = shadowFilePath; 195 | data.Data.PreOperationCallback.Parameters.Lookup.calledFromCreate = calledFromCreate; 196 | 197 | break; 198 | } 199 | /* 200 | case VFSOpcode_Rename: 201 | { 202 | // 203 | // prepare a notification for a client 204 | // 205 | 206 | VFSInitData( &data, VFSDataType_PreOperationCallback ); 207 | 208 | data.Data.PreOperationCallback.op = VFSOpcode_Rename; 209 | data.Data.PreOperationCallback.id = (int64_t)id; 210 | data.Data.PreOperationCallback.Parameters.Rename.from = inData->Parameters.Rename.from; 211 | data.Data.PreOperationCallback.Parameters.Rename.to = inData->Parameters.Rename.to; 212 | 213 | break; 214 | } 215 | */ 216 | case VFSOpcode_Exchange: 217 | { 218 | // 219 | // prepare a notification for a client 220 | // 221 | 222 | VFSInitData( &data, VFSDataType_PreOperationCallback ); 223 | 224 | data.Data.PreOperationCallback.op = VFSOpcode_Exchange; 225 | data.Data.PreOperationCallback.id = (int64_t)id; 226 | data.Data.PreOperationCallback.Parameters.Exchange.from = inData->Parameters.Exchange.from; 227 | data.Data.PreOperationCallback.Parameters.Exchange.to = inData->Parameters.Exchange.to; 228 | 229 | break; 230 | } 231 | 232 | case VFSOpcode_Filter: 233 | { 234 | // 235 | // prepare a notification for a client 236 | // 237 | 238 | VFSInitData( &data, VFSDataType_PreOperationCallback ); 239 | 240 | data.Data.PreOperationCallback.op = VFSOpcode_Filter; 241 | data.Data.PreOperationCallback.id = (int64_t)id; 242 | data.Data.PreOperationCallback.Parameters.Filter.op = inData->Parameters.Filter.in.op; 243 | data.Data.PreOperationCallback.Parameters.Filter.path = inData->Parameters.Filter.in.path; 244 | 245 | insertedInPreOperationDataMap = gPreOperationDataMap.addDataByKey( id, inData ); 246 | assert( insertedInPreOperationDataMap ); 247 | if( ! insertedInPreOperationDataMap ) 248 | inData->Parameters.Filter.out.isControlledFile = false; 249 | 250 | break; 251 | } 252 | 253 | default: 254 | assert( !"An unknown inData->op was provided to VFSFilter0UserClient::preOperationCallbackAndWaitForReply" ); 255 | break; 256 | } 257 | 258 | // 259 | // enter in the waiting list 260 | // 261 | if( gFileOpenWaitingList.enter( id ) ){ 262 | 263 | // 264 | // notify a user client 265 | // 266 | this->sendVFSDataToClient( &data ); 267 | 268 | if( data.Status.WasEnqueued ){ 269 | 270 | // 271 | // wait for a client response 272 | // 273 | gFileOpenWaitingList.wait( id ); 274 | 275 | } else { 276 | 277 | // 278 | // just remove the waiting entry by signalling it 279 | // 280 | gFileOpenWaitingList.signal( id ); 281 | } 282 | } // end if( gFileOpenWaitingList.enter 283 | 284 | if( insertedInPreOperationDataMap ){ 285 | 286 | gPreOperationDataMap.removeKey( id ); 287 | assert( NULL == gPreOperationDataMap.getDataByKey( id ) ); 288 | insertedInPreOperationDataMap = false; 289 | } 290 | } 291 | 292 | void 293 | QvrPreOperationCallbackAndWaitForReply( 294 | __in QvrPreOperationCallback* data 295 | ) 296 | 297 | { 298 | VFSFilter0UserClient* client = com_VFSFilter0::getInstance()->getClient(); 299 | if( ! client ){ 300 | 301 | #if MACH_ASSERT 302 | if( VFSOpcode_Filter == data->op ) 303 | data->Parameters.Filter.out.noClient = true; 304 | #endif // DBG 305 | 306 | return; 307 | } 308 | 309 | client->preOperationCallbackAndWaitForReply( data ); 310 | 311 | com_VFSFilter0::getInstance()->putClient(); 312 | } 313 | 314 | //-------------------------------------------------------------------- 315 | 316 | IOReturn 317 | VFSFilter0UserClient::reply( 318 | __in void *vInBuffer, //VFSClientReply 319 | __out void *vOutBuffer, 320 | __in void *vInSize, 321 | __in void *vOutSizeP, 322 | void *, void *) 323 | { 324 | IOReturn RC = kIOReturnSuccess; 325 | VFSClientReply* reply = (VFSClientReply*)vInBuffer; 326 | vm_size_t inSize = (vm_size_t)vInSize; 327 | 328 | // 329 | // there is no output data 330 | // 331 | *(UInt32*)vOutSizeP = 0x0; 332 | 333 | if( inSize < sizeof( *reply ) ) 334 | return kIOReturnBadArgument; 335 | 336 | // 337 | // it is safe to use a value returned by getDataByKey as it will not be deleted untill a corresponding event 338 | // is set to a signal state 339 | // 340 | QvrPreOperationCallback* inData = (QvrPreOperationCallback*)gPreOperationDataMap.getDataByKey( (void*)reply->id ); 341 | if( inData ){ 342 | 343 | // 344 | // we are waiting for some decision made by the daemon 345 | // 346 | switch( inData->op ) 347 | { 348 | case VFSOpcode_Filter: 349 | inData->Parameters.Filter.out.isControlledFile = ( 0x0 != reply->Data.Filter.isControlledFile ); 350 | #if MACH_ASSERT 351 | inData->Parameters.Filter.out.replyWasReceived = true; 352 | #endif // MACH_ASSERT 353 | break; 354 | 355 | default: 356 | break; 357 | } // end switch 358 | } 359 | 360 | // 361 | // invalidate the pointer before signalling a waiting thread that will deallocate the memory 362 | // 363 | inData = NULL; 364 | 365 | // 366 | // wakeup a waiting thread 367 | // 368 | gFileOpenWaitingList.signal( (void*)reply->id ); 369 | 370 | return RC; 371 | } 372 | 373 | //-------------------------------------------------------------------- 374 | 375 | bool 376 | VFSFilter0UserClient::terminate(IOOptionBits options) 377 | { 378 | // 379 | // if somebody does a kextunload while a client is attached 380 | // 381 | if (fProvider && fProvider->isOpen(this)) 382 | fProvider->close(this); 383 | 384 | (void)stopLogging(); 385 | 386 | return super::terminate(options); 387 | } 388 | 389 | //-------------------------------------------------------------------- 390 | 391 | IOReturn 392 | VFSFilter0UserClient::startLogging(void) 393 | { 394 | return kIOReturnSuccess; 395 | } 396 | 397 | //-------------------------------------------------------------------- 398 | 399 | IOReturn 400 | VFSFilter0UserClient::stopLogging(void) 401 | { 402 | return kIOReturnSuccess; 403 | } 404 | 405 | //-------------------------------------------------------------------- 406 | 407 | bool 408 | VFSFilter0UserClient::initWithTask(task_t owningTask, void *securityID, UInt32 type) 409 | { 410 | if (!super::initWithTask(owningTask, securityID , type)) 411 | return false; 412 | 413 | if (!owningTask) 414 | return false; 415 | 416 | queueLock = IOLockAlloc(); 417 | assert( queueLock ); 418 | if( ! queueLock ) 419 | return false; 420 | 421 | fClient = owningTask; 422 | fClientProc = current_proc(); 423 | fProvider = NULL; 424 | fDataQueue = NULL; 425 | fSharedMemory = NULL; 426 | 427 | return true; 428 | } 429 | 430 | //-------------------------------------------------------------------- 431 | 432 | void 433 | VFSFilter0UserClient::free() 434 | { 435 | if( queueLock ) 436 | IOLockFree( queueLock ); 437 | 438 | super::free(); 439 | } 440 | 441 | //-------------------------------------------------------------------- 442 | 443 | IOReturn 444 | VFSFilter0UserClient::registerNotificationPort(mach_port_t port, UInt32 type, UInt32 ref) 445 | { 446 | if ((!fDataQueue) || (port == MACH_PORT_NULL)) 447 | return kIOReturnError; 448 | 449 | fDataQueue->setNotificationPort(port); 450 | 451 | return kIOReturnSuccess; 452 | } 453 | 454 | //-------------------------------------------------------------------- 455 | 456 | IOReturn 457 | VFSFilter0UserClient::clientMemoryForType(UInt32 type, IOOptionBits *options, 458 | IOMemoryDescriptor **memory) 459 | { 460 | *memory = NULL; 461 | *options = 0; 462 | 463 | if (type == kIODefaultMemoryType) { 464 | if (!fSharedMemory) 465 | return kIOReturnNoMemory; 466 | fSharedMemory->retain(); // client will decrement this reference 467 | *memory = fSharedMemory; 468 | return kIOReturnSuccess; 469 | } 470 | 471 | // unknown memory type 472 | return kIOReturnNoMemory; 473 | } 474 | 475 | //-------------------------------------------------------------------- 476 | 477 | IOExternalMethod * 478 | VFSFilter0UserClient::getTargetAndMethodForIndex(IOService **target, UInt32 index) 479 | { 480 | if (index >= (UInt32)kt_kVnodeWatcherUserClientNMethods) 481 | return NULL; 482 | 483 | switch (index) { 484 | case kt_kVnodeWatcherUserClientOpen: 485 | case kt_kVnodeWatcherUserClientClose: 486 | case kt_kVnodeWatcherUserClientReply: 487 | *target = this; 488 | break; 489 | 490 | default: 491 | *target = fProvider; 492 | break; 493 | } 494 | 495 | return (IOExternalMethod *)&sMethods[index]; 496 | } 497 | 498 | //-------------------------------------------------------------------- 499 | 500 | void VFSFilter0UserClient::sendVFSDataToClient( __in VFSData* kernelData ) 501 | { 502 | UInt32 size; 503 | VFSFilter0UserClientData data; 504 | 505 | bzero( &data, sizeof(data)); 506 | 507 | assert( kernelData->Header.Type < VFSDataType_Count ); 508 | 509 | data.Header = kernelData->Header; 510 | 511 | switch( kernelData->Header.Type ){ 512 | case VFSDataType_Audit: 513 | { 514 | bool emptyPath = false; 515 | bool emptyRedirectedPath = false; 516 | 517 | data.Data.Audit.opcode = kernelData->Data.Audit.op; 518 | data.Data.Audit.error = kernelData->Data.Audit.error; 519 | 520 | size = sizeof( data ) - sizeof( data.Data.Audit.redirectedPath ); 521 | 522 | if( kernelData->Data.Audit.path ){ 523 | 524 | size_t name_len = strlen( kernelData->Data.Audit.path ) + sizeof( '\0' ); 525 | 526 | if( name_len <= sizeof( data.Data.Audit.path ) ) 527 | memcpy( data.Data.Audit.path, kernelData->Data.Audit.path, name_len ); 528 | else 529 | emptyPath = true; 530 | 531 | } else if( !kernelData->Data.Audit.path && kernelData->Data.Audit.vn ){ 532 | 533 | int name_len = sizeof(data.Data.Audit.path); 534 | 535 | errno_t error = vn_getpath( (vnode_t)kernelData->Data.Audit.vn, data.Data.Audit.path, &name_len ); 536 | if( error ) 537 | emptyPath = true; 538 | 539 | } else { 540 | 541 | emptyPath = true; 542 | } 543 | 544 | if( emptyPath ){ 545 | 546 | data.Data.Audit.path[0] = '\0'; 547 | } 548 | 549 | if( kernelData->Data.Audit.redirectedPath ){ 550 | 551 | size_t name_len = strlen( kernelData->Data.Audit.redirectedPath ) + sizeof( '\0' ); 552 | 553 | if( name_len <= sizeof( data.Data.Audit.redirectedPath ) ){ 554 | 555 | memcpy( data.Data.Audit.redirectedPath, kernelData->Data.Audit.redirectedPath, name_len ); 556 | size += name_len; 557 | 558 | } else { 559 | emptyRedirectedPath = true; 560 | } 561 | 562 | } else { 563 | 564 | emptyRedirectedPath = true; 565 | } 566 | 567 | if( emptyRedirectedPath ){ 568 | data.Data.Audit.redirectedPath[0] = '\0'; 569 | size += sizeof( '\0' ); 570 | } 571 | break; 572 | } 573 | 574 | case VFSDataType_PreOperationCallback: 575 | { 576 | data.Data.PreOperationCallback.id = kernelData->Data.PreOperationCallback.id; 577 | data.Data.PreOperationCallback.op = kernelData->Data.PreOperationCallback.op; 578 | 579 | switch( kernelData->Data.PreOperationCallback.op ){ 580 | 581 | case VFSOpcode_Lookup: 582 | { 583 | 584 | bool emptyPath = false; 585 | 586 | data.Data.PreOperationCallback.Parameters.Lookup.calledFromCreate = kernelData->Data.PreOperationCallback.Parameters.Lookup.calledFromCreate; 587 | 588 | size = sizeof( data ) - sizeof( data.Data.PreOperationCallback.Parameters.Lookup.path ); 589 | 590 | if( kernelData->Data.PreOperationCallback.Parameters.Lookup.redirectedPath ){ 591 | 592 | size_t name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Lookup.redirectedPath ) + sizeof( '\0' ); 593 | 594 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Lookup.redirectedPath ) ) 595 | memcpy( data.Data.PreOperationCallback.Parameters.Lookup.redirectedPath, kernelData->Data.PreOperationCallback.Parameters.Lookup.redirectedPath, name_len ); 596 | else 597 | emptyPath = true; 598 | 599 | } else { 600 | 601 | emptyPath = true; 602 | } 603 | 604 | if( emptyPath ){ 605 | 606 | data.Data.PreOperationCallback.Parameters.Lookup.redirectedPath[0] = '\0'; 607 | } 608 | 609 | emptyPath= false; 610 | 611 | if( kernelData->Data.PreOperationCallback.Parameters.Lookup.path ){ 612 | 613 | size_t name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Lookup.path ) + sizeof( '\0' ); 614 | 615 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Lookup.path ) ) 616 | memcpy( data.Data.PreOperationCallback.Parameters.Lookup.path, kernelData->Data.PreOperationCallback.Parameters.Lookup.path, name_len ); 617 | else 618 | emptyPath = true; 619 | 620 | } else if( kernelData->Data.PreOperationCallback.Parameters.Lookup.vn ){ 621 | 622 | int name_len = sizeof(data.Data.PreOperationCallback.Parameters.Lookup.path ); 623 | 624 | errno_t error = vn_getpath( (vnode_t)kernelData->Data.PreOperationCallback.Parameters.Lookup.vn, data.Data.PreOperationCallback.Parameters.Lookup.path, &name_len ); 625 | if( error ) 626 | emptyPath = true; 627 | 628 | } else { 629 | 630 | emptyPath = true; 631 | } 632 | 633 | if( emptyPath ){ 634 | 635 | data.Data.PreOperationCallback.Parameters.Lookup.path[0] = '\0'; 636 | } 637 | 638 | emptyPath = false; 639 | 640 | if( kernelData->Data.PreOperationCallback.Parameters.Lookup.shadowFilePath ){ 641 | 642 | size_t name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Lookup.shadowFilePath ) + sizeof( '\0' ); 643 | 644 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Lookup.shadowFilePath ) ) 645 | memcpy( data.Data.PreOperationCallback.Parameters.Lookup.shadowFilePath, kernelData->Data.PreOperationCallback.Parameters.Lookup.shadowFilePath, name_len ); 646 | else 647 | emptyPath = true; 648 | 649 | } else { 650 | 651 | emptyPath = true; 652 | } 653 | 654 | if( emptyPath ){ 655 | 656 | data.Data.PreOperationCallback.Parameters.Lookup.shadowFilePath[0] = '\0'; 657 | } 658 | 659 | 660 | size += strlen( data.Data.PreOperationCallback.Parameters.Lookup.shadowFilePath ) + sizeof( '\0' ); 661 | break; 662 | } 663 | 664 | case VFSOpcode_Exchange: 665 | { 666 | 667 | size = sizeof( data ) - sizeof( data.Data.PreOperationCallback.Parameters.Exchange.to ); 668 | 669 | size_t name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Exchange.from ) + sizeof( '\0' ); 670 | 671 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Exchange.from ) ) 672 | memcpy( data.Data.PreOperationCallback.Parameters.Exchange.from, kernelData->Data.PreOperationCallback.Parameters.Exchange.from, name_len ); 673 | else 674 | data.Data.PreOperationCallback.Parameters.Exchange.from[0] = '\0'; 675 | 676 | name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Exchange.to ) + sizeof( '\0' ); 677 | 678 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Exchange.to ) ) 679 | memcpy( data.Data.PreOperationCallback.Parameters.Exchange.to, kernelData->Data.PreOperationCallback.Parameters.Exchange.to, name_len ); 680 | else 681 | data.Data.PreOperationCallback.Parameters.Exchange.to[0] = '\0'; 682 | 683 | size += strlen( data.Data.PreOperationCallback.Parameters.Exchange.to ) + sizeof( '\0' ); 684 | 685 | break; 686 | } 687 | 688 | case VFSOpcode_Filter: 689 | { 690 | data.Data.PreOperationCallback.Parameters.Filter.op = kernelData->Data.PreOperationCallback.Parameters.Filter.op; 691 | 692 | size = sizeof( data ) - sizeof( data.Data.PreOperationCallback.Parameters.Filter.path ); 693 | 694 | size_t name_len = strlen( kernelData->Data.PreOperationCallback.Parameters.Filter.path ) + sizeof( '\0' ); 695 | 696 | if( name_len <= sizeof( data.Data.PreOperationCallback.Parameters.Filter.path ) ) 697 | memcpy( data.Data.PreOperationCallback.Parameters.Filter.path, kernelData->Data.PreOperationCallback.Parameters.Filter.path, name_len ); 698 | else 699 | data.Data.PreOperationCallback.Parameters.Filter.path[0] = '\0'; 700 | 701 | size += strlen( data.Data.PreOperationCallback.Parameters.Filter.path ) + sizeof( '\0' ); 702 | 703 | break; 704 | } 705 | 706 | default: 707 | { 708 | assert( !"An unknown kernelData->Data.PreOperationCallback.op was provided to VFSFilter0UserClient::sendVFSDataToClient" ); 709 | break; 710 | } 711 | } 712 | 713 | break; 714 | } 715 | 716 | default: 717 | size = 0; 718 | break; 719 | } // end switch 720 | 721 | 722 | if( size ){ 723 | 724 | IOLockLock( this->queueLock ); 725 | {// start of the lock 726 | fDataQueue->enqueue( &data, size ); 727 | }// end of the lock 728 | IOLockUnlock( this->queueLock ); 729 | 730 | kernelData->Status.WasEnqueued = true; 731 | 732 | } // end if( size ) 733 | 734 | } 735 | 736 | //-------------------------------------------------------------------- 737 | --------------------------------------------------------------------------------