Skip to content

Commit fcbdc4a

Browse files
MichaelCuevasmeta-codesync[bot]
authored andcommitted
Populate ACL attributes in getAttributesFromFilesV2
Summary: Populate getAttributesFromFilesV2 ACL fields from existing local UNDER_ACL state and backing-store path ACL lookup results. When local state proves a path is not under an ACL, return an empty ACL list without hitting the backing store. Otherwise, use the path ACL lookup result to fill ACLs and derive AclInfo.underAcl from whether that ACL list is empty. Keep this diff focused on returning the thrift attributes; checkout-race hardening and broader restricted-tree behavior tests belong in later diffs. Reviewed By: muirdm Differential Revision: D104246341 fbshipit-source-id: 1590b5f29d660daa96b6eec81ba309d721ec5b93
1 parent 9eb32d7 commit fcbdc4a

2 files changed

Lines changed: 339 additions & 11 deletions

File tree

‎eden/fs/service/EdenServiceHandler.cpp‎

Lines changed: 183 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <algorithm>
1212
#include <optional>
1313
#include <stdexcept>
14+
#include <system_error>
1415
#include <typeinfo>
1516
#include <unordered_set>
1617

@@ -123,6 +124,9 @@ using namespace std::literals::string_view_literals;
123124
namespace {
124125
using namespace facebook::eden;
125126

127+
constexpr auto kAclPathAttributes =
128+
ENTRY_ATTRIBUTE_UNDER_ACL | ENTRY_ATTRIBUTE_ACLs;
129+
126130
std::string getClientCmdline(
127131
const std::shared_ptr<ServerState>& serverState_,
128132
const ObjectFetchContextPtr& context_) {
@@ -142,6 +146,148 @@ std::string getClientCmdline(
142146
return client_cmdline;
143147
}
144148

149+
bool isAclOnlyRequest(EntryAttributeFlags requestedAttributes) {
150+
return requestedAttributes.containsAnyOf(kAclPathAttributes) &&
151+
(requestedAttributes & kAclPathAttributes) == requestedAttributes;
152+
}
153+
154+
bool isPermissionDenied(const folly::exception_wrapper& exception) {
155+
bool permissionDenied = false;
156+
exception.with_exception([&](const std::system_error& error) {
157+
permissionDenied = error.code() == std::errc::permission_denied;
158+
});
159+
return permissionDenied;
160+
}
161+
162+
void setPathAclLookupError(
163+
EntryAttributes& attributes,
164+
EntryAttributeFlags requestedAttributes,
165+
const folly::exception_wrapper& exception) {
166+
if (requestedAttributes.contains(ENTRY_ATTRIBUTE_UNDER_ACL) &&
167+
(!attributes.underAcl.has_value() ||
168+
attributes.underAcl->hasException())) {
169+
attributes.underAcl = folly::Try<bool>{exception};
170+
}
171+
if (requestedAttributes.contains(ENTRY_ATTRIBUTE_ACLs)) {
172+
attributes.aclInfo = folly::Try<EntryAclInfo>{exception};
173+
}
174+
}
175+
176+
folly::coro::Task<std::vector<folly::Try<EntryAttributes>>>
177+
co_fetchPathAclLookupsForAttributes(
178+
std::shared_ptr<ObjectStore> objectStore,
179+
RootId checkedOutRootId,
180+
ObjectFetchContextPtr fetchContext,
181+
std::vector<folly::Try<EntryAttributes>> allRes,
182+
std::vector<std::string> paths,
183+
EntryAttributeFlags reqBitmask) {
184+
const auto aclOnlyRequest = isAclOnlyRequest(reqBitmask);
185+
std::vector<size_t> aclPathIndices;
186+
aclPathIndices.reserve(paths.size());
187+
188+
for (size_t i = 0; i < allRes.size(); ++i) {
189+
auto& tryAttributes = allRes[i];
190+
if (tryAttributes.hasException()) {
191+
if (aclOnlyRequest && isPermissionDenied(tryAttributes.exception())) {
192+
XLOGF(
193+
WARN,
194+
"ACL-only getAttributesFromFilesV2 request for {} hit permission denied while reading local metadata; falling back to path ACL lookup",
195+
paths.at(i));
196+
allRes[i] = folly::Try<EntryAttributes>{EntryAttributes{}};
197+
aclPathIndices.push_back(i);
198+
}
199+
continue;
200+
}
201+
202+
auto& attributes = tryAttributes.value();
203+
if (attributes.underAcl.has_value()) {
204+
if (attributes.underAcl->hasException()) {
205+
if (reqBitmask.contains(ENTRY_ATTRIBUTE_ACLs)) {
206+
attributes.aclInfo =
207+
folly::Try<EntryAclInfo>{attributes.underAcl->exception()};
208+
}
209+
continue;
210+
}
211+
if (!attributes.underAcl->value()) {
212+
if (reqBitmask.contains(ENTRY_ATTRIBUTE_ACLs)) {
213+
attributes.aclInfo =
214+
folly::Try<EntryAclInfo>{EntryAclInfo{/*underAcl=*/false, {}}};
215+
}
216+
continue;
217+
}
218+
219+
if (!reqBitmask.contains(ENTRY_ATTRIBUTE_ACLs)) {
220+
continue;
221+
}
222+
223+
aclPathIndices.push_back(i);
224+
continue;
225+
}
226+
227+
if (reqBitmask.containsAnyOf(kAclPathAttributes)) {
228+
aclPathIndices.push_back(i);
229+
}
230+
}
231+
232+
if (aclPathIndices.empty()) {
233+
co_return allRes;
234+
}
235+
236+
std::vector<std::string> lookupPaths;
237+
lookupPaths.reserve(aclPathIndices.size());
238+
for (auto index : aclPathIndices) {
239+
lookupPaths.push_back(paths.at(index));
240+
}
241+
242+
auto aclInfosTry = co_await co_awaitTry(
243+
objectStore->co_getPathAcls(checkedOutRootId, lookupPaths, fetchContext));
244+
if (aclInfosTry.hasException()) {
245+
XLOGF(
246+
WARN,
247+
"getAttributesFromFilesV2 path ACL lookup failed for {} path(s): {}",
248+
lookupPaths.size(),
249+
folly::exceptionStr(aclInfosTry.exception()));
250+
for (auto index : aclPathIndices) {
251+
if (allRes.at(index).hasValue()) {
252+
setPathAclLookupError(
253+
allRes.at(index).value(), reqBitmask, aclInfosTry.exception());
254+
}
255+
}
256+
co_return allRes;
257+
}
258+
259+
auto aclInfos = std::move(aclInfosTry).value();
260+
for (size_t offset = 0; offset < aclInfos.size(); ++offset) {
261+
auto index = aclPathIndices.at(offset);
262+
if (!allRes.at(index).hasValue()) {
263+
continue;
264+
}
265+
266+
auto& attributes = allRes.at(index).value();
267+
if (aclInfos.at(offset).hasException()) {
268+
XLOGF(
269+
WARN,
270+
"getAttributesFromFilesV2 path ACL lookup failed for {}: {}",
271+
paths.at(index),
272+
folly::exceptionStr(aclInfos.at(offset).exception()));
273+
setPathAclLookupError(
274+
attributes, reqBitmask, aclInfos.at(offset).exception());
275+
continue;
276+
}
277+
278+
auto acls = std::move(aclInfos.at(offset).value());
279+
auto underAcl = !acls.empty();
280+
if (reqBitmask.contains(ENTRY_ATTRIBUTE_UNDER_ACL)) {
281+
attributes.underAcl = folly::Try<bool>{underAcl};
282+
}
283+
if (reqBitmask.contains(ENTRY_ATTRIBUTE_ACLs)) {
284+
attributes.aclInfo =
285+
folly::Try<EntryAclInfo>{EntryAclInfo{underAcl, std::move(acls)}};
286+
}
287+
}
288+
co_return allRes;
289+
}
290+
145291
std::string logHash(StringPiece thriftArg) {
146292
if (thriftArg.size() == Hash20::RAW_SIZE) {
147293
return Hash20{folly::ByteRange{thriftArg}}.toString();
@@ -4305,22 +4451,53 @@ EdenServiceHandler::getEntryAttributes(
43054451
AttributesRequestScope reqScope,
43064452
SyncBehavior sync,
43074453
const ObjectFetchContextPtr& fetchContext) {
4308-
return waitForPendingWrites(edenMount, sync)
4454+
auto localReqBitmask = reqBitmask;
4455+
if (reqBitmask.contains(ENTRY_ATTRIBUTE_ACLs)) {
4456+
localReqBitmask |= ENTRY_ATTRIBUTE_UNDER_ACL;
4457+
}
4458+
auto waitFuture = waitForPendingWrites(edenMount, sync);
4459+
return std::move(waitFuture)
43094460
.thenValue([this,
43104461
&edenMount,
4311-
&paths,
4462+
paths = paths,
43124463
fetchContext = fetchContext.copy(),
43134464
reqBitmask,
4465+
localReqBitmask,
43144466
reqScope](auto&&) mutable {
43154467
vector<ImmediateFuture<EntryAttributes>> futures;
43164468
futures.reserve(paths.size());
43174469
for (const auto& path : paths) {
43184470
futures.emplace_back(getEntryAttributesForPath(
4319-
edenMount, reqBitmask, reqScope, path, fetchContext));
4471+
edenMount, localReqBitmask, reqScope, path, fetchContext));
43204472
}
43214473

4322-
// Collect all futures into a single tuple
4323-
return facebook::eden::collectAll(std::move(futures));
4474+
auto objectStore = edenMount.getObjectStore();
4475+
auto checkedOutRootId = edenMount.getCheckedOutRootId();
4476+
return facebook::eden::collectAll(std::move(futures))
4477+
.thenValue(
4478+
[objectStore = std::move(objectStore),
4479+
paths = std::move(paths),
4480+
fetchContext = fetchContext.copy(),
4481+
reqBitmask,
4482+
checkedOutRootId = std::move(checkedOutRootId)](
4483+
std::vector<folly::Try<EntryAttributes>> allRes) mutable
4484+
-> ImmediateFuture<
4485+
std::vector<folly::Try<EntryAttributes>>> {
4486+
if (!reqBitmask.containsAnyOf(kAclPathAttributes)) {
4487+
return allRes;
4488+
}
4489+
4490+
return ImmediateFuture<
4491+
std::vector<folly::Try<EntryAttributes>>>{
4492+
co_fetchPathAclLookupsForAttributes(
4493+
objectStore,
4494+
std::move(checkedOutRootId),
4495+
fetchContext.copy(),
4496+
std::move(allRes),
4497+
std::move(paths),
4498+
reqBitmask)
4499+
.semi()};
4500+
});
43244501
});
43254502
}
43264503

@@ -4396,7 +4573,6 @@ EdenServiceHandler::semifuture_getAttributesFromFilesV2(
43964573
toLogArg(paths));
43974574
auto& fetchContext = helper->getFetchContext();
43984575

4399-
auto config = server_->getServerState()->getEdenConfig();
44004576
auto entryAttributesFuture = getEntryAttributes(
44014577
mountHandle.getEdenMount(),
44024578
paths,
@@ -4424,11 +4600,7 @@ EdenServiceHandler::semifuture_getAttributesFromFilesV2(
44244600
}
44254601
return res;
44264602
}))
4427-
.ensure([mountHandle, params = std::move(params)]() {
4428-
// keeps the params memory around for the duration of the thrift call,
4429-
// so that we can safely use the paths by reference to avoid making
4430-
// copies.
4431-
})
4603+
.ensure([mountHandle, params = std::move(params)]() {})
44324604
.semi();
44334605
}
44344606

0 commit comments

Comments
 (0)