[deps] Set LPAC ACLs on windows for CfT binary

In some cases, windows bots don't have permission to run the CfT
binary due to missing ACLs.

Chromium tests usually set this ACL on the out/ directory when
building. For us it's enough to set up the ACL when we download the
CfT binary.

Manually verified on a Windows cloudtop that ACLs are correctly in
place after downloading a fresh binary.

R=mathias@chromium.org

Fixed: 324929074
Change-Id: Ieb5cb18ff883c1ee305be9738c725c3ebca00262
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5330806
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: Mathias Bynens <mathias@chromium.org>
This commit is contained in:
Simon Zünd
2024-02-28 09:07:17 +00:00
committed by Devtools-frontend LUCI CQ
parent dc6d30f9f6
commit 4df6525013
2 changed files with 102 additions and 0 deletions
+6
View File
@@ -18,6 +18,7 @@ import sys
import urllib.request
import zipfile
from set_lpac_acls import set_lpac_acls
def parse_options(cli_args):
parser = argparse.ArgumentParser(description='Download Chrome')
@@ -98,6 +99,11 @@ def download_and_extract(options):
with open(VERSION_NUMBER_FILE, 'w') as file:
file.write(options.version_number)
# On Windows we have to setup LPAC ACLs for the binary.
# See https://bit.ly/31yqMJR.
if os.name == 'nt':
set_lpac_acls(os.path.dirname(EXPECTED_BINARY))
if __name__ == '__main__':
OPTIONS = parse_options(sys.argv[1:])
+96
View File
@@ -0,0 +1,96 @@
# Copyright 2024 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# This file extracts the `set_lpac_acls` function from
# chromium/src/testing/scripts/common.py.
# Otherwise we'd have to pull in all of chromium/src/testing.
# We need to set the lpac ACLs on windows for the CfT binary.
# See https://bit.ly/31yqMJR.
import codecs
import contextlib
import logging
import os
import platform
import subprocess
import sys
import tempfile
logging.basicConfig(level=logging.INFO)
# ACL might be explicitly set or inherited.
CORRECT_ACL_VARIANTS = [
'APPLICATION PACKAGE AUTHORITY' \
'\\ALL RESTRICTED APPLICATION PACKAGES:(OI)(CI)(RX)', \
'APPLICATION PACKAGE AUTHORITY' \
'\\ALL RESTRICTED APPLICATION PACKAGES:(I)(OI)(CI)(RX)'
]
# pylint: disable=useless-object-inheritance
def set_lpac_acls(acl_dir, is_test_script=False):
"""Sets LPAC ACLs on a directory. Windows 10 only."""
if platform.release() != '10':
return
try:
existing_acls = subprocess.check_output(['icacls', acl_dir],
stderr=subprocess.STDOUT,
universal_newlines=True)
except subprocess.CalledProcessError as e:
logging.error('Failed to retrieve existing ACLs for directory %s',
acl_dir)
logging.error('Command output: %s', e.output)
sys.exit(e.returncode)
acls_correct = False
for acl in CORRECT_ACL_VARIANTS:
if acl in existing_acls:
acls_correct = True
if not acls_correct:
try:
existing_acls = subprocess.check_output(
['icacls', acl_dir, '/grant', '*S-1-15-2-2:(OI)(CI)(RX)'],
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
logging.error('Failed to retrieve existing ACLs for directory %s',
acl_dir)
logging.error('Command output: %s', e.output)
sys.exit(e.returncode)
if not is_test_script:
return
# Bots running on luci use hardlinks that do not have correct ACLs so these
# must be manually overridden here.
with temporary_file() as tempfile_path:
subprocess.check_output(
['icacls', acl_dir, '/save', tempfile_path, '/t', '/q', '/c'],
stderr=subprocess.STDOUT)
# ACL files look like this, e.g. for c:\a\b\c\d\Release_x64
#
# Release_x64
# D:AI(A;OICI;0x1200a9;;;S-1-15-2-2)(A;OICIID;FA;;;BA)
# Release_x64\icudtl_extra.dat
# D:AI(A;ID;0x1200a9;;;S-1-15-2-2)(A;ID;FA;;;BA)(A;ID;0x1301bf;;;BU)
with codecs.open(tempfile_path, encoding='utf_16_le') as aclfile:
for filename in aclfile:
acl = next(aclfile).strip()
full_filename = os.path.abspath(
os.path.join(acl_dir, os.pardir, filename.strip()))
if 'S-1-15-2-2' in acl:
continue
if os.path.isdir(full_filename):
continue
subprocess.check_output(
['icacls', full_filename, '/grant', '*S-1-15-2-2:(RX)'],
stderr=subprocess.STDOUT)
@contextlib.contextmanager
def temporary_file():
fd, path = tempfile.mkstemp()
os.close(fd)
try:
yield path
finally:
os.remove(path)