Skip to content

Commit 9eb32d7

Browse files
MichaelCuevasmeta-codesync[bot]
authored andcommitted
Add backing-store path ACL lookups
Summary: Add the minimal path ACL lookup plumbing from EdenFS backing stores through Sapling backingstore FFI to EdenAPI /check_path_permission. The coroutine API returns structured per-path ACL entries and per-path errors so EdenFS can populate requestAcl data without changing the EdenFS Thrift API. Keep this diff focused on the backing-store lookup surface. FilteredBackingStore unwraps the filter root and forwards to the backing store. Eagerepo now provides the small test-server implementation needed for path ACL lookups by reporting .slacl ancestors with its existing placeholder ACL name. Reviewed By: SBones Differential Revision: D104246343 fbshipit-source-id: bf5c89086504fd16314a1d1a257beef0b9c7c20a
1 parent 72a8d03 commit 9eb32d7

16 files changed

Lines changed: 402 additions & 14 deletions

File tree

‎eden/fs/store/BackingStore.h‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "eden/fs/model/ObjectId.h"
2222
#include "eden/fs/model/RootId.h"
2323
#include "eden/fs/model/TreeAuxDataFwd.h"
24+
#include "eden/fs/model/TreeEntry.h"
2425
#include "eden/fs/model/TreeFwd.h"
2526
#include "eden/fs/store/BackingStoreType.h"
2627
#include "eden/fs/store/ImportPriority.h"
@@ -342,6 +343,22 @@ class BackingStore : public RootIdCodec, public ObjectIdCodec {
342343
return true;
343344
}
344345

346+
/**
347+
* Fetch rich ACL information for the given repo-relative paths at the
348+
* specified checked-out root. Results are aligned with the input order.
349+
*/
350+
virtual folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
351+
co_getPathAcls(
352+
const RootId& rootId,
353+
const std::vector<std::string>& paths,
354+
const ObjectFetchContextPtr& context) {
355+
(void)rootId;
356+
(void)paths;
357+
(void)context;
358+
co_yield folly::coro::co_error(
359+
std::runtime_error("co_getPathAcls() not supported"));
360+
}
361+
345362
/**
346363
* Prefetch all the blobs represented by the HashRange.
347364
*

‎eden/fs/store/FilteredBackingStore.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,16 @@ ImmediateFuture<bool> FilteredBackingStore::checkPermission(
612612
FilteredObjectId::fromObjectId(manifestId).object());
613613
}
614614

615+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
616+
FilteredBackingStore::co_getPathAcls(
617+
const RootId& rootId,
618+
const std::vector<std::string>& paths,
619+
const ObjectFetchContextPtr& context) {
620+
auto [parsedRootId, _] = parseFilterIdFromRootId(rootId);
621+
co_return co_await backingStore_->co_getPathAcls(
622+
parsedRootId, paths, context);
623+
}
624+
615625
folly::SemiFuture<folly::Unit> FilteredBackingStore::prefetchBlobs(
616626
ObjectIdRange ids,
617627
const ObjectFetchContextPtr& context) {

‎eden/fs/store/FilteredBackingStore.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ class FilteredBackingStore
212212
ObjectIdRange ids,
213213
const ObjectFetchContextPtr& context) override;
214214

215+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
216+
co_getPathAcls(
217+
const RootId& rootId,
218+
const std::vector<std::string>& paths,
219+
const ObjectFetchContextPtr& context) override;
220+
215221
ImmediateFuture<GetGlobFilesResult> getGlobFiles(
216222
const RootId& id,
217223
const std::vector<std::string>& globs,

‎eden/fs/store/ObjectStore.cpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,4 +1117,12 @@ ImmediateFuture<bool> ObjectStore::checkPermissionIfExpired(
11171117
return backingStore_->checkPermission(manifestId);
11181118
}
11191119

1120+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
1121+
ObjectStore::co_getPathAcls(
1122+
const RootId& rootId,
1123+
const std::vector<std::string>& paths,
1124+
const ObjectFetchContextPtr& context) const {
1125+
co_return co_await backingStore_->co_getPathAcls(rootId, paths, context);
1126+
}
1127+
11201128
} // namespace facebook::eden

‎eden/fs/store/ObjectStore.h‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,12 @@ class ObjectStore : public IObjectStore,
407407
const ObjectId& manifestId,
408408
std::chrono::steady_clock::time_point lastCheck) const;
409409

410+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
411+
co_getPathAcls(
412+
const RootId& rootId,
413+
const std::vector<std::string>& paths,
414+
const ObjectFetchContextPtr& context) const;
415+
410416
/**
411417
* Get the BackingStore used by this ObjectStore
412418
*/

‎eden/fs/store/sl/BUCK‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ cpp_library(
4646
srcs = ["SaplingBackingStore.cpp"],
4747
headers = ["SaplingBackingStore.h"],
4848
deps = [
49+
"fbsource//third-party/fmt:fmt",
4950
"fbsource//third-party/re2:re2",
5051
":sapling_import_request",
5152
":sapling_object_id",

‎eden/fs/store/sl/SaplingBackingStore.cpp‎

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
#include <algorithm>
1111
#include <chrono>
1212
#include <stdexcept>
13+
#include <string>
1314
#include <thread>
1415
#include <utility>
1516
#include <variant>
1617

18+
#include <fmt/format.h>
1719
#include <re2/re2.h>
1820

1921
#include <folly/Executor.h>
@@ -280,7 +282,7 @@ void SaplingBackingStore::initializeOBCCounters() {
280282
getTreePerRepoLatencies_ = monitoring::OBCP99P95P50(
281283
monitoring::OdsCategoryId::ODS_EDEN,
282284
fmt::format("eden.store.sapling.fetch_tree_{}_us", repoName_),
283-
{hostname});
285+
{std::move(hostname)});
284286
isOBCEnabled_ = true;
285287
}
286288
#endif
@@ -2492,6 +2494,79 @@ ImmediateFuture<bool> SaplingBackingStore::checkPermission(
24922494
return result.has_access;
24932495
}
24942496

2497+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
2498+
SaplingBackingStore::co_getPathAcls(
2499+
const RootId& rootId,
2500+
const std::vector<std::string>& paths,
2501+
const ObjectFetchContextPtr& context) {
2502+
auto self = shared_from_this();
2503+
// TODO(T272514471): Plumb ObjectFetchContext through the Sapling FFI so path
2504+
// ACL lookups are attributed in fetch traces like other backing-store calls.
2505+
(void)context;
2506+
co_await self->faultInjector_.co_checkAsync(
2507+
"SaplingBackingStore::getPathAcls", rootId.value());
2508+
co_return co_await folly::coro::co_withExecutor(
2509+
self->serverThreadPool_,
2510+
folly::coro::co_invoke(
2511+
[self, rootId = rootId, paths = std::vector<std::string>(paths)]()
2512+
-> folly::coro::Task<
2513+
std::vector<folly::Try<std::vector<EntryAcl>>>> {
2514+
rust::Vec<rust::String> rustPaths;
2515+
rustPaths.reserve(paths.size());
2516+
std::copy(
2517+
paths.begin(), paths.end(), std::back_inserter(rustPaths));
2518+
2519+
auto result = sapling_backingstore_check_path_permissions(
2520+
*self->store_.get(),
2521+
rust::Str{rootId.value().data(), rootId.value().size()},
2522+
std::move(rustPaths));
2523+
if (result.error != nullptr) {
2524+
throw std::runtime_error(result.error->what());
2525+
}
2526+
2527+
std::vector<folly::Try<std::vector<EntryAcl>>> aclInfos;
2528+
aclInfos.reserve(paths.size());
2529+
if (result.data.size() != paths.size()) {
2530+
throw std::runtime_error(
2531+
fmt::format(
2532+
"path ACL lookup returned {} result(s) for {} path(s)",
2533+
result.data.size(),
2534+
paths.size()));
2535+
}
2536+
2537+
for (size_t index = 0; index < paths.size(); ++index) {
2538+
const auto& pathAclInfo = result.data[index];
2539+
std::string responsePath{pathAclInfo.path};
2540+
if (responsePath != paths[index]) {
2541+
throw std::runtime_error(
2542+
fmt::format(
2543+
"path ACL lookup returned response for {} at index {} instead of {}",
2544+
responsePath,
2545+
index,
2546+
paths[index]));
2547+
}
2548+
2549+
std::string error{pathAclInfo.error};
2550+
if (!error.empty()) {
2551+
aclInfos.emplace_back(std::runtime_error(error));
2552+
continue;
2553+
}
2554+
2555+
std::vector<EntryAcl> entries;
2556+
entries.reserve(pathAclInfo.entries.size());
2557+
for (const auto& entry : pathAclInfo.entries) {
2558+
entries.push_back(
2559+
EntryAcl{
2560+
std::string{entry.restriction_root},
2561+
std::string{entry.repo_region_acl},
2562+
std::string{entry.permission_request_group}});
2563+
}
2564+
aclInfos.emplace_back(std::move(entries));
2565+
}
2566+
co_return aclInfos;
2567+
}));
2568+
}
2569+
24952570
void SaplingBackingStore::logBackingStoreFetch(
24962571
const ObjectFetchContext& context,
24972572
folly::Range<SlOidView*> slOids,

‎eden/fs/store/sl/SaplingBackingStore.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,11 @@ class SaplingBackingStore final
672672
const std::vector<std::string>& prefixes) override;
673673

674674
ImmediateFuture<bool> checkPermission(const ObjectId& manifestId) override;
675+
folly::coro::now_task<std::vector<folly::Try<std::vector<EntryAcl>>>>
676+
co_getPathAcls(
677+
const RootId& rootId,
678+
const std::vector<std::string>& paths,
679+
const ObjectFetchContextPtr& context) override;
675680

676681
/**
677682
* The worker runloop function.

‎eden/scm/lib/backingstore/src/backingstore.rs‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use smallvec::SmallVec;
3939
use storemodel::BoxIterator;
4040
use storemodel::FileAuxData;
4141
use storemodel::FileStore;
42+
use storemodel::PathAclInfo;
4243
use storemodel::TreeAuxData;
4344
use storemodel::TreeEntry;
4445
use storemodel::TreeStore;
@@ -47,6 +48,7 @@ use types::FetchContext;
4748
use types::HgId;
4849
use types::Key;
4950
use types::RepoPath;
51+
use types::RepoPathBuf;
5052
use types::errors::KeyedError;
5153

5254
use crate::ffi::ffi::BackingStoreErrorKind;
@@ -537,6 +539,22 @@ impl BackingStore {
537539
Ok(true)
538540
}
539541

542+
#[instrument(level = "trace", skip(self, paths))]
543+
pub fn check_path_permissions(
544+
&self,
545+
hg_cs_id: &str,
546+
paths: Vec<String>,
547+
) -> Result<Vec<PathAclInfo>> {
548+
let hg_cs_id = HgId::from_hex(hg_cs_id.as_bytes())?;
549+
let path_bufs = paths
550+
.into_iter()
551+
.map(RepoPathBuf::from_string)
552+
.collect::<std::result::Result<Vec<_>, _>>()?;
553+
self.maybe_reload()
554+
.treestore
555+
.check_path_permissions(hg_cs_id, path_bufs)
556+
}
557+
540558
#[instrument(level = "trace", skip(self))]
541559
pub fn witness_file_read(&self, path: &RepoPath, local: bool, pid: u32) {
542560
let inner = self.inner.load();

‎eden/scm/lib/backingstore/src/ffi.rs‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,23 @@ pub(crate) mod ffi {
262262
error: UniquePtr<SaplingBackingStoreError>,
263263
}
264264

265+
pub struct PathAclEntry {
266+
restriction_root: String,
267+
repo_region_acl: String,
268+
permission_request_group: String,
269+
}
270+
271+
pub struct PathAclInfo {
272+
path: String,
273+
error: String,
274+
entries: Vec<PathAclEntry>,
275+
}
276+
277+
pub struct GetPathAclInfosResult {
278+
data: Vec<PathAclInfo>,
279+
error: UniquePtr<SaplingBackingStoreError>,
280+
}
281+
265282
extern "Rust" {
266283
type BackingStore;
267284

@@ -349,6 +366,12 @@ pub(crate) mod ffi {
349366
manifest_id: &[u8],
350367
) -> CheckPermissionResult;
351368

369+
pub fn sapling_backingstore_check_path_permissions(
370+
store: &BackingStore,
371+
hg_cs_id: &str,
372+
paths: Vec<String>,
373+
) -> GetPathAclInfosResult;
374+
352375
pub fn sapling_backingstore_witness_file_read(
353376
store: &BackingStore,
354377
path: &str,
@@ -880,6 +903,92 @@ pub fn sapling_backingstore_check_permission(
880903
}
881904
}
882905

906+
pub fn sapling_backingstore_check_path_permissions(
907+
store: &BackingStore,
908+
hg_cs_id: &str,
909+
paths: Vec<String>,
910+
) -> ffi::GetPathAclInfosResult {
911+
let requested_paths = paths.clone();
912+
match store.check_path_permissions(hg_cs_id, paths) {
913+
Ok(results) => {
914+
let mut results = results.into_iter();
915+
let mut data = Vec::with_capacity(requested_paths.len());
916+
for requested_path in requested_paths {
917+
let Some(result) = results.next() else {
918+
data.push(ffi::PathAclInfo {
919+
path: requested_path.clone(),
920+
error: format!("path ACL lookup returned no result for {requested_path}"),
921+
entries: Vec::new(),
922+
});
923+
continue;
924+
};
925+
926+
let storemodel::PathAclInfo {
927+
path,
928+
entries,
929+
error,
930+
} = result;
931+
let response_path = path.to_string();
932+
if response_path != requested_path {
933+
let error = format!(
934+
"path ACL lookup returned response for {response_path} instead of {requested_path}"
935+
);
936+
data.push(ffi::PathAclInfo {
937+
path: requested_path,
938+
error,
939+
entries: Vec::new(),
940+
});
941+
continue;
942+
}
943+
944+
let Some(error) = error else {
945+
let entries = entries
946+
.into_iter()
947+
.map(|entry| ffi::PathAclEntry {
948+
restriction_root: entry.restriction_root.to_string(),
949+
repo_region_acl: entry.repo_region_acl,
950+
permission_request_group: entry.permission_request_group,
951+
})
952+
.collect();
953+
data.push(ffi::PathAclInfo {
954+
path: requested_path,
955+
error: String::new(),
956+
entries,
957+
});
958+
continue;
959+
};
960+
961+
data.push(ffi::PathAclInfo {
962+
path: requested_path,
963+
error,
964+
entries: Vec::new(),
965+
});
966+
}
967+
968+
if let Some(extra) = results.next() {
969+
let extra_count = 1 + results.count();
970+
return ffi::GetPathAclInfosResult {
971+
data: Vec::new(),
972+
error: into_backingstore_err(anyhow!(
973+
"path ACL lookup returned {} extra result(s), starting with {}",
974+
extra_count,
975+
extra.path
976+
)),
977+
};
978+
}
979+
980+
ffi::GetPathAclInfosResult {
981+
data,
982+
error: UniquePtr::null(),
983+
}
984+
}
985+
Err(error) => ffi::GetPathAclInfosResult {
986+
data: Vec::new(),
987+
error: into_backingstore_err(error),
988+
},
989+
}
990+
}
991+
883992
pub fn sapling_backingstore_witness_file_read(
884993
store: &BackingStore,
885994
path: &str,

0 commit comments

Comments
 (0)