-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFilesystem.hs
More file actions
202 lines (165 loc) · 6.65 KB
/
Filesystem.hs
File metadata and controls
202 lines (165 loc) · 6.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
--- |
--- | This module contains a simple abstraction of the fuse library
--- | This should be reusable by other projects
--- |
--- Copyright : (c) Florian Richter 2011
--- License : GPL
---
module Filesystem (
FsObject
, FsContent(..)
, FileInfoHandler
, ReadFunc
, WriteFunc
, CloseFunc
, UserGroupID
, getUserGroupID
, getStatDir
, getStatFileR
, getStatFileRW
, getStatFileRX
, startupFileSystem
) where
import System.Fuse
import System.Posix.Types
import System.Posix.Files
import System.Posix.User
import System.Environment(withArgs)
import System.Log.Logger
import System.Log.Handler.Simple
import qualified Data.ByteString.Char8 as B
import Control.Monad
type FsObject = (FileStat, FsContent)
-- | filesystem entry
data FsContent = FsDir (IO [FilePath])
| FsFile (OpenMode -> IO (Either Errno (ReadFunc, Maybe WriteFunc, CloseFunc))) FuseOpenInfo
-- | function, that provides the content of the filesystem
type FileInfoHandler = FilePath -> IO (Maybe FsObject)
-- | type signatures of read function
type ReadFunc = Integer -> Integer -> IO B.ByteString
-- | type signatures of write function
type WriteFunc = B.ByteString -> Integer -> IO ByteCount
-- | type signatures of close function
type CloseFunc = IO ()
type FileHandle = (ReadFunc, Maybe WriteFunc, CloseFunc)
type UserGroupID = (UserID, GroupID)
-- | get user and group id of current user
getUserGroupID :: IO UserGroupID
getUserGroupID = do
uid <- getRealUserID
gid <- getRealGroupID
return (uid, gid)
-- helpers for usefull stats
getStatDir ugid = getStat ugid Directory "rx" 0
getStatFileR ugid size = getStat ugid RegularFile "r" size
getStatFileRW ugid size = getStat ugid RegularFile "rw" size
getStatFileRX ugid size = getStat ugid RegularFile "rx" size
-- | helper for generic file/directory stat
getStat :: Integral n => UserGroupID -> EntryType -> String -> n -> FileStat
getStat (uid, gid) entryType fileModeStr size = FileStat
{ statEntryType = entryType
, statFileMode = strToFileMode fileModeStr
, statLinkCount = 1
, statFileOwner = uid
, statFileGroup = gid
, statSpecialDeviceID = 0
, statFileSize = fromInteger (toInteger size)
, statBlocks = 1
, statAccessTime= 0
, statModificationTime = 0
, statStatusChangeTime = 0
}
where
strToFileMode str = foldr1 unionFileModes (map chrToFileMode str)
chrToFileMode 'r' = foldr1 unionFileModes [ ownerReadMode , groupReadMode , otherReadMode ]
chrToFileMode 'w' = foldr1 unionFileModes [ ownerWriteMode , groupWriteMode , otherWriteMode ]
chrToFileMode 'x' = foldr1 unionFileModes [ ownerExecuteMode , groupExecuteMode , otherExecuteMode ]
chrToFileMode _ = nullFileMode
-- | fuse open handler
fsOpen :: FileInfoHandler -> FilePath -> OpenMode -> OpenFileFlags -> IO (Either Errno (FuseOpenInfo, FileHandle))
fsOpen infoHandler path mode flags = do
object <- infoHandler path
case object of
Just (_, FsFile openFunc openInfo) -> do
result <- openFunc mode
case result of
Left errno ->
return $ Left errno
Right (readFunc, writeFunc, closeFunc) ->
return $ Right (openInfo ,(readFunc, writeFunc, closeFunc))
Just (_, FsDir _) -> return $ Left eNOENT
Nothing -> return $ Left eNOENT
-- | fuse read handler
fsRead :: FilePath -> FileHandle -> ByteCount -> FileOffset -> IO (Either Errno B.ByteString)
fsRead path (readFunc, _, _) size offset = Right `liftM` readFunc (fromIntegral size) (fromIntegral offset)
-- | fuse write handler
fsWrite :: FilePath -> FileHandle -> B.ByteString -> FileOffset -> IO (Either Errno ByteCount)
fsWrite path (_, writeFunc, _) stuff offset = case writeFunc of
Just writeF -> Right `liftM` writeF stuff (fromIntegral offset)
Nothing -> return $ Left eACCES
-- | fuse release (close systemcall) handler
fsRelease :: FilePath -> FileHandle -> IO ()
fsRelease path (_, _, closeFunc) = closeFunc
-- | fuse opendir handler
fsOpenDir :: FileInfoHandler -> FilePath -> IO Errno
fsOpenDir infoHandler path = do
result <- fsReadDir infoHandler path
case result of
Right _ -> return eOK
Left errno -> return errno
-- | fuse readdir handler
fsReadDir :: FileInfoHandler -> FilePath -> IO (Either Errno [(FilePath, FileStat)])
fsReadDir infoHandler path = do
ugid <- getUserGroupID
info <- infoHandler path
case info of
Just (stat, FsDir list) -> do
dirlist <- list
stats <- mapM (getStats path) dirlist
return $ Right ([(".", getStatDir ugid), ("..", getStatDir ugid)] ++ (zip dirlist stats))
Just (stat, FsFile _ _) ->
return $ Left eNOTDIR
Nothing ->
return $ Left eNOENT
where
getStats path name = do
let completepath = if (last path) == '/' then path ++ name else path ++ "/" ++ name
Just (stat, _) <- infoHandler completepath
return stat
-- | fuse releasedir handler
fsReleaseDir :: FileInfoHandler -> FilePath -> IO Errno
fsReleaseDir infoHandler path = do
return eOK
-- | fuse getstat handler
fsGetStat :: FileInfoHandler -> FilePath -> IO (Either Errno FileStat)
fsGetStat infoHandler path = do
info <- infoHandler path
case info of
Just (stat, _) -> return $ Right stat
Nothing -> return $ Left eNOENT
-- | fuse getstat handler
fsAccess :: FileInfoHandler -> FilePath -> Int -> IO Errno
fsAccess infoHandler path amode = do
info <- infoHandler path
case info of
Just (stat, _) -> return eOK
Nothing -> return eNOENT
fuseOps startHandler stopHandler infoHandler = defaultFuseOps {
fuseInit = startHandler,
fuseDestroy = stopHandler,
fuseOpen = fsOpen infoHandler,
fuseRead = fsRead,
fuseWrite = fsWrite,
fuseRelease = fsRelease,
fuseOpenDirectory = fsOpenDir infoHandler,
fuseReadDirectory = fsReadDir infoHandler,
fuseReleaseDirectory = fsReleaseDir infoHandler,
fuseGetFileStat = fsGetStat infoHandler,
fuseAccess = fsAccess infoHandler
}
-- | start fuse manager, puts program in background
startupFileSystem :: FilePath -> IO () -> IO () -> FileInfoHandler -> IO ()
startupFileSystem mountpoint startHandler stopHandler infoHandler = do
--withArgs [mountpoint] $ fuseMain (fuseOps startHandler stopHandler infoHandler) defaultExceptionHandler
withArgs [mountpoint, "-f", "-d"] $ fuseMain (fuseOps startHandler stopHandler infoHandler) defaultExceptionHandler
-- vim: sw=4 expandtab