From f1892dfd4300e0da115a1bedfffa1373431ddbd4 Mon Sep 17 00:00:00 2001 From: Vinnie Magro Date: Thu, 12 Oct 2023 07:45:19 -0700 Subject: [PATCH] [antlir] fallback to copy if binary resource in XAR is not executable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Something must have changed about how XAR is packaged: S371398 Test Plan: Using new cm entrypoint from stacked diff ``` ❯ buck2 run 'fbcode//windtunnel/cogwheel/examples/example_multiplier:tw.cogwheel.examplemultiplier=publish' ``` Was printed a bunch of times ``` W 20231012 07:38:13.311 3800156 fs_utils.py:380 antlir.btrfsutil-bin is not executable ``` Reviewed By: sergeyfd Differential Revision: D50224934 fbshipit-source-id: 48a35cefa78eac8057c46333afa323eccbf60320 --- antlir/fs_utils.py | 63 +++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/antlir/fs_utils.py b/antlir/fs_utils.py index f76ebdf48e4..8b3b507dd66 100644 --- a/antlir/fs_utils.py +++ b/antlir/fs_utils.py @@ -371,37 +371,38 @@ def resource(cls, package, name: str, *, exe: bool) -> Iterator["Path"]: # Future: once the bug with the XAR `access` implementation # is fixed (https://fburl.com/42s41c0g), this can just check # for boolean equality. - if exe and not os.access(rsrc_in.name, os.X_OK): - raise RuntimeError( - f"{package}.{name} is not executable" - ) # pragma: no cover - yield Path(rsrc_in.name).abspath() - else: # pragma: no cover - # The resource has no path, so we have to materialize it. - # - # This code path is not reached by our coverage harness, - # since resources in '@mode/dev will always have a real - # filesystem path. However, we get all the needed signal - # from running `test-fs-utils-path-resource-*' in - # `@mode/dev` and `@mode/opt'. - # - # Wrap in a temporary directory so we can `chmod 755` below. - with temp_dir() as td: - with open(td / name, "wb") as rsrc_out: - # We can't use `os.sendfile` because `rsrc_in` may - # not be backed by a real FD. - while True: - # Read 512KiB chunks to mask the syscall cost - chunk = rsrc_in.read(2**19) - if not chunk: - break - rsrc_out.write(chunk) - if exe: - # The temporary directory protects us from undesired - # access. The goal is to let both the current user - # and `root` execute this path, but nobody else. - os.chmod(td / name, 0o755) - yield td / name + if exe and os.access(rsrc_in.name, os.X_OK): + yield Path(rsrc_in.name).abspath() + return + else: + # why does this happen? who knows but we can make a copy of + # the binary that _is_ executable + log.warning(f"{package}.{name} is not executable") + # The resource has no path, so we have to materialize it. + # + # This code path is not reached by our coverage harness, + # since resources in '@mode/dev will always have a real + # filesystem path. However, we get all the needed signal + # from running `test-fs-utils-path-resource-*' in + # `@mode/dev` and `@mode/opt'. + # + # Wrap in a temporary directory so we can `chmod 755` below. + with temp_dir() as td: + with open(td / name, "wb") as rsrc_out: + # We can't use `os.sendfile` because `rsrc_in` may + # not be backed by a real FD. + while True: + # Read 512KiB chunks to mask the syscall cost + chunk = rsrc_in.read(2**19) + if not chunk: + break + rsrc_out.write(chunk) + if exe: + # The temporary directory protects us from undesired + # access. The goal is to let both the current user + # and `root` execute this path, but nobody else. + os.chmod(td / name, 0o755) + yield td / name # Future: Consider if we actually want something like # `relative_outside_base`, which is `.normpath().has_leading_dot_dot()`.