From 0111ba98ea0a91965ebb215b408ee1c10286a245 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Wed, 27 Feb 2013 17:18:41 +0100 Subject: [PATCH] Handle hard links to other files in the output --- doc/manual/release-notes.xml | 10 ++++++++++ src/libstore/local-store.cc | 32 ++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/doc/manual/release-notes.xml b/doc/manual/release-notes.xml index 9cbd1fbb8..513e4f03b 100644 --- a/doc/manual/release-notes.xml +++ b/doc/manual/release-notes.xml @@ -6,6 +6,16 @@ + + +
Release 1.5 (February 27, 2013) + +This is a brown paper bag release to fix a regression introduced +by the hard link security fix in 1.4. + +
+ +
Release 1.4 (February 26, 2013) diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index ff6b6c71c..2c439a66d 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -509,7 +509,11 @@ void canonicaliseTimestampAndPermissions(const Path & path) } -static void canonicalisePathMetaData_(const Path & path, uid_t fromUid) +typedef std::pair Inode; +typedef set InodesSeen; + + +static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSeen & inodesSeen) { checkInterrupt(); @@ -521,10 +525,24 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid) has already been checked in dumpPath(). */ assert(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode)); - canonicaliseTimestampAndPermissions(path, st); + /* Fail if the file is not owned by the build user. This prevents + us from messing up the ownership/permissions of files + hard-linked into the output (e.g. "ln /etc/shadow $out/foo"). + However, ignore files that we chown'ed ourselves previously to + ensure that we don't fail on hard links within the same build + (i.e. "touch $out/foo; ln $out/foo $out/bar"). */ + if (fromUid != (uid_t) -1 && st.st_uid != fromUid) { + assert(!S_ISDIR(st.st_mode)); + if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end()) + throw BuildError(format("invalid ownership on file `%1%'") % path); + mode_t mode = st.st_mode & ~S_IFMT; + assert(st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore); + return; + } - if (fromUid != (uid_t) -1 && st.st_uid != fromUid) - throw BuildError(format("invalid ownership on file `%1%'") % path); + inodesSeen.insert(Inode(st.st_dev, st.st_ino)); + + canonicaliseTimestampAndPermissions(path, st); /* Change ownership to the current uid. If it's a symlink, use lchown if available, otherwise don't bother. Wrong ownership @@ -547,14 +565,16 @@ static void canonicalisePathMetaData_(const Path & path, uid_t fromUid) if (S_ISDIR(st.st_mode)) { Strings names = readDirectory(path); foreach (Strings::iterator, i, names) - canonicalisePathMetaData_(path + "/" + *i, fromUid); + canonicalisePathMetaData_(path + "/" + *i, fromUid, inodesSeen); } } void canonicalisePathMetaData(const Path & path, uid_t fromUid) { - canonicalisePathMetaData_(path, fromUid); + InodesSeen inodesSeen; + + canonicalisePathMetaData_(path, fromUid, inodesSeen); /* On platforms that don't have lchown(), the top-level path can't be a symlink, since we can't change its ownership. */