Skip to content

Commit 1102fdd

Browse files
committed
Add runtime closure invariant
1 parent db20d22 commit 1102fdd

File tree

6 files changed

+69
-10
lines changed

6 files changed

+69
-10
lines changed

src/libstore/build/local-derivation-goal.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2763,7 +2763,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
27632763
isn't statically known so that we can safely unlock the path before
27642764
the next iteration */
27652765
if (newInfo.ca)
2766-
localStore.registerValidPaths({{newInfo.path, newInfo}});
2766+
localStore.registerValidPaths({{newInfo.path, newInfo}}, false);
27672767

27682768
infos.emplace(outputName, std::move(newInfo));
27692769
}

src/libstore/local-store.cc

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,6 @@ uint64_t LocalStore::addValidPath(State & state,
861861
throw Error("cannot add path '%s' to the Nix store because it claims to be content-addressed but isn't",
862862
printStorePath(info.path));
863863

864-
syncPathPermissions(info);
865-
866864
state.stmts->RegisterValidPath.use()
867865
(printStorePath(info.path))
868866
(info.narHash.to_string(Base16, true))
@@ -1174,20 +1172,22 @@ void LocalStore::syncPathPermissions(const ValidPathInfo & info)
11741172
}
11751173
}
11761174

1177-
void LocalStore::registerValidPath(const ValidPathInfo & info)
1175+
void LocalStore::registerValidPath(const ValidPathInfo & info, bool syncPermissions)
11781176
{
1179-
registerValidPaths({{info.path, info}});
1177+
registerValidPaths({{info.path, info}}, syncPermissions);
11801178
}
11811179

11821180

1183-
void LocalStore::registerValidPaths(const ValidPathInfos & infos)
1181+
void LocalStore::registerValidPaths(const ValidPathInfos & infos, bool syncPermissions)
11841182
{
11851183
/* SQLite will fsync by default, but the new valid paths may not
11861184
be fsync-ed. So some may want to fsync them before registering
11871185
the validity, at the expense of some speed of the path
11881186
registering operation. */
11891187
if (settings.syncBeforeRegistering) sync();
11901188

1189+
std::vector<StorePath> sortedPaths;
1190+
11911191
retrySQLite<void>([&]() {
11921192
auto state(_state.lock());
11931193

@@ -1222,7 +1222,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
12221222
error if a cycle is detected and roll back the
12231223
transaction. Cycles can only occur when a derivation
12241224
has multiple outputs. */
1225-
topoSort(paths,
1225+
sortedPaths = topoSort(paths,
12261226
{[&](const StorePath & path) {
12271227
auto i = infos.find(path);
12281228
return i == infos.end() ? StorePathSet() : i->second.references;
@@ -1236,12 +1236,55 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
12361236

12371237
txn.commit();
12381238
});
1239+
1240+
std::reverse(sortedPaths.begin(), sortedPaths.end());
1241+
1242+
if (syncPermissions)
1243+
for (auto path : sortedPaths)
1244+
syncPathPermissions(infos.at(path));
12391245
}
12401246

12411247
void LocalStore::setCurrentAccessStatus(const Path & path, const LocalStore::AccessStatus & status)
12421248
{
12431249
experimentalFeatureSettings.require(Xp::ACLs);
12441250

1251+
if (isInStore(path)) {
1252+
StorePath storePath(baseNameOf(path));
1253+
1254+
// FIXME(acls): cache is broken when called from registerValidPaths
1255+
1256+
std::promise<ref<const ValidPathInfo>> promise;
1257+
1258+
queryPathInfoUncached(storePath,
1259+
{[&](std::future<std::shared_ptr<const ValidPathInfo>> result) {
1260+
try {
1261+
promise.set_value(ref(result.get()));
1262+
} catch (...) {
1263+
promise.set_exception(std::current_exception());
1264+
}
1265+
}});
1266+
1267+
auto info = promise.get_future().get();
1268+
1269+
for (auto reference : info->references) {
1270+
if (reference == storePath) continue;
1271+
auto otherStatus = getCurrentAccessStatus(printStorePath(reference));
1272+
if (!otherStatus.isProtected) continue;
1273+
if (!status.isProtected)
1274+
throw AccessDenied("can not make %s non-protected because it references a protected path %s", path, printStorePath(reference));
1275+
std::vector<AccessControlEntity> difference;
1276+
std::set_difference(status.entities.begin(), status.entities.end(), otherStatus.entities.begin(), otherStatus.entities.end(), difference.begin());
1277+
1278+
if (! difference.empty()) {
1279+
std::string entities;
1280+
for (auto entity : difference) entities += ACL::printTag(entity) + ", ";
1281+
throw AccessDenied("can not allow %s access to %s because it references path %s to which they do not have access", entities.substr(0, entities.size()-2), path, printStorePath(reference));
1282+
}
1283+
}
1284+
}
1285+
1286+
debug("setting access status %s on %s", status.json().dump(), path);
1287+
12451288
using namespace ACL;
12461289

12471290
// NOTE: On Darwin, the standard posix permissions are not part of the ACL API.

src/libstore/local-store.hh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,9 @@ public:
257257
* register the hash of the file system contents of the path. The
258258
* hash must be a SHA-256 hash.
259259
*/
260-
void registerValidPath(const ValidPathInfo & info);
260+
void registerValidPath(const ValidPathInfo & info, bool syncPermissions = true);
261261

262-
void registerValidPaths(const ValidPathInfos & infos);
262+
void registerValidPaths(const ValidPathInfos & infos, bool syncPermissions = true);
263263

264264
unsigned int getProtocol() override;
265265

src/libutil/acl.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@ void Permissions::allowExecute(bool allow)
355355
erase(perms.begin(), perms.end());
356356
}
357357

358+
std::string printTag(Tag tag)
359+
{
360+
return std::visit(overloaded {
361+
[&](User u){
362+
return fmt("user with uid %d", u.uid);
363+
},
364+
[&](Group g){
365+
return fmt("group with gid %d", g.gid);
366+
},
367+
}, tag);
368+
}
369+
370+
358371
AccessControlList::AccessControlList(std::filesystem::path p)
359372
{
360373
auto native = Native::AccessControlList(p);
@@ -384,6 +397,7 @@ void AccessControlList::set(std::filesystem::path p)
384397
native[Other {}] = current[Other {}];
385398
if (!empty())
386399
native[Mask {}] = {Permission::Read, Permission::Write, Permission::Execute};
400+
if (current == native) return;
387401
#endif
388402
native.set(p);
389403
}

src/libutil/acl.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ struct Group
6565
*/
6666
typedef std::variant<User, Group> Tag;
6767

68+
std::string printTag(Tag t);
69+
6870
namespace Native {
6971
#ifdef __APPLE__
7072

tests/repl.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ testRepl () {
5252
# Simple test, try building a drv
5353
testRepl
5454
# Same thing (kind-of), but with a remote store.
55-
testRepl --store "$TEST_ROOT/store?real=$NIX_STORE_DIR"
55+
testRepl --store "$TEST_ROOT/repl-store?real=$NIX_STORE_DIR"
5656

5757
testReplResponse () {
5858
local commands="$1"; shift

0 commit comments

Comments
 (0)