generate-programs-index: Check whether symlink targets exist

Fixes https://github.com/NixOS/nixpkgs/issues/26661
This commit is contained in:
Eelco Dolstra 2017-07-07 16:45:45 +02:00
parent 74c917454c
commit a5e125330c
No known key found for this signature in database
GPG key ID: 8170B4726D7198DE
3 changed files with 75 additions and 17 deletions

View file

@ -22,14 +22,14 @@ stdenv.mkDerivation {
cp ${./file-cache.hh} file-cache.hh cp ${./file-cache.hh} file-cache.hh
g++ -g ${./generate-programs-index.cc} -Wall -std=c++14 -o $out/bin/generate-programs-index -I . \ g++ -Os -g ${./generate-programs-index.cc} -Wall -std=c++14 -o $out/bin/generate-programs-index -I . \
$(pkg-config --cflags nix-main) \ $(pkg-config --cflags nix-main) \
$(pkg-config --libs nix-main) \ $(pkg-config --libs nix-main) \
$(pkg-config --libs nix-expr) \ $(pkg-config --libs nix-expr) \
$(pkg-config --libs nix-store) \ $(pkg-config --libs nix-store) \
-lsqlite3 -lgc -lsqlite3 -lgc
g++ -g ${./index-debuginfo.cc} -Wall -std=c++14 -o $out/bin/index-debuginfo -I . \ g++ -Os -g ${./index-debuginfo.cc} -Wall -std=c++14 -o $out/bin/index-debuginfo -I . \
$(pkg-config --cflags nix-main) \ $(pkg-config --cflags nix-main) \
$(pkg-config --libs nix-main) \ $(pkg-config --libs nix-main) \
$(pkg-config --libs nix-store) \ $(pkg-config --libs nix-store) \

View file

@ -23,6 +23,11 @@ class FileCache
Sync<State> state_; Sync<State> state_;
struct Stat : FSAccessor::Stat
{
std::string target;
};
public: public:
FileCache(const Path & path) FileCache(const Path & path)
@ -42,6 +47,7 @@ public:
type integer not null, type integer not null,
fileSize integer, fileSize integer,
isExecutable integer, isExecutable integer,
target text,
primary key (storePath, subPath), primary key (storePath, subPath),
foreign key (storePath) references StorePaths(id) on delete cascade foreign key (storePath) references StorePaths(id) on delete cascade
); );
@ -60,17 +66,17 @@ public:
state->insertPath.create(state->db, state->insertPath.create(state->db,
"insert or ignore into StorePaths(path) values (?)"); "insert or ignore into StorePaths(path) values (?)");
state->queryFiles.create(state->db, state->queryFiles.create(state->db,
"select subPath, type, fileSize, isExecutable from StorePathContents where storePath = ?"); "select subPath, type, fileSize, isExecutable, target from StorePathContents where storePath = ?");
state->insertFile.create(state->db, state->insertFile.create(state->db,
"insert into StorePathContents(storePath, subPath, type, fileSize, isExecutable) values (?, ?, ?, ?, ?)"); "insert into StorePathContents(storePath, subPath, type, fileSize, isExecutable, target) values (?, ?, ?, ?, ?, ?)");
} }
/* Return the files in a store path, using a SQLite database to /* Return the files in a store path, using a SQLite database to
cache the results. */ cache the results. */
std::map<std::string, FSAccessor::Stat> std::map<std::string, Stat>
getFiles(ref<BinaryCacheStore> binaryCache, const Path & storePath) getFiles(ref<BinaryCacheStore> binaryCache, const Path & storePath)
{ {
std::map<std::string, FSAccessor::Stat> files; std::map<std::string, Stat> files;
/* Look up the path in the SQLite cache. */ /* Look up the path in the SQLite cache. */
{ {
@ -80,8 +86,13 @@ public:
auto id = useQueryPath.getInt(0); auto id = useQueryPath.getInt(0);
auto useQueryFiles(state->queryFiles.use()(id)); auto useQueryFiles(state->queryFiles.use()(id));
while (useQueryFiles.next()) { while (useQueryFiles.next()) {
files[useQueryFiles.getStr(0)] = FSAccessor::Stat{ Stat st;
(FSAccessor::Type) useQueryFiles.getInt(1), (uint64_t) useQueryFiles.getInt(2), useQueryFiles.getInt(3) != 0}; st.type = (FSAccessor::Type) useQueryFiles.getInt(1);
st.fileSize = (uint64_t) useQueryFiles.getInt(2);
st.isExecutable = useQueryFiles.getInt(3) != 0;
if (!useQueryFiles.isNull(4))
st.target = useQueryFiles.getStr(4);
files.emplace(useQueryFiles.getStr(0), st);
} }
return files; return files;
} }
@ -92,7 +103,7 @@ public:
std::function<void(const std::string &, json &)> recurse; std::function<void(const std::string &, json &)> recurse;
recurse = [&](const std::string & relPath, json & v) { recurse = [&](const std::string & relPath, json & v) {
FSAccessor::Stat st; Stat st;
std::string type = v["type"]; std::string type = v["type"];
@ -108,6 +119,7 @@ public:
st.isExecutable = v.value("executable", false); st.isExecutable = v.value("executable", false);
} else if (type == "symlink") { } else if (type == "symlink") {
st.type = FSAccessor::Type::tSymlink; st.type = FSAccessor::Type::tSymlink;
st.target = v.value("target", "");
} else return; } else return;
files[relPath] = st; files[relPath] = st;
@ -152,6 +164,7 @@ public:
(x.second.type) (x.second.type)
(x.second.fileSize, x.second.type == FSAccessor::Type::tRegular) (x.second.fileSize, x.second.type == FSAccessor::Type::tRegular)
(x.second.isExecutable, x.second.type == FSAccessor::Type::tRegular) (x.second.isExecutable, x.second.type == FSAccessor::Type::tRegular)
(x.second.target, x.second.type == FSAccessor::Type::tSymlink)
.exec(); .exec();
} }

View file

@ -137,15 +137,60 @@ void mainWrapped(int argc, char * * argv)
std::set<std::string> programs; std::set<std::string> programs;
for (auto & file : files) { for (auto file : files) {
// FIXME: we assume that symlinks point to
// programs. Should check that.
if (file.second.type == FSAccessor::Type::tDirectory ||
(file.second.type == FSAccessor::Type::tRegular && !file.second.isExecutable))
continue;
std::smatch match; std::smatch match;
if (std::regex_match(file.first, match, isProgram)) if (!std::regex_match(file.first, match, isProgram)) continue;
programs.insert(match[1]);
auto curPath = file.first;
auto stat = file.second;
while (stat.type == FSAccessor::Type::tSymlink) {
auto target = canonPath(
hasPrefix(stat.target, "/")
? stat.target
: dirOf(storePath + "/" + curPath) + "/" + stat.target);
// FIXME: resolve symlinks in components of stat.target.
if (!hasPrefix(target, "/nix/store/")) break;
/* Assume that symlinks to other store paths point
to executables. But check symlinks within the
same store path. */
if (target.compare(0, storePath.size(), storePath) != 0) {
stat.type = FSAccessor::Type::tRegular;
stat.isExecutable = true;
break;
}
std::string sub(target, storePath.size() + 1);
auto file2 = files.find(sub);
if (file2 == files.end()) {
printError("symlink %s has non-existent target %s",
storePath + "/" + file.first, stat.target);
break;
}
if (file2->second.type != FSAccessor::Type::tRegular
|| !file2->second.isExecutable)
{
printError("symlink %s points to non-executable %s",
storePath + "/" + file.first, stat.target);
break;
}
curPath = sub;
stat = file2->second;
}
if (stat.type == FSAccessor::Type::tDirectory
|| stat.type == FSAccessor::Type::tSymlink
|| (stat.type == FSAccessor::Type::tRegular && !stat.isExecutable))
continue;
programs.insert(match[1]);
} }
if (programs.empty()) return; if (programs.empty()) return;