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
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 --libs nix-main) \
$(pkg-config --libs nix-expr) \
$(pkg-config --libs nix-store) \
-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 --libs nix-main) \
$(pkg-config --libs nix-store) \

View file

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

View file

@ -137,15 +137,60 @@ void mainWrapped(int argc, char * * argv)
std::set<std::string> programs;
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;
for (auto file : files) {
std::smatch match;
if (std::regex_match(file.first, match, isProgram))
programs.insert(match[1]);
if (!std::regex_match(file.first, match, isProgram)) continue;
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;