diff options
Diffstat (limited to 'src/libutil/immutable.cc')
-rw-r--r-- | src/libutil/immutable.cc | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/src/libutil/immutable.cc b/src/libutil/immutable.cc new file mode 100644 index 000000000000..f72f85625486 --- /dev/null +++ b/src/libutil/immutable.cc @@ -0,0 +1,67 @@ +#include "config.h" + +#include "immutable.hh" +#include "util.hh" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#if HAVE_LINUX_FS_H +#include <linux/fs.h> +#include <sys/ioctl.h> +#include <errno.h> +#endif + +namespace nix { + + +void changeMutable(const Path & path, bool mut) +{ +#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL) + + /* Don't even try if we're not root. One day we should support + the CAP_LINUX_IMMUTABLE capability. */ + if (getuid() != 0) return; + + /* The O_NOFOLLOW is important to prevent us from changing the + mutable bit on the target of a symlink (which would be a + security hole). */ + AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW); + if (fd == -1) { + if (errno == ELOOP) return; // it's a symlink + throw SysError(format("opening file `%1%'") % path); + } + + unsigned int flags = 0, old; + + /* Silently ignore errors getting/setting the immutable flag so + that we work correctly on filesystems that don't support it. */ + if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return; + + old = flags; + + if (mut) flags &= ~FS_IMMUTABLE_FL; + else flags |= FS_IMMUTABLE_FL; + + if (old == flags) return; + + if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return; + +#endif +} + + +void makeImmutable(const Path & path) +{ + changeMutable(path, false); +} + + +void makeMutable(const Path & path) +{ + changeMutable(path, true); +} + + +} |