Copyright | © 2016 Julian Ospald |
---|---|
License | BSD3 |
Maintainer | Julian Ospald <hasufell@posteo.de> |
Stability | experimental |
Portability | portable |
Safe Haskell | None |
Language | Haskell2010 |
This module provides high-level IO related file operations like
copy, delete, move and so on. It only operates on Path x which
guarantees us well-typed paths. Passing in Path Abs to any
of these functions generally increases safety. Passing Path Rel
may trigger looking up the current directory via getcwd
in some
cases where it cannot be avoided.
Some functions are just path-safe wrappers around
unix functions, others have stricter exception handling
and some implement functionality that doesn't have a unix
counterpart (like copyDirRecursive
).
Some of these operations are due to their nature not atomic, which means they may do multiple syscalls which form one context. Some of them also have to examine the filetypes explicitly before the syscalls, so a reasonable decision can be made. That means the result is undefined if another process changes that context while the non-atomic operation is still happening. However, where possible, as few syscalls as possible are used and the underlying exception handling is kept.
Note: BlockDevice
, CharacterDevice
, NamedPipe
and Socket
are ignored by some of the more high-level functions (like easyCopy
).
For other functions (like copyFile
), the behavior on these file types is
unreliable/unsafe. Check the documentation of those functions for details.
Synopsis
- data FileType
- data RecursiveErrorMode
- data CopyMode
- copyDirRecursive :: Path b1 -> Path b2 -> CopyMode -> RecursiveErrorMode -> IO ()
- recreateSymlink :: Path b1 -> Path b2 -> CopyMode -> IO ()
- copyFile :: Path b1 -> Path b2 -> CopyMode -> IO ()
- easyCopy :: Path b1 -> Path b2 -> CopyMode -> RecursiveErrorMode -> IO ()
- deleteFile :: Path b -> IO ()
- deleteDir :: Path b -> IO ()
- deleteDirRecursive :: Path b -> IO ()
- easyDelete :: Path b -> IO ()
- openFile :: Path b -> IO ProcessID
- executeFile :: Path b -> [ByteString] -> IO ProcessID
- createRegularFile :: FileMode -> Path b -> IO ()
- createDir :: FileMode -> Path b -> IO ()
- createDirRecursive :: FileMode -> Path b -> IO ()
- createSymlink :: Path b -> ByteString -> IO ()
- renameFile :: Path b1 -> Path b2 -> IO ()
- moveFile :: Path b1 -> Path b2 -> CopyMode -> IO ()
- readFile :: Path b -> IO ByteString
- readFileEOF :: Path b -> IO ByteString
- writeFile :: Path b -> ByteString -> IO ()
- appendFile :: Path b -> ByteString -> IO ()
- newFilePerms :: FileMode
- newDirPerms :: FileMode
- getDirsFiles :: Path b -> IO [Path b]
- getFileType :: Path b -> IO FileType
- canonicalizePath :: Path b -> IO (Path Abs)
- toAbs :: Path b -> IO (Path Abs)
Types
data RecursiveErrorMode Source #
The error mode for recursive operations.
On FailEarly
the whole operation fails immediately if any of the
recursive sub-operations fail, which is sort of the default
for IO operations.
On CollectFailures
skips errors in the recursion and keeps on recursing.
However all errors are collected in the RecursiveFailure
error type,
which is raised finally if there was any error. Also note that
RecursiveFailure
does not give any guarantees on the ordering
of the collected exceptions.
The mode for copy and file moves. Overwrite mode is usually not very well defined, but is a convenience shortcut.
File copying
:: Path b1 | source dir |
-> Path b2 | destination (parent dirs are not automatically created) |
-> CopyMode | |
-> RecursiveErrorMode | |
-> IO () |
Copies the contents of a directory recursively to the given destination, while preserving permissions. Does not follow symbolic links. This behaves more or less like the following, without descending into the destination if it already exists:
cp -a /source/dir /destination/somedir
For directory contents, this will ignore any file type that is not
RegularFile
, SymbolicLink
or Directory
.
For Overwrite
copy mode this does not prune destination directory
contents, so the destination might contain more files than the source after
the operation has completed. Permissions of existing directories are
fixed.
Safety/reliability concerns:
- not atomic
- examines filetypes explicitly
- an explicit check
throwDestinationInSource
is carried out for the top directory for basic sanity, because otherwise we might end up with an infinite copy loop... however, this operation is not carried out recursively (because it's slow)
Throws:
NoSuchThing
if source directory does not existPermissionDenied
if source directory can't be openedSameFile
if source and destination are the same file (HPathIOException
)DestinationInSource
if destination is contained in source (HPathIOException
)
Throws in FailEarly
RecursiveErrorMode only:
PermissionDenied
if output directory is not writableInvalidArgument
if source directory is wrong type (symlink)InappropriateType
if source directory is wrong type (regular file)
Throws in CollectFailures
RecursiveErrorMode only:
RecursiveFailure
if any of the recursive operations that are not part of the top-directory sanity-checks fail (HPathIOException
)
Throws in Strict
CopyMode only:
AlreadyExists
if destination already exists
Note: may call getcwd
(only if destination is a relative path)
Recreate a symlink.
In Overwrite
copy mode only files and empty directories are deleted.
Safety/reliability concerns:
Overwrite
mode is inherently non-atomic
Throws:
InvalidArgument
if source file is wrong type (not a symlink)PermissionDenied
if output directory cannot be written toPermissionDenied
if source directory cannot be openedSameFile
if source and destination are the same file (HPathIOException
)
Throws in Strict
mode only:
AlreadyExists
if destination already exists
Throws in Overwrite
mode only:
UnsatisfiedConstraints
if destination file is non-empty directory
Notes:
- calls
symlink
- calls
getcwd
in Overwrite mode (if destination is a relative path)
Copies the given regular file to the given destination.
Neither follows symbolic links, nor accepts them.
For "copying" symbolic links, use recreateSymlink
instead.
Note that this is still sort of a low-level function and doesn't
examine file types. For a more high-level version, use easyCopy
instead.
In Overwrite
copy mode only overwrites actual files, not directories.
In Strict
mode the destination file must not exist.
Safety/reliability concerns:
Overwrite
mode is not atomic- when used on
CharacterDevice
, reads the "contents" and copies them to a regular file, which might take indefinitely - when used on
BlockDevice
, may either read the "contents" and copy them to a regular file (potentially hanging indefinitely) or may create a regular empty destination file - when used on
NamedPipe
, will hang indefinitely
Throws:
NoSuchThing
if source file does not existNoSuchThing
if source file is a aSocket
PermissionDenied
if output directory is not writablePermissionDenied
if source directory can't be openedInvalidArgument
if source file is wrong type (symlink or directory)SameFile
if source and destination are the same file (HPathIOException
)
Throws in Strict
mode only:
AlreadyExists
if destination already exists
Notes:
- may call
getcwd
in Overwrite mode (if destination is a relative path)
easyCopy :: Path b1 -> Path b2 -> CopyMode -> RecursiveErrorMode -> IO () Source #
Copies a regular file, directory or symbolic link. In case of a symbolic link it is just recreated, even if it points to a directory. Any other file type is ignored.
Safety/reliability concerns:
- examines filetypes explicitly
- calls
copyDirRecursive
for directories
Note: may call getcwd
in Overwrite mode (if destination is a relative path)
File deletion
deleteFile :: Path b -> IO () Source #
Deletes the given file. Raises eISDIR
if run on a directory. Does not follow symbolic links.
Throws:
InappropriateType
for wrong file type (directory)NoSuchThing
if the file does not existPermissionDenied
if the directory cannot be read
deleteDir :: Path b -> IO () Source #
Deletes the given directory, which must be empty, never symlinks.
Throws:
InappropriateType
for wrong file type (symlink to directory)InappropriateType
for wrong file type (regular file)NoSuchThing
if directory does not existUnsatisfiedConstraints
if directory is not emptyPermissionDenied
if we can't open or write to parent directory
Notes: calls rmdir
deleteDirRecursive :: Path b -> IO () Source #
Deletes the given directory recursively. Does not follow symbolic
links. Tries deleteDir
first before attemtping a recursive
deletion.
On directory contents this behaves like easyDelete
and thus will ignore any file type that is not RegularFile
,
SymbolicLink
or Directory
.
Safety/reliability concerns:
- not atomic
- examines filetypes explicitly
Throws:
InappropriateType
for wrong file type (symlink to directory)InappropriateType
for wrong file type (regular file)NoSuchThing
if directory does not existPermissionDenied
if we can't open or write to parent directory
easyDelete :: Path b -> IO () Source #
Deletes a file, directory or symlink. In case of directory, performs recursive deletion. In case of a symlink, the symlink file is deleted. Any other file type is ignored.
Safety/reliability concerns:
- examines filetypes explicitly
- calls
deleteDirRecursive
for directories
File opening
openFile :: Path b -> IO ProcessID Source #
Opens a file appropriately by invoking xdg-open. The file type is not checked. This forks a process.
:: Path b | program |
-> [ByteString] | arguments |
-> IO ProcessID |
Executes a program with the given arguments. This forks a process.
File creation
createRegularFile :: FileMode -> Path b -> IO () Source #
Create an empty regular file at the given directory with the given filename.
Throws:
PermissionDenied
if output directory cannot be written toAlreadyExists
if destination already existsNoSuchThing
if any of the parent components of the path do not exist
createDir :: FileMode -> Path b -> IO () Source #
Create an empty directory at the given directory with the given filename.
Throws:
PermissionDenied
if output directory cannot be written toAlreadyExists
if destination already existsNoSuchThing
if any of the parent components of the path do not exist
createDirRecursive :: FileMode -> Path b -> IO () Source #
Create an empty directory at the given directory with the given filename. All parent directories are created with the same filemode. This basically behaves like:
mkdir -p /some/dir
Safety/reliability concerns:
- not atomic
Throws:
PermissionDenied
if any part of the path components do not exist and cannot be written toAlreadyExists
if destination already exists and is not a directory
Note: calls getcwd
if the input path is a relative path
:: Path b | destination file |
-> ByteString | path the symlink points to |
-> IO () |
Create a symlink.
Throws:
PermissionDenied
if output directory cannot be written toAlreadyExists
if destination file already existsNoSuchThing
if any of the parent components of the path do not exist
Note: calls symlink
File renaming/moving
renameFile :: Path b1 -> Path b2 -> IO () Source #
Rename a given file with the provided filename. Destination and source
must be on the same device, otherwise eXDEV
will be raised.
Does not follow symbolic links, but renames the symbolic link file.
Safety/reliability concerns:
- has a separate set of exception handling, apart from the syscall
Throws:
NoSuchThing
if source file does not existPermissionDenied
if output directory cannot be written toPermissionDenied
if source directory cannot be openedUnsupportedOperation
if source and destination are on different devicesAlreadyExists
if destination already existsSameFile
if destination and source are the same file (HPathIOException
)
Note: calls rename
(but does not allow to rename over existing files)
Move a file. This also works across devices by copy-delete fallback. And also works on directories.
Does not follow symbolic links, but renames the symbolic link file.
Safety/reliability concerns:
Overwrite
mode is not atomic- copy-delete fallback is inherently non-atomic
- since this function calls
easyCopy
andeasyDelete
as a fallback torenameFile
, file types that are notRegularFile
,SymbolicLink
orDirectory
may be ignored - for
Overwrite
mode, the destination will be deleted (not recursively) before moving
Throws:
NoSuchThing
if source file does not existPermissionDenied
if output directory cannot be written toPermissionDenied
if source directory cannot be openedSameFile
if destination and source are the same file (HPathIOException
)
Throws in Strict
mode only:
AlreadyExists
if destination already exists
Notes:
- calls
rename
(but does not allow to rename over existing files) - calls
getcwd
in Overwrite mode if destination is a relative path
File reading
readFile :: Path b -> IO ByteString Source #
Read the given file at once into memory as a strict ByteString. Symbolic links are followed, no sanity checks on file size or file type. File must exist.
Note: the size of the file is determined in advance, as to only have one allocation.
Safety/reliability concerns:
- since amount of bytes to read is determined in advance, the file might be read partially only if something else is appending to it while reading
- the whole file is read into memory!
Throws:
InappropriateType
if file is not a regular file or a symlinkPermissionDenied
if we cannot read the file or the directory containting itNoSuchThing
if the file does not exist
readFileEOF :: Path b -> IO ByteString Source #
Read the given file in chunks of size `8192` into memory until
fread
returns 0. Returns a lazy ByteString, because it uses
Builders under the hood.
Safety/reliability concerns:
- the whole file is read into memory!
Throws:
InappropriateType
if file is not a regular file or a symlinkPermissionDenied
if we cannot read the file or the directory containting itNoSuchThing
if the file does not exist
File writing
writeFile :: Path b -> ByteString -> IO () Source #
Write a given ByteString to a file, truncating the file beforehand. The file must exist. Follows symlinks.
Throws:
InappropriateType
if file is not a regular file or a symlinkPermissionDenied
if we cannot read the file or the directory containting itNoSuchThing
if the file does not exist
appendFile :: Path b -> ByteString -> IO () Source #
Append a given ByteString to a file. The file must exist. Follows symlinks.
Throws:
InappropriateType
if file is not a regular file or a symlinkPermissionDenied
if we cannot read the file or the directory containting itNoSuchThing
if the file does not exist
File permissions
newFilePerms :: FileMode Source #
Default permissions for a new file.
newDirPerms :: FileMode Source #
Default permissions for a new directory.
Directory reading
Gets all filenames of the given directory. This excludes "." and "..". This version does not follow symbolic links.
The contents are not sorted and there is no guarantee on the ordering.
Throws:
NoSuchThing
if directory does not existInappropriateType
if file type is wrong (file)InappropriateType
if file type is wrong (symlink to file)InappropriateType
if file type is wrong (symlink to dir)PermissionDenied
if directory cannot be opened
Filetype operations
getFileType :: Path b -> IO FileType Source #
Get the file type of the file located at the given path. Does not follow symbolic links.
Throws:
NoSuchThing
if the file does not existPermissionDenied
if any part of the path is not accessible
Others
canonicalizePath :: Path b -> IO (Path Abs) Source #
Applies realpath
on the given path.
Throws:
NoSuchThing
if the file at the given path does not existNoSuchThing
if the symlink is broken