on master help sign in
patch up darwin fstype
commit1921dca03fc5c4a336dbc3de1c72ca23309a8e81 authorAgentM <agentm@themactionfaction.com> dateWed 18 Oct 2017, 21:09 (9 years ago) parentd697aa818d3907
6 files changed+131−27
cbits/darwin_statfs.c+2−1 project-m36.cabal−7 src/lib/ProjectM36/FSType.hs+98 src/lib/ProjectM36/FileLock.hs+13−7 src/lib/ProjectM36/Server.hs+5−5 test/TransactionGraph/Persist.hs+13−7
cbits/darwin_statfs.c+2−1
@@ -4,7 +4,7 @@
44
55#ifndef _PROJECT_M36_STATFS_
66#define _PROJECT_M36_STATFS_
7−
7+#ifdef __APPLE__
88int cDarwinFSJournaled(const char* path)
99{
1010 struct statfs s = {0};
@@ -20,3 +20,4 @@ int cDarwinFSJournaled(const char* path)
2020 }
2121}
2222#endif
23+#endif
project-m36.cabal−7
@@ -226,13 +226,6 @@ Executable tutd
226226 Hs-Source-Dirs: ./src/bin
227227 Default-Language: Haskell2010
228228 Default-Extensions: OverloadedStrings
229−-- if os(windows)
230−-- Build-Depends: Win32 >= 2.3 && < 2.4
231−-- Other-Modules: ProjectM36.Win32Handle
232−-- else
233−-- C-sources: ProjectM36/DirectoryFsync.c
234−-- Build-Depends: unix
235−
236229
237230Executable project-m36-server
238231 Build-Depends: base,
src/lib/ProjectM36/FSType.hsnew file+98
@@ -0,0 +1,98 @@
1+{-# LANGUAGE CPP #-}
2+-- confirm that the filesystem type is a journaled FS type expected by Project:M36
3+-- use statfs on Linux and macOS and GetVolumeInformation on Windows
4+-- this could still be fooled with symlinks or by disabling journaling on filesystems that support that
5+module ProjectM36.FSType where
6+
7+#if defined(mingw32_HOST_OS)
8+# if defined(i386_HOST_ARCH)
9+# define WINDOWS_CCONV stdcall
10+# elif defined(x86_64_HOST_ARCH)
11+# define WINDOWS_CCONV ccall
12+# endif
13+import System.Win32.Types
14+import Foreign.ForeignPtr
15+import Data.Word
16+import Data.Bits
17+import Foreign.Storable
18+
19+foreign import WINDOWS_CCONV unsafe "windows.h GetVolumePathNameW"
20+ c_GetVolumePathName :: LPCTSTR -> LPTSTR -> DWORD -> IO BOOL
21+
22+foreign import WINDOWS_CCONV unsafe "windows.h GetVolumeInformationW"
23+ c_GetVolumeInformation :: LPCTSTR -> LPTSTR -> DWORD -> LPDWORD -> LPDWORD -> LPDWORD -> LPTSTR -> DWORD -> IO BOOL
24+
25+#define FILE_SUPPORTS_USN_JOURNAL 0x02000000
26+
27+getVolumePathName :: FilePath -> IO String
28+getVolumePathName path = do
29+ let maxpathlen = 260 --ANSI MAX_PATH- we only care about the drive name anyway
30+ withTString path $ \c_path -> do
31+ fp_pathout <- mallocForeignPtrBytes maxpathlen
32+ withForeignPtr fp_pathout $ \pathout -> do
33+ failIfFalse_ ("GetVolumePathNameW " ++ path) (c_GetVolumePathName c_path pathout (fromIntegral maxpathlen))
34+ peekTString pathout
35+
36+fsTypeSupportsJournaling :: FilePath -> IO Bool
37+fsTypeSupportsJournaling path = do
38+ -- get the drive path of the incoming path
39+ drive <- getVolumePathName path
40+ withTString drive $ \c_drive -> do
41+ foreign_flags <- mallocForeignPtrBytes 8
42+ withForeignPtr foreign_flags $ \ptr_fsFlags -> do
43+ failIfFalse_ (unwords ["GetVolumeInformationW", path]) (c_GetVolumeInformation c_drive nullPtr 0 nullPtr nullPtr ptr_fsFlags nullPtr 0)
44+ fsFlags <- peekByteOff ptr_fsFlags 0 :: IO Word64
45+ pure (fsFlags .&. FILE_SUPPORTS_USN_JOURNAL /= 0)
46+
47+#elif darwin_HOST_OS
48+--Darwin reports journaling directly in the fs flags
49+
50+type CStatFS = ()
51+foreign import ccall unsafe "cDarwinFSJournaled"
52+ c_DarwinFSJournaled :: CString -> IO CInt
53+
54+fsTypeSupportsJournaling :: FilePath -> IO Bool
55+fsTypeSupportsJournaling path =
56+ withCString path $ \c_path -> do
57+ ret <- throwErrnoIfMinus1 "statfs" (c_DarwinFSJournaled c_path)
58+ pure (ret > (0 :: CInt))
59+
60+#elif linux_HOST_OS
61+import Foreign
62+import Foreign.C.Error
63+import Foreign.C.String
64+import Foreign.C.Types
65+
66+#include "MachDeps.h"
67+--Linux cannot report journaling, so we just check the filesystem type as a proxy
68+type CStatFS = ()
69+foreign import ccall unsafe "sys/vfs.h statfs"
70+ c_statfs :: CString -> Ptr CStatFS -> IO CInt
71+
72+#if WORD_SIZE_IN_BITS == 64
73+type CFSType = Word64
74+sizeofStructStatFS :: Int
75+sizeofStructStatFS = 120
76+#else
77+#error 32-bit not supported due to sizeof struct statfs missing
78+type CFSType = Word32
79+sizeofStructStatFS :: Int
80+sizeofStructStatFS = undefined
81+#endif
82+
83+fsTypeSupportsJournaling :: FilePath -> IO Bool
84+fsTypeSupportsJournaling path = do
85+ struct_statfs <- mallocForeignPtrBytes sizeofStructStatFS
86+ withCString path $ \c_path -> do
87+ withForeignPtr struct_statfs $ \ptr_statfs -> do
88+ throwErrnoIfMinus1_ "statfs" (c_statfs c_path ptr_statfs)
89+ cfstype <- peekByteOff ptr_statfs 0 :: IO CFSType
90+ let journaledFS = [0xEF53, --EXT3+4
91+ 0x5346544e, --NTFS
92+ 0x52654973, --REISERFS
93+ 0x58465342, --XFS
94+ 0x3153464a --JFS
95+ ]
96+ pure (elem cfstype journaledFS)
97+#endif
98+
src/lib/ProjectM36/FileLock.hs+13−7
@@ -24,19 +24,25 @@ foreign import WINDOWS_CCONV "LockFileEx" c_lockFileEx :: HANDLE -> DWORD -> DWO
2424
2525foreign import WINDOWS_CCONV "UnlockFileEx" c_unlockFileEx :: HANDLE -> DWORD -> DWORD -> DWORD -> LPOVERLAPPED -> IO BOOL
2626
27−type LockFile = Handle
27+type LockFile = HANDLE
2828
2929openLockFile :: FilePath -> IO LockFile
30−openLockFile path = openFile path ReadMode
30+openLockFile path = createFile path
31+ (gENERIC_READ .|. gENERIC_WRITE)
32+ (fILE_SHARE_READ .|. fILE_SHARE_WRITE)
33+ nullPtr
34+ oPEN_ALWAYS
35+ fILE_ATTRIBUTE_NORMAL
36+ nullPtr
3137
3238closeLockFile :: LockFile -> IO ()
3339closeLockFile file = do
3440 unlockFile file
35− hClose file
41+ closeHandle file
3642
3743--swiped from System.FileLock package
38−lockFile :: Handle -> LockType -> IO ()
39−lockFile handle lock = withHandleToHANDLE handle $ \winHandle -> do
44+lockFile :: HANDLE -> LockType -> IO ()
45+lockFile winHandle lock = do
4046 let exFlag = case lock of
4147 WriteLock -> 2
4248 ReadLock -> 0
@@ -51,8 +57,8 @@ lockFile handle lock = withHandleToHANDLE handle $ \winHandle -> do
5157 else
5258 error "failed to wait for database lock"
5359
54−unlockFile :: Handle -> IO ()
55−unlockFile handle = withHandleToHANDLE handle $ \winHandle -> do
60+unlockFile :: HANDLE -> IO ()
61+unlockFile winHandle = do
5662 let sizeof_OVERLAPPED = 32
5763 allocaBytes sizeof_OVERLAPPED $ \op -> do
5864 zeroMemory op $ fromIntegral sizeof_OVERLAPPED
src/lib/ProjectM36/Server.hs+5−5
@@ -86,11 +86,11 @@ checkFSType performCheck strat =
8686 CrashSafePersistence path ->
8787 if performCheck then do
8888 -- if the path does not (yet) exist, then walk back a step- the db directory may not yet have been created
89− fullpathexists <- doesDirectoryExist path
90− let fscheckpath = if fullpathexists then
91− path
92− else
93− takeDirectory path
89+ fullpathexists <- doesDirectoryExist path
90+ let fscheckpath = if fullpathexists then
91+ path
92+ else
93+ takeDirectory path
9494 fsTypeSupportsJournaling fscheckpath
9595 else
9696 pure True
test/TransactionGraph/Persist.hs+13−7
@@ -77,22 +77,28 @@ testFunctionPersistence :: Test
7777testFunctionPersistence = TestCase $ withSystemTempDirectory "m36testdb" $ \tempdir -> do
7878 let dbdir = tempdir </> "dbdir"
7979 connInfo = InProcessConnectionInfo (MinimalPersistence dbdir) emptyNotificationCallback []
80− Right conn <- connectProjectM36 connInfo
81− Right sess <- createSessionAtHead conn "master"
80+ conn <- assertIOEither $ connectProjectM36 connInfo
81+ sess <- assertIOEither $ createSessionAtHead conn "master"
8282 let intTCons = PrimitiveTypeConstructor "Int" IntAtomType
8383 addfunc = AddAtomFunction "testdisk" [
8484 intTCons,
8585 ADTypeConstructor "Either" [ADTypeConstructor "AtomFunctionError" [],
8686 intTCons]] "(\\(x:_) -> pure x) :: [Atom] -> Either AtomFunctionError Atom"
87− Right () <- executeDatabaseContextIOExpr sess conn addfunc
88− Right () <- commit sess conn
87+ _ <- assertIOEither $ executeDatabaseContextIOExpr sess conn addfunc
88+
89+ _ <- assertIOEither $ commit sess conn
8990 close conn
9091 --re-open the connection to reload the graph
91− Right conn2 <- connectProjectM36 connInfo
92− Right sess2 <- createSessionAtHead conn2 "master"
92+ conn2 <- assertIOEither $ connectProjectM36 connInfo
93+ sess2 <- assertIOEither $ createSessionAtHead conn2 "master"
9394
9495 res <- executeRelationalExpr sess2 conn2 (MakeRelationFromExprs Nothing [TupleExpr (M.singleton "a" (FunctionAtomExpr "testdisk" [NakedAtomExpr (IntAtom 3)] ()))])
9596 let expectedRel = mkRelationFromList (attributesFromList [Attribute "a" IntAtomType]) [[IntAtom 3]]
9697 assertEqual "testdisk dbc function run" expectedRel res
9798
98−
99+assertIOEither :: (Show a) => IO (Either a b) -> IO b
100+assertIOEither x = do
101+ ret <- x
102+ case ret of
103+ Left err -> assertFailure (show err) >> undefined
104+ Right val -> pure val