packages feed

funion (empty) → 0.0.1

raw patch · 4 files changed

+256/−0 lines, 4 filesdep +HFusedep +basedep +bytestringsetup-changed

Dependencies added: HFuse, base, bytestring, directory, filepath, haskell98, unix

Files

+ Funion.hs view
@@ -0,0 +1,211 @@+module Main where++import qualified Data.ByteString.Char8 as B+import System.Posix.Types+import System.Posix.Files+import System.FilePath.Posix+import System.Posix.IO+import System.Directory+import System.Fuse+import System.IO+import System(getArgs)+import System.Environment(withArgs)+import Control.Monad+import Data.Maybe+import Data.List (nubBy)+import Data.ByteString.Char8 (pack)++{-+OPEN QUESTIONS:+For now, I suppose that I could just specify the directories that I want to union on the command line.+What about making this more aggressive and having some way of specifying unioning rules?  Perhaps+via a DSL?++How should I present SymLinks?  +-}++{- TODO(nathan)+* CHANGE ARGUMENTS TO PROGRAM +  Change it so that all arguments are assumed to be 'mine' unless somehow+  otherwise noted.  Possibly with "--" ++* There's currently no real error checking whatsoever.+* Add funionWrite+-}++data FunionFS = FunionFS {+    funionEntryName     :: FilePath+  , funionActualPath    :: FilePath+  , funionVirtualPath   :: FilePath+  , funionFileStat      :: FileStat+  , funionContents      :: [FunionFS]+  }+ deriving Show+++dirContents :: FilePath -> IO [FilePath]+dirContents = fmap (filter (`notElem` [".",".."])) . getDirectoryContents+++fileExists, dirExists :: FilePath -> FilePath -> IO Bool+fileExists path name = doesFileExist $ path </> name+dirExists  path name = doesDirectoryExist $ path </> name+++getFileStats, getDirStats :: FilePath-> FilePath -> IO FunionFS+getFileStats path name = getStats RegularFile (path </> name)+getDirStats path name = getStats Directory (path </> name)+++getStats :: EntryType -> FilePath -> IO FunionFS+getStats entrytype uri = do+  status <- getFileStatus uri+  return FunionFS {+      funionEntryName   = takeFileName uri+    , funionActualPath  = uri+    , funionVirtualPath = ""+    , funionFileStat    = FileStat { statEntryType = entrytype+        , statFileMode  = fileMode status+        , statLinkCount = linkCount status+        , statFileOwner = fileOwner status+        , statFileGroup = fileGroup status+        , statSpecialDeviceID = specialDeviceID status+        , statFileSize  = fileSize status+        , statBlocks    = 1            -- This is WRONG.  Change+        , statAccessTime= accessTime status+        , statModificationTime = modificationTime status+        , statStatusChangeTime = statusChangeTime status+      }+     , funionContents = []+  }+++readDir :: FilePath -> IO (FunionFS)+readDir uri = do+  contents <- dirContents uri+  files <- filterM (fileExists uri) contents+  fileList <- mapM (getFileStats uri) files+  -- list of directories+  dirs <- filterM (dirExists uri) contents+  dirList <- mapM (getDirStats uri) dirs++  return FunionFS {+      funionEntryName   = takeFileName uri+    , funionActualPath  = ""+    , funionVirtualPath = uri+    , funionFileStat    = dirStat+    , funionContents    = fileList ++ dirList+  }++funionLookUp :: [FilePath] -> FilePath -> IO (Maybe FunionFS)+funionLookUp dirsToUnion path = do+  dirs  <- filterM (`dirExists` path) dirsToUnion+  dirList <- mapM (readDir.(</> path)) dirs+  files <- filterM (`fileExists` path) dirsToUnion+  fileStats <- mapM (`getFileStats` path) files+  let contents = map funionContents dirList+  case dirs of+    []        -> if length fileStats > 0 then return $ Just $ head fileStats else return Nothing+    otherwise -> return $ if length fileStats > 0 then Just $ head fileStats else Just FunionFS {+            funionEntryName   = takeFileName path+          , funionActualPath  = ""+          , funionVirtualPath = path+          , funionFileStat    = dirStat+          , funionContents    = nubBy (\x y -> (funionEntryName x) == (funionEntryName y))  $ concat contents+        }+++funionFSOps :: [FilePath] -> FuseOperations Fd+funionFSOps dir =+  defaultFuseOps{ fuseGetFileStat        = funionGetFileStat dir+                , fuseOpen               = funionOpen dir+                , fuseFlush              = funionFlush dir+                , fuseRead               = funionRead dir+                , fuseOpenDirectory      = funionOpenDirectory dir+                , fuseReadDirectory      = funionReadDirectory dir+                , fuseGetFileSystemStats = funionGetFileSystemStats dir+                }+++funionGetFileStat :: [FilePath] -> FilePath -> IO (Either Errno FileStat)+funionGetFileStat dirsToUnion (_:dir) = do+    (Just file) <- funionLookUp dirsToUnion dir+    return $ Right $ funionFileStat file+++funionOpen :: [FilePath] -> FilePath -> OpenMode -> OpenFileFlags -> IO (Either Errno Fd)+funionOpen dirsToUnion (_:path) mode flags = do+  file <- funionLookUp dirsToUnion path+  case file of+    Just f -> do+            fd <- openFd (funionActualPath f) ReadOnly Nothing defaultFileFlags+            return (Right fd)+    Nothing -> return (Left eNOENT)++-- What if 'fd' is no good?  What will happen?+funionFlush :: [FilePath] -> FilePath -> Fd -> IO Errno+funionFlush _ _ fd = do closeFd fd; return eOK+++funionOpenDirectory :: [FilePath] -> FilePath -> IO Errno+funionOpenDirectory dirsToUnion (_:path) = do+  extantDirs <- filterM (`dirExists` path) dirsToUnion+  return $ if length extantDirs > 0 then eOK else eNOENT+++funionGetFileSystemStats :: [FilePath]->String -> IO (Either Errno FileSystemStats)+funionGetFileSystemStats fileTree  str =+  return $ Right FileSystemStats+    { fsStatBlockSize  = 512+    , fsStatBlockCount = 1+    , fsStatBlocksFree = 1+    , fsStatBlocksAvailable = 1+    , fsStatFileCount  = 5      -- IS THIS CORRECT?+    , fsStatFilesFree  = 10     -- WHAT IS THIS?+    , fsStatMaxNameLength = 255 -- SEEMS SMALL?+    }+++funionReadDirectory :: [FilePath] ->FilePath -> IO (Either Errno [(FilePath, FileStat)])+funionReadDirectory dirsToUnion (_:dir) = do+  entry <- funionLookUp dirsToUnion dir+  let contents = funionContents $ fromJust entry+  let dirContents = map (\x -> (funionEntryName x :: String , funionFileStat x)) contents+  return $ Right $ [ (".", dirStat), ("..", dirStat)] ++ dirContents+++funionRead  :: [FilePath] -> FilePath -> Fd -> ByteCount -> FileOffset -> IO (Either Errno B.ByteString)+funionRead dirsToUnion (_:path) fd byteCount offset = do+  (Just file) <- funionLookUp dirsToUnion path+  fdSeek fd AbsoluteSeek offset+  (bytes, num) <- fdRead fd  byteCount+  return $ Right $ pack bytes+++dirStat = FileStat { statEntryType = Directory+  , statFileMode = foldr1 unionFileModes+                     [ ownerReadMode+                     , ownerExecuteMode+                     , groupReadMode+                     , groupExecuteMode+                     , otherReadMode+                     , otherExecuteMode+                     ]+  , statLinkCount = 5+  , statFileOwner = 1000+  , statFileGroup = 1000+  , statSpecialDeviceID = 0+  , statFileSize  = 4096+  , statBlocks    = 1+  , statAccessTime= 0+  , statModificationTime = 0+  , statStatusChangeTime = 0+  }+++main :: IO ()+main = do+  args <- getArgs+  let args' = map tail $ filter (\(x:xs) ->  x == '+') args+  let args2 = filter(\(x:xs) -> x /= '+') args+  withArgs args2 $ fuseMain (funionFSOps args')  defaultExceptionHandler
+ LICENSE view
@@ -0,0 +1,25 @@+Copyright (c) 2010, Nathan M. Wiegand+All rights reserved.++Redistribution and use in source and binary forms, with or without modification,+are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright notice,+    * this list of conditions and the following disclaimer.+    * Redistributions in binary form must reproduce the above copyright notice,+    * this list of conditions and the following disclaimer in the documentation+    * and/or other materials provided with the distribution.+    * Neither the name of the author nor the names of its contributors+    * may be used to endorse or promote products derived from this software+    * without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
+ funion.cabal view
@@ -0,0 +1,17 @@+name:                funion+version:             0.0.1+synopsis:            A unioning file-system using HFuse +description:         A unioning file-system using HFuse+category:            System+license:             BSD3+license-file:        LICENSE+author:              Nathan Wiegand+maintainer:          nathanwiegand@gmail.com+homepage:            http://github.com/nathanwiegand/funion+build-depends:       base+build-type:          Simple++executable:          funion+main-is:             Funion.hs+ghc-options:         -threaded+build-depends:       base >= 3 && <= 4, HFuse >= 0.2.1, bytestring >=0.9, haskell98, directory >= 1, unix >= 2.3, filepath >= 1.1