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
|
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) \
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue