xref: /aosp_15_r20/art/dexopt_chroot_setup/dexopt_chroot_setup.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2024 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker #include "dexopt_chroot_setup.h"
18*795d594fSAndroid Build Coastguard Worker 
19*795d594fSAndroid Build Coastguard Worker #include <linux/mount.h>
20*795d594fSAndroid Build Coastguard Worker #include <sched.h>
21*795d594fSAndroid Build Coastguard Worker #include <sys/mount.h>
22*795d594fSAndroid Build Coastguard Worker #include <sys/stat.h>
23*795d594fSAndroid Build Coastguard Worker #include <sys/types.h>
24*795d594fSAndroid Build Coastguard Worker 
25*795d594fSAndroid Build Coastguard Worker #include <algorithm>
26*795d594fSAndroid Build Coastguard Worker #include <cerrno>
27*795d594fSAndroid Build Coastguard Worker #include <chrono>
28*795d594fSAndroid Build Coastguard Worker #include <cstring>
29*795d594fSAndroid Build Coastguard Worker #include <filesystem>
30*795d594fSAndroid Build Coastguard Worker #include <iterator>
31*795d594fSAndroid Build Coastguard Worker #include <mutex>
32*795d594fSAndroid Build Coastguard Worker #include <optional>
33*795d594fSAndroid Build Coastguard Worker #include <regex>
34*795d594fSAndroid Build Coastguard Worker #include <string>
35*795d594fSAndroid Build Coastguard Worker #include <string_view>
36*795d594fSAndroid Build Coastguard Worker #include <system_error>
37*795d594fSAndroid Build Coastguard Worker #include <tuple>
38*795d594fSAndroid Build Coastguard Worker #include <vector>
39*795d594fSAndroid Build Coastguard Worker 
40*795d594fSAndroid Build Coastguard Worker #include "aidl/com/android/server/art/BnDexoptChrootSetup.h"
41*795d594fSAndroid Build Coastguard Worker #include "android-base/errors.h"
42*795d594fSAndroid Build Coastguard Worker #include "android-base/file.h"
43*795d594fSAndroid Build Coastguard Worker #include "android-base/logging.h"
44*795d594fSAndroid Build Coastguard Worker #include "android-base/no_destructor.h"
45*795d594fSAndroid Build Coastguard Worker #include "android-base/properties.h"
46*795d594fSAndroid Build Coastguard Worker #include "android-base/result.h"
47*795d594fSAndroid Build Coastguard Worker #include "android-base/scopeguard.h"
48*795d594fSAndroid Build Coastguard Worker #include "android-base/strings.h"
49*795d594fSAndroid Build Coastguard Worker #include "android-base/unique_fd.h"
50*795d594fSAndroid Build Coastguard Worker #include "android/binder_auto_utils.h"
51*795d594fSAndroid Build Coastguard Worker #include "android/binder_manager.h"
52*795d594fSAndroid Build Coastguard Worker #include "android/binder_process.h"
53*795d594fSAndroid Build Coastguard Worker #include "base/file_utils.h"
54*795d594fSAndroid Build Coastguard Worker #include "base/macros.h"
55*795d594fSAndroid Build Coastguard Worker #include "base/os.h"
56*795d594fSAndroid Build Coastguard Worker #include "base/stl_util.h"
57*795d594fSAndroid Build Coastguard Worker #include "base/utils.h"
58*795d594fSAndroid Build Coastguard Worker #include "exec_utils.h"
59*795d594fSAndroid Build Coastguard Worker #include "fstab/fstab.h"
60*795d594fSAndroid Build Coastguard Worker #include "tools/binder_utils.h"
61*795d594fSAndroid Build Coastguard Worker #include "tools/cmdline_builder.h"
62*795d594fSAndroid Build Coastguard Worker #include "tools/tools.h"
63*795d594fSAndroid Build Coastguard Worker 
64*795d594fSAndroid Build Coastguard Worker namespace art {
65*795d594fSAndroid Build Coastguard Worker namespace dexopt_chroot_setup {
66*795d594fSAndroid Build Coastguard Worker 
67*795d594fSAndroid Build Coastguard Worker namespace {
68*795d594fSAndroid Build Coastguard Worker 
69*795d594fSAndroid Build Coastguard Worker using ::android::base::ConsumePrefix;
70*795d594fSAndroid Build Coastguard Worker using ::android::base::Error;
71*795d594fSAndroid Build Coastguard Worker using ::android::base::GetProperty;
72*795d594fSAndroid Build Coastguard Worker using ::android::base::Join;
73*795d594fSAndroid Build Coastguard Worker using ::android::base::make_scope_guard;
74*795d594fSAndroid Build Coastguard Worker using ::android::base::NoDestructor;
75*795d594fSAndroid Build Coastguard Worker using ::android::base::ReadFileToString;
76*795d594fSAndroid Build Coastguard Worker using ::android::base::Readlink;
77*795d594fSAndroid Build Coastguard Worker using ::android::base::Result;
78*795d594fSAndroid Build Coastguard Worker using ::android::base::SetProperty;
79*795d594fSAndroid Build Coastguard Worker using ::android::base::Split;
80*795d594fSAndroid Build Coastguard Worker using ::android::base::Tokenize;
81*795d594fSAndroid Build Coastguard Worker using ::android::base::unique_fd;
82*795d594fSAndroid Build Coastguard Worker using ::android::base::WaitForProperty;
83*795d594fSAndroid Build Coastguard Worker using ::android::base::WriteStringToFile;
84*795d594fSAndroid Build Coastguard Worker using ::android::fs_mgr::FstabEntry;
85*795d594fSAndroid Build Coastguard Worker using ::art::tools::CmdlineBuilder;
86*795d594fSAndroid Build Coastguard Worker using ::art::tools::Fatal;
87*795d594fSAndroid Build Coastguard Worker using ::art::tools::GetProcMountsDescendantsOfPath;
88*795d594fSAndroid Build Coastguard Worker using ::art::tools::NonFatal;
89*795d594fSAndroid Build Coastguard Worker using ::art::tools::PathStartsWith;
90*795d594fSAndroid Build Coastguard Worker using ::ndk::ScopedAStatus;
91*795d594fSAndroid Build Coastguard Worker 
92*795d594fSAndroid Build Coastguard Worker constexpr const char* kServiceName = "dexopt_chroot_setup";
93*795d594fSAndroid Build Coastguard Worker const NoDestructor<std::string> kBindMountTmpDir(
94*795d594fSAndroid Build Coastguard Worker     std::string(DexoptChrootSetup::PRE_REBOOT_DEXOPT_DIR) + "/mount_tmp");
95*795d594fSAndroid Build Coastguard Worker const NoDestructor<std::string> kOtaSlotFile(std::string(DexoptChrootSetup::PRE_REBOOT_DEXOPT_DIR) +
96*795d594fSAndroid Build Coastguard Worker                                              "/ota_slot");
97*795d594fSAndroid Build Coastguard Worker const NoDestructor<std::string> kSnapshotMappedFile(
98*795d594fSAndroid Build Coastguard Worker     std::string(DexoptChrootSetup::PRE_REBOOT_DEXOPT_DIR) + "/snapshot_mapped");
99*795d594fSAndroid Build Coastguard Worker constexpr mode_t kChrootDefaultMode = 0755;
100*795d594fSAndroid Build Coastguard Worker constexpr std::chrono::milliseconds kSnapshotCtlTimeout = std::chrono::seconds(60);
101*795d594fSAndroid Build Coastguard Worker constexpr std::array<const char*, 4> kExternalLibDirs = {
102*795d594fSAndroid Build Coastguard Worker     "/system/lib", "/system/lib64", "/system_ext/lib", "/system_ext/lib64"};
103*795d594fSAndroid Build Coastguard Worker 
IsOtaUpdate(const std::optional<std::string> & ota_slot)104*795d594fSAndroid Build Coastguard Worker bool IsOtaUpdate(const std::optional<std::string>& ota_slot) { return ota_slot.has_value(); }
105*795d594fSAndroid Build Coastguard Worker 
Run(std::string_view log_name,const std::vector<std::string> & args)106*795d594fSAndroid Build Coastguard Worker Result<void> Run(std::string_view log_name, const std::vector<std::string>& args) {
107*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << "Running " << log_name << ": " << Join(args, /*separator=*/" ");
108*795d594fSAndroid Build Coastguard Worker 
109*795d594fSAndroid Build Coastguard Worker   std::string error_msg;
110*795d594fSAndroid Build Coastguard Worker   if (!Exec(args, &error_msg)) {
111*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to run {}: {}", log_name, error_msg);
112*795d594fSAndroid Build Coastguard Worker   }
113*795d594fSAndroid Build Coastguard Worker 
114*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << log_name << " returned code 0";
115*795d594fSAndroid Build Coastguard Worker   return {};
116*795d594fSAndroid Build Coastguard Worker }
117*795d594fSAndroid Build Coastguard Worker 
GetArtExecCmdlineBuilder()118*795d594fSAndroid Build Coastguard Worker Result<CmdlineBuilder> GetArtExecCmdlineBuilder() {
119*795d594fSAndroid Build Coastguard Worker   std::string error_msg;
120*795d594fSAndroid Build Coastguard Worker   std::string art_root = GetArtRootSafe(&error_msg);
121*795d594fSAndroid Build Coastguard Worker   if (!error_msg.empty()) {
122*795d594fSAndroid Build Coastguard Worker     return Error() << error_msg;
123*795d594fSAndroid Build Coastguard Worker   }
124*795d594fSAndroid Build Coastguard Worker   CmdlineBuilder args;
125*795d594fSAndroid Build Coastguard Worker   args.Add(art_root + "/bin/art_exec")
126*795d594fSAndroid Build Coastguard Worker       .Add("--chroot=%s", DexoptChrootSetup::CHROOT_DIR)
127*795d594fSAndroid Build Coastguard Worker       .Add("--process-name-suffix=Pre-reboot Dexopt chroot");
128*795d594fSAndroid Build Coastguard Worker   return args;
129*795d594fSAndroid Build Coastguard Worker }
130*795d594fSAndroid Build Coastguard Worker 
CreateDir(const std::string & path)131*795d594fSAndroid Build Coastguard Worker Result<void> CreateDir(const std::string& path) {
132*795d594fSAndroid Build Coastguard Worker   std::error_code ec;
133*795d594fSAndroid Build Coastguard Worker   std::filesystem::create_directory(path, ec);
134*795d594fSAndroid Build Coastguard Worker   if (ec) {
135*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to create dir '{}': {}", path, ec.message());
136*795d594fSAndroid Build Coastguard Worker   }
137*795d594fSAndroid Build Coastguard Worker   return {};
138*795d594fSAndroid Build Coastguard Worker }
139*795d594fSAndroid Build Coastguard Worker 
IsSymlink(const std::string & path)140*795d594fSAndroid Build Coastguard Worker Result<bool> IsSymlink(const std::string& path) {
141*795d594fSAndroid Build Coastguard Worker   std::error_code ec;
142*795d594fSAndroid Build Coastguard Worker   bool res = std::filesystem::is_symlink(path, ec);
143*795d594fSAndroid Build Coastguard Worker   if (ec) {
144*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to create dir '{}': {}", path, ec.message());
145*795d594fSAndroid Build Coastguard Worker   }
146*795d594fSAndroid Build Coastguard Worker   return res;
147*795d594fSAndroid Build Coastguard Worker }
148*795d594fSAndroid Build Coastguard Worker 
IsSelfOrParentSymlink(const std::string & path)149*795d594fSAndroid Build Coastguard Worker Result<bool> IsSelfOrParentSymlink(const std::string& path) {
150*795d594fSAndroid Build Coastguard Worker   // We don't use `Realpath` because it does a `stat(2)` call which requires the SELinux "getattr"
151*795d594fSAndroid Build Coastguard Worker   // permission. which we don't have on all mount points.
152*795d594fSAndroid Build Coastguard Worker   unique_fd fd(open(path.c_str(), O_PATH | O_CLOEXEC));
153*795d594fSAndroid Build Coastguard Worker   if (fd.get() < 0) {
154*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to open '{}' to resolve real path", path);
155*795d594fSAndroid Build Coastguard Worker   }
156*795d594fSAndroid Build Coastguard Worker   std::string real_path;
157*795d594fSAndroid Build Coastguard Worker   if (!Readlink(ART_FORMAT("/proc/self/fd/{}", fd.get()), &real_path)) {
158*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to resolve real path for '{}'", path);
159*795d594fSAndroid Build Coastguard Worker   }
160*795d594fSAndroid Build Coastguard Worker   return path != real_path;
161*795d594fSAndroid Build Coastguard Worker }
162*795d594fSAndroid Build Coastguard Worker 
Unmount(const std::string & target,bool logging=true)163*795d594fSAndroid Build Coastguard Worker Result<void> Unmount(const std::string& target, bool logging = true) {
164*795d594fSAndroid Build Coastguard Worker   if (umount2(target.c_str(), UMOUNT_NOFOLLOW) == 0) {
165*795d594fSAndroid Build Coastguard Worker     LOG_IF(INFO, logging) << ART_FORMAT("Unmounted '{}'", target);
166*795d594fSAndroid Build Coastguard Worker     return {};
167*795d594fSAndroid Build Coastguard Worker   }
168*795d594fSAndroid Build Coastguard Worker   LOG(WARNING) << ART_FORMAT(
169*795d594fSAndroid Build Coastguard Worker       "Failed to umount2 '{}': {}. Retrying with MNT_DETACH", target, strerror(errno));
170*795d594fSAndroid Build Coastguard Worker   if (umount2(target.c_str(), UMOUNT_NOFOLLOW | MNT_DETACH) == 0) {
171*795d594fSAndroid Build Coastguard Worker     LOG_IF(INFO, logging) << ART_FORMAT("Unmounted '{}' with MNT_DETACH", target);
172*795d594fSAndroid Build Coastguard Worker     return {};
173*795d594fSAndroid Build Coastguard Worker   }
174*795d594fSAndroid Build Coastguard Worker   return ErrnoErrorf("Failed to umount2 '{}'", target);
175*795d594fSAndroid Build Coastguard Worker }
176*795d594fSAndroid Build Coastguard Worker 
177*795d594fSAndroid Build Coastguard Worker // Bind-mounts `source` at `target` with the mount propagation type being "shared". You generally
178*795d594fSAndroid Build Coastguard Worker // want to use `BindMount` instead.
179*795d594fSAndroid Build Coastguard Worker //
180*795d594fSAndroid Build Coastguard Worker // `BindMountDirect` is safe to use only if there is no child mount points under `target`. DO NOT
181*795d594fSAndroid Build Coastguard Worker // mount or unmount under `target` because mount events propagate to `source`.
BindMountDirect(const std::string & source,const std::string & target)182*795d594fSAndroid Build Coastguard Worker Result<void> BindMountDirect(const std::string& source, const std::string& target) {
183*795d594fSAndroid Build Coastguard Worker   // Don't follow symlinks.
184*795d594fSAndroid Build Coastguard Worker   CHECK(!OR_RETURN(IsSelfOrParentSymlink(target))) << target;
185*795d594fSAndroid Build Coastguard Worker   if (mount(source.c_str(),
186*795d594fSAndroid Build Coastguard Worker             target.c_str(),
187*795d594fSAndroid Build Coastguard Worker             /*fs_type=*/nullptr,
188*795d594fSAndroid Build Coastguard Worker             MS_BIND,
189*795d594fSAndroid Build Coastguard Worker             /*data=*/nullptr) != 0) {
190*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to bind-mount '{}' at '{}'", source, target);
191*795d594fSAndroid Build Coastguard Worker   }
192*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << ART_FORMAT("Bind-mounted '{}' at '{}'", source, target);
193*795d594fSAndroid Build Coastguard Worker   return {};
194*795d594fSAndroid Build Coastguard Worker }
195*795d594fSAndroid Build Coastguard Worker 
196*795d594fSAndroid Build Coastguard Worker // Bind-mounts `source` at `target` with the mount propagation type being "slave+shared".
BindMount(const std::string & source,const std::string & target)197*795d594fSAndroid Build Coastguard Worker Result<void> BindMount(const std::string& source, const std::string& target) {
198*795d594fSAndroid Build Coastguard Worker   // Don't bind-mount repeatedly.
199*795d594fSAndroid Build Coastguard Worker   CHECK(!PathStartsWith(source, DexoptChrootSetup::CHROOT_DIR));
200*795d594fSAndroid Build Coastguard Worker   // Don't follow symlinks.
201*795d594fSAndroid Build Coastguard Worker   CHECK(!OR_RETURN(IsSelfOrParentSymlink(target))) << target;
202*795d594fSAndroid Build Coastguard Worker   // system_server has a different mount namespace from init, and it uses slave mounts. E.g:
203*795d594fSAndroid Build Coastguard Worker   //
204*795d594fSAndroid Build Coastguard Worker   //    a: init mount ns: shared(1):          /foo
205*795d594fSAndroid Build Coastguard Worker   //    b: init mount ns: shared(2):          /mnt
206*795d594fSAndroid Build Coastguard Worker   //    c: SS mount ns:   slave(1):           /foo
207*795d594fSAndroid Build Coastguard Worker   //    d: SS mount ns:   slave(2):           /mnt
208*795d594fSAndroid Build Coastguard Worker   //
209*795d594fSAndroid Build Coastguard Worker   // We create our chroot setup in the init namespace but also want it to appear inside the
210*795d594fSAndroid Build Coastguard Worker   // system_server one, since we need to access some files in it from system_server (in particular
211*795d594fSAndroid Build Coastguard Worker   // service-art.jar).
212*795d594fSAndroid Build Coastguard Worker   //
213*795d594fSAndroid Build Coastguard Worker   // Hence we want the mount propagation type to be "slave+shared": Slave of the init namespace so
214*795d594fSAndroid Build Coastguard Worker   // that unmounts in the chroot doesn't affect the rest of the system, while at the same time
215*795d594fSAndroid Build Coastguard Worker   // shared with the system_server namespace so that it gets the same mounts recursively in the
216*795d594fSAndroid Build Coastguard Worker   // chroot tree. This can be achieved in 4 steps:
217*795d594fSAndroid Build Coastguard Worker   //
218*795d594fSAndroid Build Coastguard Worker   // 1. Bind-mount /foo at a temp mount point /mnt/pre_reboot_dexopt/mount_tmp.
219*795d594fSAndroid Build Coastguard Worker   //    a: init mount ns: shared(1):          /foo
220*795d594fSAndroid Build Coastguard Worker   //    b: init mount ns: shared(2):          /mnt
221*795d594fSAndroid Build Coastguard Worker   //    e: init mount ns: shared(1):          /mnt/pre_reboot_dexopt/mount_tmp
222*795d594fSAndroid Build Coastguard Worker   //    c: SS mount ns:   slave(1):           /foo
223*795d594fSAndroid Build Coastguard Worker   //    d: SS mount ns:   slave(2):           /mnt
224*795d594fSAndroid Build Coastguard Worker   //    f: SS mount ns:   slave(1):           /mnt/pre_reboot_dexopt/mount_tmp
225*795d594fSAndroid Build Coastguard Worker   //
226*795d594fSAndroid Build Coastguard Worker   // 2. Make the temp mount point slave.
227*795d594fSAndroid Build Coastguard Worker   //    a: init mount ns: shared(1):          /foo
228*795d594fSAndroid Build Coastguard Worker   //    b: init mount ns: shared(2):          /mnt
229*795d594fSAndroid Build Coastguard Worker   //    e: init mount ns: slave(1):           /mnt/pre_reboot_dexopt/mount_tmp
230*795d594fSAndroid Build Coastguard Worker   //    c: SS mount ns:   slave(1):           /foo
231*795d594fSAndroid Build Coastguard Worker   //    d: SS mount ns:   slave(2):           /mnt
232*795d594fSAndroid Build Coastguard Worker   //    f: SS mount ns:   slave(1):           /mnt/pre_reboot_dexopt/mount_tmp
233*795d594fSAndroid Build Coastguard Worker   //
234*795d594fSAndroid Build Coastguard Worker   // 3. Bind-mount the temp mount point at /mnt/pre_reboot_dexopt/chroot/foo. (The new mount point
235*795d594fSAndroid Build Coastguard Worker   //    gets "slave+shared". It gets "slave" because the source (`e`) is "slave", and it gets
236*795d594fSAndroid Build Coastguard Worker   //    "shared" because the dest (`b`) is "shared".)
237*795d594fSAndroid Build Coastguard Worker   //    a: init mount ns: shared(1):          /foo
238*795d594fSAndroid Build Coastguard Worker   //    b: init mount ns: shared(2):          /mnt
239*795d594fSAndroid Build Coastguard Worker   //    e: init mount ns: slave(1):           /mnt/pre_reboot_dexopt/mount_tmp
240*795d594fSAndroid Build Coastguard Worker   //    g: init mount ns: slave(1),shared(3): /mnt/pre_reboot_dexopt/chroot/foo
241*795d594fSAndroid Build Coastguard Worker   //    b: SS mount ns:   slave(1):           /foo
242*795d594fSAndroid Build Coastguard Worker   //    d: SS mount ns:   slave(2):           /mnt
243*795d594fSAndroid Build Coastguard Worker   //    f: SS mount ns:   slave(1):           /mnt/pre_reboot_dexopt/mount_tmp
244*795d594fSAndroid Build Coastguard Worker   //    h: SS mount ns:   slave(3):           /mnt/pre_reboot_dexopt/chroot/foo
245*795d594fSAndroid Build Coastguard Worker   //
246*795d594fSAndroid Build Coastguard Worker   // 4. Unmount the temp mount point.
247*795d594fSAndroid Build Coastguard Worker   //    a: init mount ns: shared(1):          /foo
248*795d594fSAndroid Build Coastguard Worker   //    b: init mount ns: shared(2):          /mnt
249*795d594fSAndroid Build Coastguard Worker   //    g: init mount ns: slave(1),shared(3): /mnt/pre_reboot_dexopt/chroot/foo
250*795d594fSAndroid Build Coastguard Worker   //    b: SS mount ns:   slave(1):           /foo
251*795d594fSAndroid Build Coastguard Worker   //    d: SS mount ns:   slave(2):           /mnt
252*795d594fSAndroid Build Coastguard Worker   //    h: SS mount ns:   slave(3):           /mnt/pre_reboot_dexopt/chroot/foo
253*795d594fSAndroid Build Coastguard Worker   //
254*795d594fSAndroid Build Coastguard Worker   // At this point, we have achieved what we want. `g` is a slave of `a` so that unmounts in `g`
255*795d594fSAndroid Build Coastguard Worker   // doesn't affect `a`, and `g` is shared with `h` so that mounts in `g` are propagated to `h`.
256*795d594fSAndroid Build Coastguard Worker   OR_RETURN(CreateDir(*kBindMountTmpDir));
257*795d594fSAndroid Build Coastguard Worker   if (mount(source.c_str(),
258*795d594fSAndroid Build Coastguard Worker             kBindMountTmpDir->c_str(),
259*795d594fSAndroid Build Coastguard Worker             /*fs_type=*/nullptr,
260*795d594fSAndroid Build Coastguard Worker             MS_BIND,
261*795d594fSAndroid Build Coastguard Worker             /*data=*/nullptr) != 0) {
262*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to bind-mount '{}' at '{}' ('{}' -> '{}')",
263*795d594fSAndroid Build Coastguard Worker                        source,
264*795d594fSAndroid Build Coastguard Worker                        *kBindMountTmpDir,
265*795d594fSAndroid Build Coastguard Worker                        source,
266*795d594fSAndroid Build Coastguard Worker                        target);
267*795d594fSAndroid Build Coastguard Worker   }
268*795d594fSAndroid Build Coastguard Worker   auto cleanup = make_scope_guard([&]() {
269*795d594fSAndroid Build Coastguard Worker     Result<void> result = Unmount(*kBindMountTmpDir, /*logging=*/false);
270*795d594fSAndroid Build Coastguard Worker     if (!result.ok()) {
271*795d594fSAndroid Build Coastguard Worker       LOG(ERROR) << result.error().message();
272*795d594fSAndroid Build Coastguard Worker     }
273*795d594fSAndroid Build Coastguard Worker   });
274*795d594fSAndroid Build Coastguard Worker   if (mount(/*source=*/nullptr,
275*795d594fSAndroid Build Coastguard Worker             kBindMountTmpDir->c_str(),
276*795d594fSAndroid Build Coastguard Worker             /*fs_type=*/nullptr,
277*795d594fSAndroid Build Coastguard Worker             MS_SLAVE,
278*795d594fSAndroid Build Coastguard Worker             /*data=*/nullptr) != 0) {
279*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf(
280*795d594fSAndroid Build Coastguard Worker         "Failed to make mount slave for '{}' ('{}' -> '{}')", *kBindMountTmpDir, source, target);
281*795d594fSAndroid Build Coastguard Worker   }
282*795d594fSAndroid Build Coastguard Worker   if (mount(kBindMountTmpDir->c_str(),
283*795d594fSAndroid Build Coastguard Worker             target.c_str(),
284*795d594fSAndroid Build Coastguard Worker             /*fs_type=*/nullptr,
285*795d594fSAndroid Build Coastguard Worker             MS_BIND,
286*795d594fSAndroid Build Coastguard Worker             /*data=*/nullptr) != 0) {
287*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to bind-mount '{}' at '{}' ('{}' -> '{}')",
288*795d594fSAndroid Build Coastguard Worker                        *kBindMountTmpDir,
289*795d594fSAndroid Build Coastguard Worker                        target,
290*795d594fSAndroid Build Coastguard Worker                        source,
291*795d594fSAndroid Build Coastguard Worker                        target);
292*795d594fSAndroid Build Coastguard Worker   }
293*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << ART_FORMAT("Bind-mounted '{}' at '{}'", source, target);
294*795d594fSAndroid Build Coastguard Worker   return {};
295*795d594fSAndroid Build Coastguard Worker }
296*795d594fSAndroid Build Coastguard Worker 
BindMountRecursive(const std::string & source,const std::string & target)297*795d594fSAndroid Build Coastguard Worker Result<void> BindMountRecursive(const std::string& source, const std::string& target) {
298*795d594fSAndroid Build Coastguard Worker   CHECK(!source.ends_with('/'));
299*795d594fSAndroid Build Coastguard Worker   OR_RETURN(BindMount(source, target));
300*795d594fSAndroid Build Coastguard Worker 
301*795d594fSAndroid Build Coastguard Worker   // Mount and make slave one by one. Do not use MS_REC because we don't want to mount a child if
302*795d594fSAndroid Build Coastguard Worker   // the parent cannot be slave (i.e., is shared). Otherwise, unmount events will be undesirably
303*795d594fSAndroid Build Coastguard Worker   // propagated to the source. For example, if "/dev" and "/dev/pts" are mounted at "/chroot/dev"
304*795d594fSAndroid Build Coastguard Worker   // and "/chroot/dev/pts" respectively, and "/chroot/dev" is shared, then unmounting
305*795d594fSAndroid Build Coastguard Worker   // "/chroot/dev/pts" will also unmount "/dev/pts".
306*795d594fSAndroid Build Coastguard Worker   //
307*795d594fSAndroid Build Coastguard Worker   // The list is in mount order.
308*795d594fSAndroid Build Coastguard Worker   std::vector<FstabEntry> entries = OR_RETURN(GetProcMountsDescendantsOfPath(source));
309*795d594fSAndroid Build Coastguard Worker   for (const FstabEntry& entry : entries) {
310*795d594fSAndroid Build Coastguard Worker     CHECK(!entry.mount_point.ends_with('/'));
311*795d594fSAndroid Build Coastguard Worker     std::string_view sub_dir = entry.mount_point;
312*795d594fSAndroid Build Coastguard Worker     CHECK(ConsumePrefix(&sub_dir, source));
313*795d594fSAndroid Build Coastguard Worker     if (sub_dir.empty()) {
314*795d594fSAndroid Build Coastguard Worker       // `source` itself. Already mounted.
315*795d594fSAndroid Build Coastguard Worker       continue;
316*795d594fSAndroid Build Coastguard Worker     }
317*795d594fSAndroid Build Coastguard Worker     if (Result<void> result = BindMount(entry.mount_point, std::string(target).append(sub_dir));
318*795d594fSAndroid Build Coastguard Worker         !result.ok()) {
319*795d594fSAndroid Build Coastguard Worker       // Match paths for the "u:object_r:apk_tmp_file:s0" file context in
320*795d594fSAndroid Build Coastguard Worker       // system/sepolicy/private/file_contexts.
321*795d594fSAndroid Build Coastguard Worker       std::regex apk_tmp_file_re(R"re((/data|/mnt/expand/[^/]+)/app/vmdl[^/]+\.tmp(/.*)?)re");
322*795d594fSAndroid Build Coastguard Worker       std::smatch match;
323*795d594fSAndroid Build Coastguard Worker       if (std::regex_match(entry.mount_point, match, apk_tmp_file_re)) {
324*795d594fSAndroid Build Coastguard Worker         // Don't bother. The mount point is a temporary directory created by Package Manager during
325*795d594fSAndroid Build Coastguard Worker         // app install. We won't be able to dexopt the app there anyway because it's not in the
326*795d594fSAndroid Build Coastguard Worker         // Package Manager's snapshot.
327*795d594fSAndroid Build Coastguard Worker         LOG(INFO) << ART_FORMAT("Skipped temporary mount point '{}'", entry.mount_point);
328*795d594fSAndroid Build Coastguard Worker         continue;
329*795d594fSAndroid Build Coastguard Worker       }
330*795d594fSAndroid Build Coastguard Worker       return result;
331*795d594fSAndroid Build Coastguard Worker     }
332*795d594fSAndroid Build Coastguard Worker   }
333*795d594fSAndroid Build Coastguard Worker   return {};
334*795d594fSAndroid Build Coastguard Worker }
335*795d594fSAndroid Build Coastguard Worker 
GetBlockDeviceName(const std::string & partition,const std::string & slot)336*795d594fSAndroid Build Coastguard Worker std::string GetBlockDeviceName(const std::string& partition, const std::string& slot) {
337*795d594fSAndroid Build Coastguard Worker   return ART_FORMAT("/dev/block/mapper/{}{}", partition, slot);
338*795d594fSAndroid Build Coastguard Worker }
339*795d594fSAndroid Build Coastguard Worker 
GetSupportedFilesystems()340*795d594fSAndroid Build Coastguard Worker Result<std::vector<std::string>> GetSupportedFilesystems() {
341*795d594fSAndroid Build Coastguard Worker   std::string content;
342*795d594fSAndroid Build Coastguard Worker   if (!ReadFileToString("/proc/filesystems", &content)) {
343*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to read '/proc/filesystems'");
344*795d594fSAndroid Build Coastguard Worker   }
345*795d594fSAndroid Build Coastguard Worker   std::vector<std::string> filesystems;
346*795d594fSAndroid Build Coastguard Worker   for (const std::string& line : Split(content, "\n")) {
347*795d594fSAndroid Build Coastguard Worker     std::vector<std::string> tokens = Tokenize(line, " \t");
348*795d594fSAndroid Build Coastguard Worker     // If there are two tokens, the first token is a "nodev" mark, meaning it's not for a block
349*795d594fSAndroid Build Coastguard Worker     // device, so we skip it.
350*795d594fSAndroid Build Coastguard Worker     if (tokens.size() == 1) {
351*795d594fSAndroid Build Coastguard Worker       filesystems.push_back(tokens[0]);
352*795d594fSAndroid Build Coastguard Worker     }
353*795d594fSAndroid Build Coastguard Worker   }
354*795d594fSAndroid Build Coastguard Worker   // Prioritize the filesystems that are known to behave correctly, just in case some bad
355*795d594fSAndroid Build Coastguard Worker   // filesystems are unexpectedly happy to mount volumes that aren't of their types. We have never
356*795d594fSAndroid Build Coastguard Worker   // seen this case in practice though.
357*795d594fSAndroid Build Coastguard Worker   constexpr const char* kWellKnownFilesystems[] = {"erofs", "ext4"};
358*795d594fSAndroid Build Coastguard Worker   for (const char* well_known_fs : kWellKnownFilesystems) {
359*795d594fSAndroid Build Coastguard Worker     auto it = std::find(filesystems.begin(), filesystems.end(), well_known_fs);
360*795d594fSAndroid Build Coastguard Worker     if (it != filesystems.end()) {
361*795d594fSAndroid Build Coastguard Worker       filesystems.erase(it);
362*795d594fSAndroid Build Coastguard Worker       filesystems.insert(filesystems.begin(), well_known_fs);
363*795d594fSAndroid Build Coastguard Worker     }
364*795d594fSAndroid Build Coastguard Worker   }
365*795d594fSAndroid Build Coastguard Worker   return filesystems;
366*795d594fSAndroid Build Coastguard Worker }
367*795d594fSAndroid Build Coastguard Worker 
Mount(const std::string & block_device,const std::string & target,bool is_optional)368*795d594fSAndroid Build Coastguard Worker Result<void> Mount(const std::string& block_device, const std::string& target, bool is_optional) {
369*795d594fSAndroid Build Coastguard Worker   static const NoDestructor<Result<std::vector<std::string>>> supported_filesystems(
370*795d594fSAndroid Build Coastguard Worker       GetSupportedFilesystems());
371*795d594fSAndroid Build Coastguard Worker   if (!supported_filesystems->ok()) {
372*795d594fSAndroid Build Coastguard Worker     return supported_filesystems->error();
373*795d594fSAndroid Build Coastguard Worker   }
374*795d594fSAndroid Build Coastguard Worker   std::vector<std::string> error_msgs;
375*795d594fSAndroid Build Coastguard Worker   for (const std::string& filesystem : supported_filesystems->value()) {
376*795d594fSAndroid Build Coastguard Worker     if (mount(block_device.c_str(),
377*795d594fSAndroid Build Coastguard Worker               target.c_str(),
378*795d594fSAndroid Build Coastguard Worker               filesystem.c_str(),
379*795d594fSAndroid Build Coastguard Worker               MS_RDONLY,
380*795d594fSAndroid Build Coastguard Worker               /*data=*/nullptr) == 0) {
381*795d594fSAndroid Build Coastguard Worker       // Success.
382*795d594fSAndroid Build Coastguard Worker       LOG(INFO) << ART_FORMAT(
383*795d594fSAndroid Build Coastguard Worker           "Mounted '{}' at '{}' with type '{}'", block_device, target, filesystem);
384*795d594fSAndroid Build Coastguard Worker       return {};
385*795d594fSAndroid Build Coastguard Worker     } else {
386*795d594fSAndroid Build Coastguard Worker       if (errno == ENOENT && is_optional) {
387*795d594fSAndroid Build Coastguard Worker         LOG(INFO) << ART_FORMAT("Skipped non-existing block device '{}'", block_device);
388*795d594fSAndroid Build Coastguard Worker         return {};
389*795d594fSAndroid Build Coastguard Worker       }
390*795d594fSAndroid Build Coastguard Worker       error_msgs.push_back(ART_FORMAT("Tried '{}': {}", filesystem, strerror(errno)));
391*795d594fSAndroid Build Coastguard Worker       if (errno != EINVAL && errno != EBUSY) {
392*795d594fSAndroid Build Coastguard Worker         // If the filesystem type is wrong, `errno` must be either `EINVAL` or `EBUSY`. For example,
393*795d594fSAndroid Build Coastguard Worker         // we've seen that trying to mount a device with a wrong filesystem type yields `EBUSY` if
394*795d594fSAndroid Build Coastguard Worker         // the device is also mounted elsewhere, though we can't find any document about this
395*795d594fSAndroid Build Coastguard Worker         // behavior.
396*795d594fSAndroid Build Coastguard Worker         break;
397*795d594fSAndroid Build Coastguard Worker       }
398*795d594fSAndroid Build Coastguard Worker     }
399*795d594fSAndroid Build Coastguard Worker   }
400*795d594fSAndroid Build Coastguard Worker   return Errorf("Failed to mount '{}' at '{}':\n{}", block_device, target, Join(error_msgs, '\n'));
401*795d594fSAndroid Build Coastguard Worker }
402*795d594fSAndroid Build Coastguard Worker 
MountTmpfs(const std::string & target,std::string_view se_context)403*795d594fSAndroid Build Coastguard Worker Result<void> MountTmpfs(const std::string& target, std::string_view se_context) {
404*795d594fSAndroid Build Coastguard Worker   if (mount(/*source=*/"tmpfs",
405*795d594fSAndroid Build Coastguard Worker             target.c_str(),
406*795d594fSAndroid Build Coastguard Worker             /*fs_type=*/"tmpfs",
407*795d594fSAndroid Build Coastguard Worker             MS_NODEV | MS_NOEXEC | MS_NOSUID,
408*795d594fSAndroid Build Coastguard Worker             ART_FORMAT("mode={:#o},rootcontext={}", kChrootDefaultMode, se_context).c_str()) != 0) {
409*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to mount tmpfs at '{}'", target);
410*795d594fSAndroid Build Coastguard Worker   }
411*795d594fSAndroid Build Coastguard Worker   return {};
412*795d594fSAndroid Build Coastguard Worker }
413*795d594fSAndroid Build Coastguard Worker 
LoadOtaSlotFile()414*795d594fSAndroid Build Coastguard Worker Result<std::optional<std::string>> LoadOtaSlotFile() {
415*795d594fSAndroid Build Coastguard Worker   std::string content;
416*795d594fSAndroid Build Coastguard Worker   if (!ReadFileToString(*kOtaSlotFile, &content)) {
417*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to read '{}'", *kOtaSlotFile);
418*795d594fSAndroid Build Coastguard Worker   }
419*795d594fSAndroid Build Coastguard Worker   if (content == "_a" || content == "_b") {
420*795d594fSAndroid Build Coastguard Worker     return content;
421*795d594fSAndroid Build Coastguard Worker   }
422*795d594fSAndroid Build Coastguard Worker   if (content.empty()) {
423*795d594fSAndroid Build Coastguard Worker     return std::nullopt;
424*795d594fSAndroid Build Coastguard Worker   }
425*795d594fSAndroid Build Coastguard Worker   return Errorf("Invalid content of '{}': '{}'", *kOtaSlotFile, content);
426*795d594fSAndroid Build Coastguard Worker }
427*795d594fSAndroid Build Coastguard Worker 
PatchLinkerConfigForCompatEnv()428*795d594fSAndroid Build Coastguard Worker Result<void> PatchLinkerConfigForCompatEnv() {
429*795d594fSAndroid Build Coastguard Worker   std::string art_linker_config_content;
430*795d594fSAndroid Build Coastguard Worker   if (!ReadFileToString(PathInChroot("/linkerconfig/com.android.art/ld.config.txt"),
431*795d594fSAndroid Build Coastguard Worker                         &art_linker_config_content)) {
432*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to read ART linker config");
433*795d594fSAndroid Build Coastguard Worker   }
434*795d594fSAndroid Build Coastguard Worker 
435*795d594fSAndroid Build Coastguard Worker   std::string compat_section =
436*795d594fSAndroid Build Coastguard Worker       OR_RETURN(ConstructLinkerConfigCompatEnvSection(art_linker_config_content));
437*795d594fSAndroid Build Coastguard Worker 
438*795d594fSAndroid Build Coastguard Worker   // Append the patched section to the global linker config. Because the compat env path doesn't
439*795d594fSAndroid Build Coastguard Worker   // start with "/apex", the global linker config is the one that takes effect.
440*795d594fSAndroid Build Coastguard Worker   std::string global_linker_config_path = PathInChroot("/linkerconfig/ld.config.txt");
441*795d594fSAndroid Build Coastguard Worker   std::string global_linker_config_content;
442*795d594fSAndroid Build Coastguard Worker   if (!ReadFileToString(global_linker_config_path, &global_linker_config_content)) {
443*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to read global linker config");
444*795d594fSAndroid Build Coastguard Worker   }
445*795d594fSAndroid Build Coastguard Worker 
446*795d594fSAndroid Build Coastguard Worker   if (!WriteStringToFile("dir.com.android.art.compat = /mnt/compat_env/apex/com.android.art/bin\n" +
447*795d594fSAndroid Build Coastguard Worker                              global_linker_config_content + compat_section,
448*795d594fSAndroid Build Coastguard Worker                          global_linker_config_path)) {
449*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to write global linker config");
450*795d594fSAndroid Build Coastguard Worker   }
451*795d594fSAndroid Build Coastguard Worker 
452*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << "Patched " << global_linker_config_path;
453*795d594fSAndroid Build Coastguard Worker   return {};
454*795d594fSAndroid Build Coastguard Worker }
455*795d594fSAndroid Build Coastguard Worker 
456*795d594fSAndroid Build Coastguard Worker // Platform libraries communicate with things outside of chroot through unstable APIs. Examples are
457*795d594fSAndroid Build Coastguard Worker // `libbinder_ndk.so` talking to `servicemanager` and `libcgrouprc.so` reading
458*795d594fSAndroid Build Coastguard Worker // `/dev/cgroup_info/cgroup.rc`. To work around incompatibility issues, we bind-mount the old
459*795d594fSAndroid Build Coastguard Worker // platform library directories into chroot so that both sides of a communication are old and
460*795d594fSAndroid Build Coastguard Worker // therefore align with each other.
461*795d594fSAndroid Build Coastguard Worker // After bind-mounting old platform libraries, the chroot environment has a combination of new
462*795d594fSAndroid Build Coastguard Worker // modules and old platform libraries. We currently use the new linker config in such an
463*795d594fSAndroid Build Coastguard Worker // environment, which is potentially problematic. If we start to see problems, we should consider
464*795d594fSAndroid Build Coastguard Worker // generating a more correct linker config in a more complex way.
PrepareExternalLibDirs()465*795d594fSAndroid Build Coastguard Worker Result<void> PrepareExternalLibDirs() {
466*795d594fSAndroid Build Coastguard Worker   std::vector<const char*> existing_lib_dirs;
467*795d594fSAndroid Build Coastguard Worker   std::copy_if(kExternalLibDirs.begin(),
468*795d594fSAndroid Build Coastguard Worker                kExternalLibDirs.end(),
469*795d594fSAndroid Build Coastguard Worker                std::back_inserter(existing_lib_dirs),
470*795d594fSAndroid Build Coastguard Worker                OS::DirectoryExists);
471*795d594fSAndroid Build Coastguard Worker   if (existing_lib_dirs.empty()) {
472*795d594fSAndroid Build Coastguard Worker     return Errorf("Unexpectedly missing platform library directories. Tried '{}'",
473*795d594fSAndroid Build Coastguard Worker                   android::base::Join(kExternalLibDirs, "', '"));
474*795d594fSAndroid Build Coastguard Worker   }
475*795d594fSAndroid Build Coastguard Worker 
476*795d594fSAndroid Build Coastguard Worker   // We should bind-mount all existing lib dirs or none of them. Try the first one to decide what
477*795d594fSAndroid Build Coastguard Worker   // to do next.
478*795d594fSAndroid Build Coastguard Worker   Result<void> result = BindMount(existing_lib_dirs[0], PathInChroot(existing_lib_dirs[0]));
479*795d594fSAndroid Build Coastguard Worker   if (result.ok()) {
480*795d594fSAndroid Build Coastguard Worker     for (size_t i = 1; i < existing_lib_dirs.size(); i++) {
481*795d594fSAndroid Build Coastguard Worker       OR_RETURN(BindMount(existing_lib_dirs[i], PathInChroot(existing_lib_dirs[i])));
482*795d594fSAndroid Build Coastguard Worker     }
483*795d594fSAndroid Build Coastguard Worker   } else if (result.error().code() == EACCES) {
484*795d594fSAndroid Build Coastguard Worker     // We don't have the permission to do so on V. Fall back to bind-mounting elsewhere.
485*795d594fSAndroid Build Coastguard Worker     LOG(WARNING) << result.error().message();
486*795d594fSAndroid Build Coastguard Worker 
487*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env")));
488*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env/system")));
489*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env/system_ext")));
490*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env/apex")));
491*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env/apex/com.android.art")));
492*795d594fSAndroid Build Coastguard Worker     OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env/apex/com.android.art/bin")));
493*795d594fSAndroid Build Coastguard Worker     OR_RETURN(BindMountDirect(PathInChroot("/apex/com.android.art/bin"),
494*795d594fSAndroid Build Coastguard Worker                               PathInChroot("/mnt/compat_env/apex/com.android.art/bin")));
495*795d594fSAndroid Build Coastguard Worker     for (const char* lib_dir : existing_lib_dirs) {
496*795d594fSAndroid Build Coastguard Worker       OR_RETURN(CreateDir(PathInChroot("/mnt/compat_env") + lib_dir));
497*795d594fSAndroid Build Coastguard Worker       OR_RETURN(BindMountDirect(lib_dir, PathInChroot("/mnt/compat_env") + lib_dir));
498*795d594fSAndroid Build Coastguard Worker     }
499*795d594fSAndroid Build Coastguard Worker 
500*795d594fSAndroid Build Coastguard Worker     OR_RETURN(PatchLinkerConfigForCompatEnv());
501*795d594fSAndroid Build Coastguard Worker   } else {
502*795d594fSAndroid Build Coastguard Worker     return result;
503*795d594fSAndroid Build Coastguard Worker   }
504*795d594fSAndroid Build Coastguard Worker 
505*795d594fSAndroid Build Coastguard Worker   return {};
506*795d594fSAndroid Build Coastguard Worker }
507*795d594fSAndroid Build Coastguard Worker 
508*795d594fSAndroid Build Coastguard Worker }  // namespace
509*795d594fSAndroid Build Coastguard Worker 
setUp(const std::optional<std::string> & in_otaSlot,bool in_mapSnapshotsForOta)510*795d594fSAndroid Build Coastguard Worker ScopedAStatus DexoptChrootSetup::setUp(const std::optional<std::string>& in_otaSlot,
511*795d594fSAndroid Build Coastguard Worker                                        bool in_mapSnapshotsForOta) {
512*795d594fSAndroid Build Coastguard Worker   if (!mu_.try_lock()) {
513*795d594fSAndroid Build Coastguard Worker     return Fatal("Unexpected concurrent calls");
514*795d594fSAndroid Build Coastguard Worker   }
515*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lock(mu_, std::adopt_lock);
516*795d594fSAndroid Build Coastguard Worker 
517*795d594fSAndroid Build Coastguard Worker   if (in_otaSlot.has_value() && (in_otaSlot.value() != "_a" && in_otaSlot.value() != "_b")) {
518*795d594fSAndroid Build Coastguard Worker     return Fatal(ART_FORMAT("Invalid OTA slot '{}'", in_otaSlot.value()));
519*795d594fSAndroid Build Coastguard Worker   }
520*795d594fSAndroid Build Coastguard Worker   OR_RETURN_NON_FATAL(SetUpChroot(in_otaSlot, in_mapSnapshotsForOta));
521*795d594fSAndroid Build Coastguard Worker   return ScopedAStatus::ok();
522*795d594fSAndroid Build Coastguard Worker }
523*795d594fSAndroid Build Coastguard Worker 
init()524*795d594fSAndroid Build Coastguard Worker ScopedAStatus DexoptChrootSetup::init() {
525*795d594fSAndroid Build Coastguard Worker   if (!mu_.try_lock()) {
526*795d594fSAndroid Build Coastguard Worker     return Fatal("Unexpected concurrent calls");
527*795d594fSAndroid Build Coastguard Worker   }
528*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lock(mu_, std::adopt_lock);
529*795d594fSAndroid Build Coastguard Worker 
530*795d594fSAndroid Build Coastguard Worker   if (OS::FileExists(PathInChroot("/linkerconfig/ld.config.txt").c_str())) {
531*795d594fSAndroid Build Coastguard Worker     return Fatal("init must not be repeatedly called");
532*795d594fSAndroid Build Coastguard Worker   }
533*795d594fSAndroid Build Coastguard Worker 
534*795d594fSAndroid Build Coastguard Worker   OR_RETURN_NON_FATAL(InitChroot());
535*795d594fSAndroid Build Coastguard Worker   return ScopedAStatus::ok();
536*795d594fSAndroid Build Coastguard Worker }
537*795d594fSAndroid Build Coastguard Worker 
tearDown(bool in_allowConcurrent)538*795d594fSAndroid Build Coastguard Worker ScopedAStatus DexoptChrootSetup::tearDown(bool in_allowConcurrent) {
539*795d594fSAndroid Build Coastguard Worker   if (in_allowConcurrent) {
540*795d594fSAndroid Build Coastguard Worker     // Normally, we don't expect concurrent calls, but this method may be called upon system server
541*795d594fSAndroid Build Coastguard Worker     // restart when another call initiated by the previous system_server instance is still being
542*795d594fSAndroid Build Coastguard Worker     // processed.
543*795d594fSAndroid Build Coastguard Worker     mu_.lock();
544*795d594fSAndroid Build Coastguard Worker   } else {
545*795d594fSAndroid Build Coastguard Worker     if (!mu_.try_lock()) {
546*795d594fSAndroid Build Coastguard Worker       return Fatal("Unexpected concurrent calls");
547*795d594fSAndroid Build Coastguard Worker     }
548*795d594fSAndroid Build Coastguard Worker   }
549*795d594fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> lock(mu_, std::adopt_lock);
550*795d594fSAndroid Build Coastguard Worker 
551*795d594fSAndroid Build Coastguard Worker   OR_RETURN_NON_FATAL(TearDownChroot());
552*795d594fSAndroid Build Coastguard Worker   return ScopedAStatus::ok();
553*795d594fSAndroid Build Coastguard Worker }
554*795d594fSAndroid Build Coastguard Worker 
Start()555*795d594fSAndroid Build Coastguard Worker Result<void> DexoptChrootSetup::Start() {
556*795d594fSAndroid Build Coastguard Worker   ScopedAStatus status = ScopedAStatus::fromStatus(
557*795d594fSAndroid Build Coastguard Worker       AServiceManager_registerLazyService(this->asBinder().get(), kServiceName));
558*795d594fSAndroid Build Coastguard Worker   if (!status.isOk()) {
559*795d594fSAndroid Build Coastguard Worker     return Error() << status.getDescription();
560*795d594fSAndroid Build Coastguard Worker   }
561*795d594fSAndroid Build Coastguard Worker 
562*795d594fSAndroid Build Coastguard Worker   ABinderProcess_startThreadPool();
563*795d594fSAndroid Build Coastguard Worker 
564*795d594fSAndroid Build Coastguard Worker   return {};
565*795d594fSAndroid Build Coastguard Worker }
566*795d594fSAndroid Build Coastguard Worker 
SetUpChroot(const std::optional<std::string> & ota_slot,bool map_snapshots_for_ota) const567*795d594fSAndroid Build Coastguard Worker Result<void> DexoptChrootSetup::SetUpChroot(const std::optional<std::string>& ota_slot,
568*795d594fSAndroid Build Coastguard Worker                                             bool map_snapshots_for_ota) const {
569*795d594fSAndroid Build Coastguard Worker   // Set the default permission mode for new files and dirs to be `kChrootDefaultMode`.
570*795d594fSAndroid Build Coastguard Worker   umask(~kChrootDefaultMode & 0777);
571*795d594fSAndroid Build Coastguard Worker 
572*795d594fSAndroid Build Coastguard Worker   // In case there is some leftover.
573*795d594fSAndroid Build Coastguard Worker   OR_RETURN(TearDownChroot());
574*795d594fSAndroid Build Coastguard Worker 
575*795d594fSAndroid Build Coastguard Worker   // Prepare the root dir of chroot. The parent directory has been created by init (see `init.rc`).
576*795d594fSAndroid Build Coastguard Worker   OR_RETURN(CreateDir(CHROOT_DIR));
577*795d594fSAndroid Build Coastguard Worker   LOG(INFO) << ART_FORMAT("Created '{}'", CHROOT_DIR);
578*795d594fSAndroid Build Coastguard Worker 
579*795d594fSAndroid Build Coastguard Worker   std::vector<std::tuple<std::string, std::string>> additional_system_partitions = {
580*795d594fSAndroid Build Coastguard Worker       {"system_ext", "/system_ext"},
581*795d594fSAndroid Build Coastguard Worker       {"vendor", "/vendor"},
582*795d594fSAndroid Build Coastguard Worker       {"product", "/product"},
583*795d594fSAndroid Build Coastguard Worker   };
584*795d594fSAndroid Build Coastguard Worker 
585*795d594fSAndroid Build Coastguard Worker   std::string partitions_from_sysprop =
586*795d594fSAndroid Build Coastguard Worker       GetProperty(kAdditionalPartitionsSysprop, /*default_value=*/"");
587*795d594fSAndroid Build Coastguard Worker   std::vector<std::string_view> partitions_from_sysprop_entries;
588*795d594fSAndroid Build Coastguard Worker   art::Split(partitions_from_sysprop, ',', &partitions_from_sysprop_entries);
589*795d594fSAndroid Build Coastguard Worker   for (std::string_view entry : partitions_from_sysprop_entries) {
590*795d594fSAndroid Build Coastguard Worker     std::vector<std::string_view> pair;
591*795d594fSAndroid Build Coastguard Worker     art::Split(entry, ':', &pair);
592*795d594fSAndroid Build Coastguard Worker     if (pair.size() != 2 || pair[0].empty() || pair[1].empty() || !pair[1].starts_with('/')) {
593*795d594fSAndroid Build Coastguard Worker       return Errorf("Malformed entry in '{}': '{}'", kAdditionalPartitionsSysprop, entry);
594*795d594fSAndroid Build Coastguard Worker     }
595*795d594fSAndroid Build Coastguard Worker     additional_system_partitions.emplace_back(std::string(pair[0]), std::string(pair[1]));
596*795d594fSAndroid Build Coastguard Worker   }
597*795d594fSAndroid Build Coastguard Worker 
598*795d594fSAndroid Build Coastguard Worker   if (!IsOtaUpdate(ota_slot)) {  // Mainline update
599*795d594fSAndroid Build Coastguard Worker     OR_RETURN(BindMount("/", CHROOT_DIR));
600*795d594fSAndroid Build Coastguard Worker     // Normally, we don't need to bind-mount "/system" because it's a part of the image mounted at
601*795d594fSAndroid Build Coastguard Worker     // "/". However, when readonly partitions are remounted read-write, an overlay is created at
602*795d594fSAndroid Build Coastguard Worker     // "/system", so we need to bind-mount "/system" to handle this case. On devices where readonly
603*795d594fSAndroid Build Coastguard Worker     // partitions are not remounted, bind-mounting "/system" doesn't hurt.
604*795d594fSAndroid Build Coastguard Worker     OR_RETURN(BindMount("/system", PathInChroot("/system")));
605*795d594fSAndroid Build Coastguard Worker     for (const auto& [partition, mount_point] : additional_system_partitions) {
606*795d594fSAndroid Build Coastguard Worker       // Some additional partitions are optional. On a device where an additional partition doesn't
607*795d594fSAndroid Build Coastguard Worker       // exist, the mount point of the partition is a symlink to a directory inside /system.
608*795d594fSAndroid Build Coastguard Worker       if (!OR_RETURN(IsSymlink(mount_point))) {
609*795d594fSAndroid Build Coastguard Worker         OR_RETURN(BindMount(mount_point, PathInChroot(mount_point)));
610*795d594fSAndroid Build Coastguard Worker       }
611*795d594fSAndroid Build Coastguard Worker     }
612*795d594fSAndroid Build Coastguard Worker   } else {
613*795d594fSAndroid Build Coastguard Worker     CHECK(ota_slot.value() == "_a" || ota_slot.value() == "_b");
614*795d594fSAndroid Build Coastguard Worker 
615*795d594fSAndroid Build Coastguard Worker     if (map_snapshots_for_ota) {
616*795d594fSAndroid Build Coastguard Worker       // Write the file early in case `snapshotctl map` fails in the middle, leaving some devices
617*795d594fSAndroid Build Coastguard Worker       // mapped. We don't assume that `snapshotctl map` is transactional.
618*795d594fSAndroid Build Coastguard Worker       if (!WriteStringToFile("", *kSnapshotMappedFile)) {
619*795d594fSAndroid Build Coastguard Worker         return ErrnoErrorf("Failed to write '{}'", *kSnapshotMappedFile);
620*795d594fSAndroid Build Coastguard Worker       }
621*795d594fSAndroid Build Coastguard Worker 
622*795d594fSAndroid Build Coastguard Worker       // Run `snapshotctl map` through init to map block devices. We can't run it ourselves because
623*795d594fSAndroid Build Coastguard Worker       // it requires the UID to be 0. See `sys.snapshotctl.map` in `init.rc`.
624*795d594fSAndroid Build Coastguard Worker       if (!SetProperty("sys.snapshotctl.map", "requested")) {
625*795d594fSAndroid Build Coastguard Worker         return Errorf("Failed to request snapshotctl map");
626*795d594fSAndroid Build Coastguard Worker       }
627*795d594fSAndroid Build Coastguard Worker       if (!WaitForProperty("sys.snapshotctl.map", "finished", kSnapshotCtlTimeout)) {
628*795d594fSAndroid Build Coastguard Worker         return Errorf("snapshotctl timed out");
629*795d594fSAndroid Build Coastguard Worker       }
630*795d594fSAndroid Build Coastguard Worker 
631*795d594fSAndroid Build Coastguard Worker       // We don't know whether snapshotctl succeeded or not, but if it failed, the mount operation
632*795d594fSAndroid Build Coastguard Worker       // below will fail with `ENOENT`.
633*795d594fSAndroid Build Coastguard Worker       OR_RETURN(
634*795d594fSAndroid Build Coastguard Worker           Mount(GetBlockDeviceName("system", ota_slot.value()), CHROOT_DIR, /*is_optional=*/false));
635*795d594fSAndroid Build Coastguard Worker     } else {
636*795d594fSAndroid Build Coastguard Worker       // update_engine has mounted `system` at `/postinstall` for us.
637*795d594fSAndroid Build Coastguard Worker       OR_RETURN(BindMount("/postinstall", CHROOT_DIR));
638*795d594fSAndroid Build Coastguard Worker     }
639*795d594fSAndroid Build Coastguard Worker 
640*795d594fSAndroid Build Coastguard Worker     for (const auto& [partition, mount_point] : additional_system_partitions) {
641*795d594fSAndroid Build Coastguard Worker       OR_RETURN(Mount(GetBlockDeviceName(partition, ota_slot.value()),
642*795d594fSAndroid Build Coastguard Worker                       PathInChroot(mount_point),
643*795d594fSAndroid Build Coastguard Worker                       /*is_optional=*/true));
644*795d594fSAndroid Build Coastguard Worker     }
645*795d594fSAndroid Build Coastguard Worker   }
646*795d594fSAndroid Build Coastguard Worker 
647*795d594fSAndroid Build Coastguard Worker   OR_RETURN(MountTmpfs(PathInChroot("/apex"), "u:object_r:apex_mnt_dir:s0"));
648*795d594fSAndroid Build Coastguard Worker   OR_RETURN(MountTmpfs(PathInChroot("/linkerconfig"), "u:object_r:linkerconfig_file:s0"));
649*795d594fSAndroid Build Coastguard Worker   OR_RETURN(MountTmpfs(PathInChroot("/mnt"), "u:object_r:pre_reboot_dexopt_file:s0"));
650*795d594fSAndroid Build Coastguard Worker   OR_RETURN(CreateDir(PathInChroot("/mnt/artd_tmp")));
651*795d594fSAndroid Build Coastguard Worker   OR_RETURN(MountTmpfs(PathInChroot("/mnt/artd_tmp"), "u:object_r:pre_reboot_dexopt_artd_file:s0"));
652*795d594fSAndroid Build Coastguard Worker   OR_RETURN(CreateDir(PathInChroot("/mnt/expand")));
653*795d594fSAndroid Build Coastguard Worker 
654*795d594fSAndroid Build Coastguard Worker   std::vector<std::string> bind_mount_srcs = {
655*795d594fSAndroid Build Coastguard Worker       // Data partitions.
656*795d594fSAndroid Build Coastguard Worker       "/data",
657*795d594fSAndroid Build Coastguard Worker       "/mnt/expand",
658*795d594fSAndroid Build Coastguard Worker       // Linux API filesystems.
659*795d594fSAndroid Build Coastguard Worker       "/dev",
660*795d594fSAndroid Build Coastguard Worker       "/proc",
661*795d594fSAndroid Build Coastguard Worker       "/sys",
662*795d594fSAndroid Build Coastguard Worker       // For apexd to query staged APEX sessions.
663*795d594fSAndroid Build Coastguard Worker       "/metadata",
664*795d594fSAndroid Build Coastguard Worker   };
665*795d594fSAndroid Build Coastguard Worker 
666*795d594fSAndroid Build Coastguard Worker   for (const std::string& src : bind_mount_srcs) {
667*795d594fSAndroid Build Coastguard Worker     OR_RETURN(BindMountRecursive(src, PathInChroot(src)));
668*795d594fSAndroid Build Coastguard Worker   }
669*795d594fSAndroid Build Coastguard Worker 
670*795d594fSAndroid Build Coastguard Worker   if (!WriteStringToFile(ota_slot.value_or(""), *kOtaSlotFile)) {
671*795d594fSAndroid Build Coastguard Worker     return ErrnoErrorf("Failed to write '{}'", *kOtaSlotFile);
672*795d594fSAndroid Build Coastguard Worker   }
673*795d594fSAndroid Build Coastguard Worker 
674*795d594fSAndroid Build Coastguard Worker   return {};
675*795d594fSAndroid Build Coastguard Worker }
676*795d594fSAndroid Build Coastguard Worker 
InitChroot() const677*795d594fSAndroid Build Coastguard Worker Result<void> DexoptChrootSetup::InitChroot() const {
678*795d594fSAndroid Build Coastguard Worker   std::optional<std::string> ota_slot = OR_RETURN(LoadOtaSlotFile());
679*795d594fSAndroid Build Coastguard Worker 
680*795d594fSAndroid Build Coastguard Worker   // Generate empty linker config to suppress warnings.
681*795d594fSAndroid Build Coastguard Worker   if (!android::base::WriteStringToFile("", PathInChroot("/linkerconfig/ld.config.txt"))) {
682*795d594fSAndroid Build Coastguard Worker     PLOG(WARNING) << "Failed to generate empty linker config to suppress warnings";
683*795d594fSAndroid Build Coastguard Worker   }
684*795d594fSAndroid Build Coastguard Worker 
685*795d594fSAndroid Build Coastguard Worker   CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder());
686*795d594fSAndroid Build Coastguard Worker   args.Add("--")
687*795d594fSAndroid Build Coastguard Worker       .Add("/system/bin/apexd")
688*795d594fSAndroid Build Coastguard Worker       .Add("--otachroot-bootstrap")
689*795d594fSAndroid Build Coastguard Worker       .AddIf(!IsOtaUpdate(ota_slot), "--also-include-staged-apexes");
690*795d594fSAndroid Build Coastguard Worker   OR_RETURN(Run("apexd", args.Get()));
691*795d594fSAndroid Build Coastguard Worker 
692*795d594fSAndroid Build Coastguard Worker   args = OR_RETURN(GetArtExecCmdlineBuilder());
693*795d594fSAndroid Build Coastguard Worker   args.Add("--drop-capabilities")
694*795d594fSAndroid Build Coastguard Worker       .Add("--")
695*795d594fSAndroid Build Coastguard Worker       .Add("/apex/com.android.runtime/bin/linkerconfig")
696*795d594fSAndroid Build Coastguard Worker       .Add("--target")
697*795d594fSAndroid Build Coastguard Worker       .Add("/linkerconfig");
698*795d594fSAndroid Build Coastguard Worker   OR_RETURN(Run("linkerconfig", args.Get()));
699*795d594fSAndroid Build Coastguard Worker 
700*795d594fSAndroid Build Coastguard Worker   if (IsOtaUpdate(ota_slot)) {
701*795d594fSAndroid Build Coastguard Worker     OR_RETURN(PrepareExternalLibDirs());
702*795d594fSAndroid Build Coastguard Worker   }
703*795d594fSAndroid Build Coastguard Worker 
704*795d594fSAndroid Build Coastguard Worker   return {};
705*795d594fSAndroid Build Coastguard Worker }
706*795d594fSAndroid Build Coastguard Worker 
TearDownChroot() const707*795d594fSAndroid Build Coastguard Worker Result<void> DexoptChrootSetup::TearDownChroot() const {
708*795d594fSAndroid Build Coastguard Worker   // For mount points in `kExternalLibDirs`, make sure we have unmounted them before running apexd,
709*795d594fSAndroid Build Coastguard Worker   // as apexd expects new libraries.
710*795d594fSAndroid Build Coastguard Worker   // For mount points under "/mnt/compat_env", make sure we have unmounted them before running
711*795d594fSAndroid Build Coastguard Worker   // apexd, as apexd doesn't expect apexes to be in-use.
712*795d594fSAndroid Build Coastguard Worker   std::vector<FstabEntry> entries = OR_RETURN(GetProcMountsDescendantsOfPath(CHROOT_DIR));
713*795d594fSAndroid Build Coastguard Worker   for (const FstabEntry entry : entries) {
714*795d594fSAndroid Build Coastguard Worker     std::string_view mount_point_in_chroot = entry.mount_point;
715*795d594fSAndroid Build Coastguard Worker     CHECK(ConsumePrefix(&mount_point_in_chroot, CHROOT_DIR));
716*795d594fSAndroid Build Coastguard Worker     if (mount_point_in_chroot.empty()) {
717*795d594fSAndroid Build Coastguard Worker       continue;  // The root mount.
718*795d594fSAndroid Build Coastguard Worker     }
719*795d594fSAndroid Build Coastguard Worker     if (ContainsElement(kExternalLibDirs, mount_point_in_chroot) ||
720*795d594fSAndroid Build Coastguard Worker         PathStartsWith(mount_point_in_chroot, "/mnt/compat_env")) {
721*795d594fSAndroid Build Coastguard Worker       OR_RETURN(Unmount(entry.mount_point));
722*795d594fSAndroid Build Coastguard Worker     }
723*795d594fSAndroid Build Coastguard Worker   }
724*795d594fSAndroid Build Coastguard Worker 
725*795d594fSAndroid Build Coastguard Worker   std::vector<FstabEntry> apex_entries =
726*795d594fSAndroid Build Coastguard Worker       OR_RETURN(GetProcMountsDescendantsOfPath(PathInChroot("/apex")));
727*795d594fSAndroid Build Coastguard Worker   // If there is only one entry, it's /apex itself.
728*795d594fSAndroid Build Coastguard Worker   bool has_apex = apex_entries.size() > 1;
729*795d594fSAndroid Build Coastguard Worker 
730*795d594fSAndroid Build Coastguard Worker   if (has_apex && OS::FileExists(PathInChroot("/system/bin/apexd").c_str())) {
731*795d594fSAndroid Build Coastguard Worker     // Delegate to apexd to unmount all APEXes. It also cleans up loop devices.
732*795d594fSAndroid Build Coastguard Worker     CmdlineBuilder args = OR_RETURN(GetArtExecCmdlineBuilder());
733*795d594fSAndroid Build Coastguard Worker     args.Add("--")
734*795d594fSAndroid Build Coastguard Worker         .Add("/system/bin/apexd")
735*795d594fSAndroid Build Coastguard Worker         .Add("--unmount-all")
736*795d594fSAndroid Build Coastguard Worker         .Add("--also-include-staged-apexes");
737*795d594fSAndroid Build Coastguard Worker     OR_RETURN(Run("apexd", args.Get()));
738*795d594fSAndroid Build Coastguard Worker   }
739*795d594fSAndroid Build Coastguard Worker 
740*795d594fSAndroid Build Coastguard Worker   // Double check to make sure all APEXes are unmounted, just in case apexd incorrectly reported
741*795d594fSAndroid Build Coastguard Worker   // success.
742*795d594fSAndroid Build Coastguard Worker   apex_entries = OR_RETURN(GetProcMountsDescendantsOfPath(PathInChroot("/apex")));
743*795d594fSAndroid Build Coastguard Worker   for (const FstabEntry& entry : apex_entries) {
744*795d594fSAndroid Build Coastguard Worker     if (entry.mount_point != PathInChroot("/apex")) {
745*795d594fSAndroid Build Coastguard Worker       return Errorf("apexd didn't unmount '{}'. See logs for details", entry.mount_point);
746*795d594fSAndroid Build Coastguard Worker     }
747*795d594fSAndroid Build Coastguard Worker   }
748*795d594fSAndroid Build Coastguard Worker 
749*795d594fSAndroid Build Coastguard Worker   // The list is in mount order.
750*795d594fSAndroid Build Coastguard Worker   entries = OR_RETURN(GetProcMountsDescendantsOfPath(CHROOT_DIR));
751*795d594fSAndroid Build Coastguard Worker   for (auto it = entries.rbegin(); it != entries.rend(); it++) {
752*795d594fSAndroid Build Coastguard Worker     OR_RETURN(Unmount(it->mount_point));
753*795d594fSAndroid Build Coastguard Worker   }
754*795d594fSAndroid Build Coastguard Worker 
755*795d594fSAndroid Build Coastguard Worker   std::error_code ec;
756*795d594fSAndroid Build Coastguard Worker   std::uintmax_t removed = std::filesystem::remove_all(CHROOT_DIR, ec);
757*795d594fSAndroid Build Coastguard Worker   if (ec) {
758*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to remove dir '{}': {}", CHROOT_DIR, ec.message());
759*795d594fSAndroid Build Coastguard Worker   }
760*795d594fSAndroid Build Coastguard Worker   if (removed > 0) {
761*795d594fSAndroid Build Coastguard Worker     LOG(INFO) << ART_FORMAT("Removed '{}'", CHROOT_DIR);
762*795d594fSAndroid Build Coastguard Worker   }
763*795d594fSAndroid Build Coastguard Worker 
764*795d594fSAndroid Build Coastguard Worker   if (!OR_RETURN(GetProcMountsDescendantsOfPath(*kBindMountTmpDir)).empty()) {
765*795d594fSAndroid Build Coastguard Worker     OR_RETURN(Unmount(*kBindMountTmpDir));
766*795d594fSAndroid Build Coastguard Worker   }
767*795d594fSAndroid Build Coastguard Worker 
768*795d594fSAndroid Build Coastguard Worker   std::filesystem::remove_all(*kBindMountTmpDir, ec);
769*795d594fSAndroid Build Coastguard Worker   if (ec) {
770*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to remove dir '{}': {}", *kBindMountTmpDir, ec.message());
771*795d594fSAndroid Build Coastguard Worker   }
772*795d594fSAndroid Build Coastguard Worker 
773*795d594fSAndroid Build Coastguard Worker   std::filesystem::remove(*kOtaSlotFile, ec);
774*795d594fSAndroid Build Coastguard Worker   if (ec) {
775*795d594fSAndroid Build Coastguard Worker     return Errorf("Failed to remove file '{}': {}", *kOtaSlotFile, ec.message());
776*795d594fSAndroid Build Coastguard Worker   }
777*795d594fSAndroid Build Coastguard Worker 
778*795d594fSAndroid Build Coastguard Worker   if (OS::FileExists(kSnapshotMappedFile->c_str())) {
779*795d594fSAndroid Build Coastguard Worker     if (!SetProperty("sys.snapshotctl.unmap", "requested")) {
780*795d594fSAndroid Build Coastguard Worker       return Errorf("Failed to request snapshotctl unmap");
781*795d594fSAndroid Build Coastguard Worker     }
782*795d594fSAndroid Build Coastguard Worker     if (!WaitForProperty("sys.snapshotctl.unmap", "finished", kSnapshotCtlTimeout)) {
783*795d594fSAndroid Build Coastguard Worker       return Errorf("snapshotctl timed out");
784*795d594fSAndroid Build Coastguard Worker     }
785*795d594fSAndroid Build Coastguard Worker     std::filesystem::remove(*kSnapshotMappedFile, ec);
786*795d594fSAndroid Build Coastguard Worker     if (ec) {
787*795d594fSAndroid Build Coastguard Worker       return Errorf("Failed to remove file '{}': {}", *kSnapshotMappedFile, ec.message());
788*795d594fSAndroid Build Coastguard Worker     }
789*795d594fSAndroid Build Coastguard Worker   }
790*795d594fSAndroid Build Coastguard Worker 
791*795d594fSAndroid Build Coastguard Worker   return {};
792*795d594fSAndroid Build Coastguard Worker }
793*795d594fSAndroid Build Coastguard Worker 
PathInChroot(std::string_view path)794*795d594fSAndroid Build Coastguard Worker std::string PathInChroot(std::string_view path) {
795*795d594fSAndroid Build Coastguard Worker   return std::string(DexoptChrootSetup::CHROOT_DIR).append(path);
796*795d594fSAndroid Build Coastguard Worker }
797*795d594fSAndroid Build Coastguard Worker 
ConstructLinkerConfigCompatEnvSection(const std::string & art_linker_config_content)798*795d594fSAndroid Build Coastguard Worker Result<std::string> ConstructLinkerConfigCompatEnvSection(
799*795d594fSAndroid Build Coastguard Worker     const std::string& art_linker_config_content) {
800*795d594fSAndroid Build Coastguard Worker   std::regex system_lib_re(R"re((=\s*|:)/(system(?:_ext)?/\$\{LIB\}))re");
801*795d594fSAndroid Build Coastguard Worker   constexpr const char* kSystemLibFmt = "$1/mnt/compat_env/$2";
802*795d594fSAndroid Build Coastguard Worker 
803*795d594fSAndroid Build Coastguard Worker   // Make a copy of the [com.android.art] section and patch particular lines.
804*795d594fSAndroid Build Coastguard Worker   std::string compat_section;
805*795d594fSAndroid Build Coastguard Worker   bool is_in_art_section = false;
806*795d594fSAndroid Build Coastguard Worker   bool replaced = false;
807*795d594fSAndroid Build Coastguard Worker   std::vector<std::string_view> art_linker_config_lines;
808*795d594fSAndroid Build Coastguard Worker   art::Split(art_linker_config_content, '\n', &art_linker_config_lines);
809*795d594fSAndroid Build Coastguard Worker   for (std::string_view line : art_linker_config_lines) {
810*795d594fSAndroid Build Coastguard Worker     if (!is_in_art_section && line == "[com.android.art]") {
811*795d594fSAndroid Build Coastguard Worker       is_in_art_section = true;
812*795d594fSAndroid Build Coastguard Worker     } else if (is_in_art_section && line.starts_with('[')) {
813*795d594fSAndroid Build Coastguard Worker       is_in_art_section = false;
814*795d594fSAndroid Build Coastguard Worker     }
815*795d594fSAndroid Build Coastguard Worker 
816*795d594fSAndroid Build Coastguard Worker     if (is_in_art_section) {
817*795d594fSAndroid Build Coastguard Worker       if (line == "[com.android.art]") {
818*795d594fSAndroid Build Coastguard Worker         compat_section += "[com.android.art.compat]\n";
819*795d594fSAndroid Build Coastguard Worker       } else {
820*795d594fSAndroid Build Coastguard Worker         std::string patched_line =
821*795d594fSAndroid Build Coastguard Worker             std::regex_replace(std::string(line), system_lib_re, kSystemLibFmt);
822*795d594fSAndroid Build Coastguard Worker         if (line != patched_line) {
823*795d594fSAndroid Build Coastguard Worker           LOG(DEBUG) << ART_FORMAT("Replacing '{}' with '{}'", line, patched_line);
824*795d594fSAndroid Build Coastguard Worker           replaced = true;
825*795d594fSAndroid Build Coastguard Worker         }
826*795d594fSAndroid Build Coastguard Worker         compat_section += patched_line;
827*795d594fSAndroid Build Coastguard Worker         compat_section += '\n';
828*795d594fSAndroid Build Coastguard Worker       }
829*795d594fSAndroid Build Coastguard Worker     }
830*795d594fSAndroid Build Coastguard Worker   }
831*795d594fSAndroid Build Coastguard Worker   if (!replaced) {
832*795d594fSAndroid Build Coastguard Worker     return Errorf("No matching lines to patch in ART linker config");
833*795d594fSAndroid Build Coastguard Worker   }
834*795d594fSAndroid Build Coastguard Worker   return compat_section;
835*795d594fSAndroid Build Coastguard Worker }
836*795d594fSAndroid Build Coastguard Worker 
837*795d594fSAndroid Build Coastguard Worker }  // namespace dexopt_chroot_setup
838*795d594fSAndroid Build Coastguard Worker }  // namespace art
839