
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