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;
123124namespace {
124125using namespace facebook ::eden;
125126
127+ constexpr auto kAclPathAttributes =
128+ ENTRY_ATTRIBUTE_UNDER_ACL | ENTRY_ATTRIBUTE_ACLs;
129+
126130std::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+
145291std::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