permissions api in directory package is useless (and possibly
harmful)
Duncan Coutts
duncan.coutts at worc.ox.ac.uk
Wed Jan 28 08:13:02 EST 2009
All,
We need to think about a new better permissions api for the
System.Directory module. The current api and also the implementation are
at best useless and possibly harmful.
I've been trying to debug various permission problems related to
installing files with Cabal on Unix and Windows. Half the problems seem
to stem from the permissions api and the copying of permissions in
copyFile.
data Permissions = Permissions {
readable :: Bool,
writable :: Bool,
executable :: Bool,
searchable :: Bool
}
getPermissions :: FilePath -> IO Permissions
setPermissions :: FilePath -> Permissions -> IO ()
These are clearly designed for the unix permissions model, however they
do not map onto it very usefully. get/setPermissions only get the user
permissions, not the group or other. So for example if I have a file:
-rw-rw-rw- 1 duncan users 0 2009-01-28 12:34 foo
then
setPermissions "foo" (Permissions True False False False)
only removes write permissions from me, not from everyone else:
-r--rw-rw- 1 duncan users 0 2009-01-28 12:34 foo
which cannot be what we wanted.
It's also pretty useless for installing files globally. For that we want
to say that a file is readable by everyone and only writable by the
owner (usually root). It might even be ok to say only readable by
everyone and writable by nobody, but we cannot even do that. Combine
that with copyFile copying permissions and we can easily lock people out
or install world-writable files depending on the umask of the user
building the software.
On windows getPermissions tells us almost nothing. In particular if it
says the file is readable or writable, this is no guarantee that we can
read or write the file. getPermissions does not look at permissions. It
only consults the old DOS read-only attribute (and the file extension to
tell us if a file is executable).
Similarly, on windows setPermissions also only sets the read-only
attribute. The read-only attribute should really be avoided. It has
rather unhelpful semantics. For example moving a file over a read-only
file fails, where as if windows permissions (ACLs) are used then it
works as expected (same as on POSIX). The read-only attribute is only
there for compatibility with heritage software, we should really never
set it.
There is also an implementation of copyPermissions, it's not actually
exposed in System.Directory but it is used by copyFile. It calls stat to
get the permissions and chmod to set them. The implementation is the
same between unix and windows, on windows it uses the stat and chmod
from the msvcrt library.
On Unix copyPermissions does work and is probably the right thing for
copyFile to do. It's the default behaviour of /bin/cp for example.
However it does mean there is no easy efficient way to make a copy of a
file where the destination file gets the default permissions, given the
umask of the current user.
This means that copyFile is not appropriate for installing files, since
the user building a package is not necessarily the same as the one
installing it and their umasks can and often are different. The unix
install program does not copy permissions, instead it sets them
explicitly. We have no portable way of doing the same. But we can at
least use the System.Posix.Files.setFileMode to do it.
One nice thing about copyFile is that it replaces the destination file
atomically. However if we have to set the permissions in a second step
then we loose this nice property. Ideally we would create the temporary
file in the destination directory (exclusively and with permissions such
that no other user could write to our file), we'd copy the data over,
set the new destination permissions and atomically replace the
destination file.
The copyPermissions function on windows is useless. It does not copy
permissions. The way that msvcrt implements stat and chmod means that
the only "permissions" that are copied is the old DOS read-only
attribute. Perhaps copying the read-only attribute is the right thing
for copyFile to do, it's what the Win32 CopyFile function does, however
it's never what I want to do for installing software package files.
So, can we craft a useful api for permissions? We should consider what
the defaults for copyfile etc should be with respect to permissions.
Where those defaults are not helpful can we think of a way to let us do
what we want (eg get default or specific permissions instead of copying
permissions).
Or is it impossible and we just have to use system-specific functions
whenever we need something non-standard. In which case we need to make
those functions better, they appear to be mostly lacking for the Win32
case.
In either case I'm sure the existing permissions api needs to be
deprecated with big flashing warnings.
Duncan
Oh, and while I'm thinking about it: it's not currently possible to open
new/exclusive files with specific permissions and the open temp file
functions on windows do not ensure the file is writable only by the
current user.
More information about the Libraries
mailing list