LIB: improve error handling
* close all directory streams in case of IOErrors * raise error on invalid input types in File operations * properly catch eXDEV in moveFile instead of all errors
This commit is contained in:
parent
27673b0751
commit
5afc25d2d1
@ -43,7 +43,8 @@ import Control.Exception
|
|||||||
)
|
)
|
||||||
import Control.Exception.Base
|
import Control.Exception.Base
|
||||||
(
|
(
|
||||||
IOException
|
onException
|
||||||
|
, IOException
|
||||||
)
|
)
|
||||||
import Control.Monad.State.Lazy
|
import Control.Monad.State.Lazy
|
||||||
(
|
(
|
||||||
@ -240,7 +241,6 @@ convertViewP f af@(bp :/ constr) =
|
|||||||
in (b, bp :/ file)
|
in (b, bp :/ file)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
afileLike :: AnchoredFile FileInfo -> (Bool, AnchoredFile FileInfo)
|
afileLike :: AnchoredFile FileInfo -> (Bool, AnchoredFile FileInfo)
|
||||||
afileLike f@(bp :/ constr) = convertViewP fileLike f
|
afileLike f@(bp :/ constr) = convertViewP fileLike f
|
||||||
|
|
||||||
@ -289,12 +289,12 @@ fileLikeSym f@(SymLink { sdest = (_ :/ s@(SymLink {}) )})
|
|||||||
= case (fileLikeSym s) of
|
= case (fileLikeSym s) of
|
||||||
(True, _) -> (True, f)
|
(True, _) -> (True, f)
|
||||||
_ -> (False, f)
|
_ -> (False, f)
|
||||||
fileLikeSym f@(SymLink { sdest = (_ :/ RegFile {} )}) = (True, f)
|
fileLikeSym f@(SymLink { sdest = (_ :/ RegFile {} )}) = (True, f)
|
||||||
fileLikeSym f@(SymLink { sdest = (_ :/ BlockDev {} )}) = (True, f)
|
fileLikeSym f@(SymLink { sdest = (_ :/ BlockDev {} )}) = (True, f)
|
||||||
fileLikeSym f@(SymLink { sdest = (_ :/ CharDev {} )}) = (True, f)
|
fileLikeSym f@(SymLink { sdest = (_ :/ CharDev {} )}) = (True, f)
|
||||||
fileLikeSym f@(SymLink { sdest = (_ :/ NamedPipe {} )}) = (True, f)
|
fileLikeSym f@(SymLink { sdest = (_ :/ NamedPipe {} )}) = (True, f)
|
||||||
fileLikeSym f@(SymLink { sdest = (_ :/ Socket {} )}) = (True, f)
|
fileLikeSym f@(SymLink { sdest = (_ :/ Socket {} )}) = (True, f)
|
||||||
fileLikeSym f = (False, f)
|
fileLikeSym f = (False, f)
|
||||||
|
|
||||||
|
|
||||||
adirSym :: AnchoredFile FileInfo -> (Bool, AnchoredFile FileInfo)
|
adirSym :: AnchoredFile FileInfo -> (Bool, AnchoredFile FileInfo)
|
||||||
@ -634,36 +634,34 @@ getContents (ADirOrSym af) = readDirectory (fullPath af)
|
|||||||
getContents _ = return []
|
getContents _ = return []
|
||||||
|
|
||||||
|
|
||||||
-- |Get all files of a given directory and return them as a List.
|
getDirsFiles' :: (FilePath -> [FilePath] -> [FilePath])
|
||||||
-- This includes "." and "..".
|
-> FilePath
|
||||||
getAllDirsFiles :: FilePath -> IO [FilePath]
|
-> IO [FilePath]
|
||||||
getAllDirsFiles fp = do
|
getDirsFiles' filterf fp = do
|
||||||
dirstream <- PFD.openDirStream fp
|
dirstream <- PFD.openDirStream fp
|
||||||
let mdirs :: [FilePath] -> IO [FilePath]
|
let mdirs :: [FilePath] -> IO [FilePath]
|
||||||
mdirs dirs = do
|
mdirs dirs = do
|
||||||
dir <- PFD.readDirStream dirstream
|
-- make sure we close the directory stream in case of errors
|
||||||
|
dir <- onException (PFD.readDirStream dirstream)
|
||||||
|
(PFD.closeDirStream dirstream)
|
||||||
if dir == ""
|
if dir == ""
|
||||||
then return dirs
|
then return dirs
|
||||||
else mdirs (dir : dirs)
|
else mdirs (dir `filterf` dirs)
|
||||||
dirs <- mdirs []
|
dirs <- mdirs []
|
||||||
PFD.closeDirStream dirstream
|
PFD.closeDirStream dirstream
|
||||||
return dirs
|
return dirs
|
||||||
|
|
||||||
|
|
||||||
|
-- |Get all files of a given directory and return them as a List.
|
||||||
|
-- This includes "." and "..".
|
||||||
|
getAllDirsFiles :: FilePath -> IO [FilePath]
|
||||||
|
getAllDirsFiles = getDirsFiles' (:)
|
||||||
|
|
||||||
|
|
||||||
-- |Get all files of a given directory and return them as a List.
|
-- |Get all files of a given directory and return them as a List.
|
||||||
-- This excludes "." and "..".
|
-- This excludes "." and "..".
|
||||||
getDirsFiles :: FilePath -> IO [FilePath]
|
getDirsFiles :: FilePath -> IO [FilePath]
|
||||||
getDirsFiles fp = do
|
getDirsFiles = getDirsFiles' insert
|
||||||
dirstream <- PFD.openDirStream fp
|
|
||||||
let mdirs :: [FilePath] -> IO [FilePath]
|
|
||||||
mdirs dirs = do
|
|
||||||
dir <- PFD.readDirStream dirstream
|
|
||||||
if dir == ""
|
|
||||||
then return dirs
|
|
||||||
else mdirs (insert dir dirs)
|
|
||||||
dirs <- mdirs []
|
|
||||||
PFD.closeDirStream dirstream
|
|
||||||
return dirs
|
|
||||||
where
|
where
|
||||||
insert dir dirs = case dir of
|
insert dir dirs = case dir of
|
||||||
"." -> dirs
|
"." -> dirs
|
||||||
|
@ -39,6 +39,11 @@ import Data.List
|
|||||||
isPrefixOf
|
isPrefixOf
|
||||||
)
|
)
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
|
import Foreign.C.Error
|
||||||
|
(
|
||||||
|
getErrno
|
||||||
|
, Errno
|
||||||
|
)
|
||||||
import IO.Utils
|
import IO.Utils
|
||||||
import System.Directory
|
import System.Directory
|
||||||
(
|
(
|
||||||
@ -51,6 +56,10 @@ import System.FilePath
|
|||||||
, isAbsolute
|
, isAbsolute
|
||||||
, takeFileName
|
, takeFileName
|
||||||
)
|
)
|
||||||
|
import System.IO.Error
|
||||||
|
(
|
||||||
|
catchIOError
|
||||||
|
)
|
||||||
|
|
||||||
import qualified System.Posix.Files as PF
|
import qualified System.Posix.Files as PF
|
||||||
|
|
||||||
@ -66,6 +75,7 @@ data FmIOException = FileDoesNotExist String
|
|||||||
| FileDoesExist String
|
| FileDoesExist String
|
||||||
| DirDoesExist String
|
| DirDoesExist String
|
||||||
| IsSymlink String
|
| IsSymlink String
|
||||||
|
| InvalidOperation String
|
||||||
deriving (Show, Typeable)
|
deriving (Show, Typeable)
|
||||||
|
|
||||||
|
|
||||||
@ -125,3 +135,19 @@ throwIsSymlink :: FilePath -> IO ()
|
|||||||
throwIsSymlink fp =
|
throwIsSymlink fp =
|
||||||
whenM (PF.isSymbolicLink <$> PF.getSymbolicLinkStatus fp)
|
whenM (PF.isSymbolicLink <$> PF.getSymbolicLinkStatus fp)
|
||||||
(throw $ IsSymlink fp)
|
(throw $ IsSymlink fp)
|
||||||
|
|
||||||
|
|
||||||
|
-- |Carries out an action, then checks if there is an IOException and
|
||||||
|
-- a specific errno. If so, then it carries out another action, otherwise
|
||||||
|
-- it rethrows the error.
|
||||||
|
catchErrno :: Errno -- ^ errno to catch
|
||||||
|
-> IO a -- ^ action to try, which can raise an IOException
|
||||||
|
-> IO a -- ^ action to carry out in case of an IOException and
|
||||||
|
-- if errno matches
|
||||||
|
-> IO a
|
||||||
|
catchErrno en a1 a2 =
|
||||||
|
catchIOError a1 $ \e -> do
|
||||||
|
errno <- getErrno
|
||||||
|
if errno == en
|
||||||
|
then a2
|
||||||
|
else ioError e
|
||||||
|
@ -35,35 +35,27 @@ import Control.Applicative
|
|||||||
)
|
)
|
||||||
import Control.Exception
|
import Control.Exception
|
||||||
(
|
(
|
||||||
handle
|
throw
|
||||||
, throw
|
|
||||||
, SomeException(..)
|
|
||||||
)
|
|
||||||
import Control.Monad
|
|
||||||
(
|
|
||||||
unless
|
|
||||||
, void
|
|
||||||
)
|
)
|
||||||
import Data.DirTree
|
import Data.DirTree
|
||||||
import Data.Foldable
|
import Data.Foldable
|
||||||
(
|
(
|
||||||
for_
|
for_
|
||||||
)
|
)
|
||||||
|
import Foreign.C.Error
|
||||||
|
(
|
||||||
|
eXDEV
|
||||||
|
)
|
||||||
import IO.Error
|
import IO.Error
|
||||||
import IO.Utils
|
import IO.Utils
|
||||||
import System.Directory
|
import System.Directory
|
||||||
(
|
(
|
||||||
doesDirectoryExist
|
doesDirectoryExist
|
||||||
, doesFileExist
|
|
||||||
, removeDirectoryRecursive
|
, removeDirectoryRecursive
|
||||||
)
|
)
|
||||||
import System.FilePath
|
import System.FilePath
|
||||||
(
|
(
|
||||||
equalFilePath
|
(</>)
|
||||||
, isAbsolute
|
|
||||||
, takeFileName
|
|
||||||
, takeDirectory
|
|
||||||
, (</>)
|
|
||||||
)
|
)
|
||||||
import System.Posix.Directory
|
import System.Posix.Directory
|
||||||
(
|
(
|
||||||
@ -74,8 +66,6 @@ import System.Posix.Files
|
|||||||
(
|
(
|
||||||
createSymbolicLink
|
createSymbolicLink
|
||||||
, readSymbolicLink
|
, readSymbolicLink
|
||||||
, fileAccess
|
|
||||||
, getFileStatus
|
|
||||||
, groupExecuteMode
|
, groupExecuteMode
|
||||||
, groupReadMode
|
, groupReadMode
|
||||||
, groupWriteMode
|
, groupWriteMode
|
||||||
@ -86,7 +76,6 @@ import System.Posix.Files
|
|||||||
, ownerReadMode
|
, ownerReadMode
|
||||||
, ownerWriteMode
|
, ownerWriteMode
|
||||||
, rename
|
, rename
|
||||||
, touchFile
|
|
||||||
, unionFileModes
|
, unionFileModes
|
||||||
, removeLink
|
, removeLink
|
||||||
)
|
)
|
||||||
@ -107,7 +96,6 @@ import System.Process
|
|||||||
|
|
||||||
import qualified System.Directory as SD
|
import qualified System.Directory as SD
|
||||||
|
|
||||||
import qualified System.Posix.Files as PF
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: file operations should be threaded and not block the UI
|
-- TODO: file operations should be threaded and not block the UI
|
||||||
@ -157,7 +145,6 @@ runFileOp (FMove fo) = return $ Just $ FMove fo
|
|||||||
runFileOp (FDelete fp) = easyDelete fp >> return Nothing
|
runFileOp (FDelete fp) = easyDelete fp >> return Nothing
|
||||||
runFileOp (FOpen fp) = openFile fp >> return Nothing
|
runFileOp (FOpen fp) = openFile fp >> return Nothing
|
||||||
runFileOp (FExecute fp args) = executeFile fp args >> return Nothing
|
runFileOp (FExecute fp args) = executeFile fp args >> return Nothing
|
||||||
runFileOp _ = return Nothing
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -216,7 +203,7 @@ copyDir cm from@(_ :/ Dir fromn _)
|
|||||||
_ -> return ()
|
_ -> return ()
|
||||||
|
|
||||||
recreateSymlink f destdir
|
recreateSymlink f destdir
|
||||||
copyDir _ _ _ = return ()
|
copyDir _ _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Recreate a symlink.
|
-- |Recreate a symlink.
|
||||||
@ -229,7 +216,7 @@ recreateSymlink symf@(_ :/ SymLink {})
|
|||||||
= do
|
= do
|
||||||
symname <- readSymbolicLink (fullPath symf)
|
symname <- readSymbolicLink (fullPath symf)
|
||||||
createSymbolicLink symname (fullPath symdest </> (name . file $ symf))
|
createSymbolicLink symname (fullPath symdest </> (name . file $ symf))
|
||||||
recreateSymlink _ _ = return ()
|
recreateSymlink _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Copies the given file to the given file destination.
|
-- |Copies the given file to the given file destination.
|
||||||
@ -243,7 +230,7 @@ copyFile from@(_ :/ RegFile {}) to@(_ :/ RegFile {}) = do
|
|||||||
to' = fullPath to
|
to' = fullPath to
|
||||||
throwSameFile from' to'
|
throwSameFile from' to'
|
||||||
SD.copyFile from' to'
|
SD.copyFile from' to'
|
||||||
copyFile _ _ = return ()
|
copyFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Copies the given file to the given dir with the same filename.
|
-- |Copies the given file to the given dir with the same filename.
|
||||||
@ -258,7 +245,7 @@ copyFileToDir from@(_ :/ RegFile fn _)
|
|||||||
let from' = fullPath from
|
let from' = fullPath from
|
||||||
to' = fullPath to </> fn
|
to' = fullPath to </> fn
|
||||||
SD.copyFile from' to'
|
SD.copyFile from' to'
|
||||||
copyFileToDir _ _ = return ()
|
copyFileToDir _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Copies a file, directory or symlink. In case of a symlink, it is just
|
-- |Copies a file, directory or symlink. In case of a symlink, it is just
|
||||||
@ -277,7 +264,7 @@ easyCopy _ from@(_ :/ RegFile fn _)
|
|||||||
easyCopy cm from@(_ :/ Dir fn _)
|
easyCopy cm from@(_ :/ Dir fn _)
|
||||||
to@(_ :/ Dir {})
|
to@(_ :/ Dir {})
|
||||||
= copyDir cm from to
|
= copyDir cm from to
|
||||||
easyCopy _ _ _ = return ()
|
easyCopy _ _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -292,8 +279,7 @@ easyCopy _ _ _ = return ()
|
|||||||
deleteSymlink :: AnchoredFile FileInfo -> IO ()
|
deleteSymlink :: AnchoredFile FileInfo -> IO ()
|
||||||
deleteSymlink f@(_ :/ SymLink {})
|
deleteSymlink f@(_ :/ SymLink {})
|
||||||
= removeLink (fullPath f)
|
= removeLink (fullPath f)
|
||||||
deleteSymlink _
|
deleteSymlink _ = throw $ InvalidOperation "wrong input type"
|
||||||
= return ()
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given file, never symlinks.
|
-- |Deletes the given file, never symlinks.
|
||||||
@ -301,8 +287,7 @@ deleteFile :: AnchoredFile FileInfo -> IO ()
|
|||||||
deleteFile (_ :/ SymLink {}) = return ()
|
deleteFile (_ :/ SymLink {}) = return ()
|
||||||
deleteFile f@(_ :/ RegFile {})
|
deleteFile f@(_ :/ RegFile {})
|
||||||
= removeLink (fullPath f)
|
= removeLink (fullPath f)
|
||||||
deleteFile _
|
deleteFile _ = throw $ InvalidOperation "wrong input type"
|
||||||
= return ()
|
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given directory, never symlinks.
|
-- |Deletes the given directory, never symlinks.
|
||||||
@ -310,7 +295,7 @@ deleteDir :: AnchoredFile FileInfo -> IO ()
|
|||||||
deleteDir (_ :/ SymLink {}) = return ()
|
deleteDir (_ :/ SymLink {}) = return ()
|
||||||
deleteDir f@(_ :/ Dir {})
|
deleteDir f@(_ :/ Dir {})
|
||||||
= removeDirectory (fullPath f)
|
= removeDirectory (fullPath f)
|
||||||
deleteDir _ = return ()
|
deleteDir _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes the given directory recursively, never symlinks.
|
-- |Deletes the given directory recursively, never symlinks.
|
||||||
@ -318,7 +303,7 @@ deleteDirRecursive :: AnchoredFile FileInfo -> IO ()
|
|||||||
deleteDirRecursive (_ :/ SymLink {}) = return ()
|
deleteDirRecursive (_ :/ SymLink {}) = return ()
|
||||||
deleteDirRecursive f@(_ :/ Dir {})
|
deleteDirRecursive f@(_ :/ Dir {})
|
||||||
= removeDirectoryRecursive (fullPath f)
|
= removeDirectoryRecursive (fullPath f)
|
||||||
deleteDirRecursive _ = return ()
|
deleteDirRecursive _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Deletes a file, directory or symlink, whatever it may be.
|
-- |Deletes a file, directory or symlink, whatever it may be.
|
||||||
@ -330,8 +315,7 @@ easyDelete f@(_ :/ RegFile {})
|
|||||||
= deleteFile f
|
= deleteFile f
|
||||||
easyDelete f@(_ :/ Dir {})
|
easyDelete f@(_ :/ Dir {})
|
||||||
= deleteDirRecursive f
|
= deleteDirRecursive f
|
||||||
easyDelete _
|
easyDelete _ = throw $ InvalidOperation "wrong input type"
|
||||||
= return ()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -350,10 +334,10 @@ openFile f = spawnProcess "xdg-open" [fullPath f]
|
|||||||
-- |Executes a program with the given arguments.
|
-- |Executes a program with the given arguments.
|
||||||
executeFile :: AnchoredFile FileInfo -- ^ program
|
executeFile :: AnchoredFile FileInfo -- ^ program
|
||||||
-> [String] -- ^ arguments
|
-> [String] -- ^ arguments
|
||||||
-> IO (Maybe ProcessHandle)
|
-> IO ProcessHandle
|
||||||
executeFile prog@(_ :/ RegFile {}) args
|
executeFile prog@(_ :/ RegFile {}) args
|
||||||
= Just <$> spawnProcess (fullPath prog) args
|
= spawnProcess (fullPath prog) args
|
||||||
executeFile _ _ = return Nothing
|
executeFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -369,7 +353,7 @@ createFile (ADirOrSym td) (ValFN fn) = do
|
|||||||
throwFileDoesExist fullp
|
throwFileDoesExist fullp
|
||||||
fd <- System.Posix.IO.createFile fullp newFilePerms
|
fd <- System.Posix.IO.createFile fullp newFilePerms
|
||||||
closeFd fd
|
closeFd fd
|
||||||
createFile _ _ = return ()
|
createFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
createDir :: AnchoredFile FileInfo -> FileName -> IO ()
|
createDir :: AnchoredFile FileInfo -> FileName -> IO ()
|
||||||
@ -377,7 +361,7 @@ createDir (ADirOrSym td) (ValFN fn) = do
|
|||||||
let fullp = fullPath td </> fn
|
let fullp = fullPath td </> fn
|
||||||
throwDirDoesExist fullp
|
throwDirDoesExist fullp
|
||||||
createDirectory fullp newFilePerms
|
createDirectory fullp newFilePerms
|
||||||
createDir _ _ = return ()
|
createDir _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -395,7 +379,7 @@ renameFile af (ValFN fn) = do
|
|||||||
throwFileDoesExist tof
|
throwFileDoesExist tof
|
||||||
throwSameFile fromf tof
|
throwSameFile fromf tof
|
||||||
rename fromf tof
|
rename fromf tof
|
||||||
renameFile _ _ = return ()
|
renameFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
|
|
||||||
|
|
||||||
-- |Move a given file to the given target directory.
|
-- |Move a given file to the given target directory.
|
||||||
@ -407,11 +391,10 @@ moveFile from to@(_ :/ Dir {}) = do
|
|||||||
to' = fullPath to </> (name . file $ from)
|
to' = fullPath to </> (name . file $ from)
|
||||||
throwFileDoesExist to'
|
throwFileDoesExist to'
|
||||||
throwSameFile from' to'
|
throwSameFile from' to'
|
||||||
handle (\(SomeException e) -> do
|
catchErrno eXDEV (rename from' to') $ do
|
||||||
easyCopy Strict from to
|
easyCopy Strict from to
|
||||||
easyDelete from
|
easyDelete from
|
||||||
) $ rename from' to'
|
moveFile _ _ = throw $ InvalidOperation "wrong input type"
|
||||||
moveFile _ _ = return ()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user