file permissions and copying files
Duncan Coutts
duncan.coutts at worc.ox.ac.uk
Fri Feb 20 06:05:55 EST 2009
All,
I have complained before about the System.Directory file permissions
API, and how it relates to copyFile.
Let me give an example of where the existing copyFile API is too
limited. Perhaps this will help us think about what an improved API
might be.
The example of course is build system component of Cabal. It turns out
that we actually need *three* different kinds of copyFile. The only
difference in each case is the treatment of file permissions /
attributes.
To achieve the worst case consider a source tree that is completely read
only and a user umask that makes new files not world readable.
The cases are:
1. Copying files within the build tree, eg as a dummy
pre-processor. In this case it should behave like "cat src >
dest" and not like cp. That is, it is essential that we create
the new files with default permissions and not copy the source
permissions. Otherwise we would end up making read-only files
which would cause problems later (especially on windows).
2. Copying source files into a temp directory to prepare a source
tarball. In this case we really do want to copy file
permissions. In particular we want to copy executable
permissions on files like ./configure. In this case it does not
matter if we also copy read-only permissions because we will
only delete these files, not try to overwrite them. Also, the
tarball creation code will only copy executable permissions.
3. Installing files. In this case it is essential that we specify
exact permissions and ignore the umask of the installing user or
the permissions of the source files. The source files could be
read-only and that would mess things up on windows as we would
not be able to re-install over the read-only files. We also do
not want to copy permissions generally because if the umask of
the user that built the files was such that they're not globally
readable then installing globally (eg via sudo) will install
files only readable by root. Instead we have to specify that the
files are read/write by their owner and readable by everyone
else (and executable for binaries).
If we were to generalise from these examples we'd have some copyFile
function that took an extra function to generate the permissions from a
combination of the source and default permissions (or it could ignore
both and use specific ones).
Note that in the case where I had to set specific permissions I didn't
really need full control over unix permission bits, I just wanted a
couple properties. I needed executable or not, readable/writable by the
file owner and readable/writable by others. Is that sufficiently
abstract to map onto both Windows and Unix file permissions?
The thing that works least well between unix and windows file
permissions is group ownership and permissions but can these be ignored
in most portable situations?
So at the moment we have:
data Permissions = Permissions {
readable :: Bool,
writable :: Bool,
executable :: Bool,
searchable :: Bool
}
The main problem with this is that it is not abstract so it looses
information when we do:
getPermissions src >>= setPermissions dst
However suppose it was an abstract type such that this worked and had
the same query functions as above. Then for setting permissions we'd
also have setReadable, etc :: Permissions -> Permissions. However as I
mentioned above for Cabal we'd need to distinguish setting
readable/writable for the file owner vs for all other users.
So how about this:
data Permissions -- abstract
getPermissions :: FilePath -> IO Permissions
setPermissions :: FilePath -> Permissions -> IO ()
-- | only tells us effective permissions for the current process
readable :: Permissions -> Bool
writable :: Permissions -> Bool
executable :: Permissions -> Bool
searchable :: Permissions -> Bool
setUserReadable, setOtherReadable :: Bool -> Permissions -> Permissions
setUserWritable, setOtherWritable :: Bool -> Permissions -> Permissions
setExecutable :: Bool -> Permissions -> Permissions
setSearchable :: Bool -> Permissions -> Permissions
So for Unix the mapping is:
setUserReadable True/False chmod u+r / u-r
setUserWritable True/False chmod u+w / u-w
setOtherReadable True/False chmod go+r / go-r
setOtherWritable True/False chmod go+w / go-w
setExecutable True/False chmod +x / -x (for files)
setSearchable True/False chmod +x / -x (for dirs)
So I'm aligning group with other and not distinguishing
executable/searchable for user vs others.
Note also that what is set by set* is not necessarily the same as what
we get back from the query functions because those take all permissions
into account including group permissions and other ACLs that we cannot
manipulate via this API.
Comments?
Duncan
More information about the Libraries
mailing list