generate-programs-index: Check whether symlink targets exist
Fixes https://github.com/NixOS/nixpkgs/issues/26661
This commit is contained in:
parent
74c917454c
commit
a5e125330c
|
@ -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) \
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue