1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/libs/image_aggregator/sparse_image_utils.h"
18 
19 #include <android-base/file.h>
20 #include <android-base/logging.h>
21 #include <string.h>
22 #include <sys/file.h>
23 
24 #include <fstream>
25 #include <string>
26 #include <string_view>
27 
28 #include "common/libs/fs/shared_fd.h"
29 #include "common/libs/utils/result.h"
30 #include "common/libs/utils/subprocess.h"
31 #include "host/libs/config/config_utils.h"
32 #include "host/libs/config/cuttlefish_config.h"
33 
34 namespace cuttlefish {
35 namespace {
36 
37 constexpr std::string_view kAndroidSparseImageMagic = "\x3A\xFF\x26\xED";
38 
AcquireLock(const std::string & tmp_lock_image_path)39 Result<SharedFD> AcquireLock(const std::string& tmp_lock_image_path) {
40   SharedFD fd =
41       SharedFD::Open(tmp_lock_image_path.c_str(), O_RDWR | O_CREAT, 0666);
42   CF_EXPECTF(fd->IsOpen(), "Failed to open '{}': '{}'", tmp_lock_image_path,
43              fd->StrError());
44 
45   CF_EXPECT(fd->Flock(LOCK_EX));
46 
47   return fd;
48 }
49 
IsSparseImage(const std::string & image_path)50 Result<bool> IsSparseImage(const std::string& image_path) {
51   std::ifstream file(image_path, std::ios::binary);
52   CF_EXPECTF(file.good(), "Could not open '{}'", image_path);
53 
54   std::string buffer(4, ' ');
55   file.read(buffer.data(), 4);
56 
57   return buffer == kAndroidSparseImageMagic;
58 }
59 
60 }  // namespace
61 
ForceRawImage(const std::string & image_path)62 Result<void> ForceRawImage(const std::string& image_path) {
63   std::string tmp_lock_image_path = image_path + ".lock";
64 
65   SharedFD fd = CF_EXPECT(AcquireLock(tmp_lock_image_path));
66 
67   if (!CF_EXPECT(IsSparseImage(image_path))) {
68     return {};
69   }
70 
71   std::string tmp_raw_image_path = image_path + ".raw";
72   // Use simg2img to convert sparse image to raw images.
73   int simg2img_status =
74       Execute({HostBinaryPath("simg2img"), image_path, tmp_raw_image_path});
75 
76   CF_EXPECT_EQ(simg2img_status, 0,
77                "Unable to convert Android sparse image '"
78                    << image_path << "' to raw image: " << simg2img_status);
79 
80   // Replace the original sparse image with the raw image.
81   // `rename` can fail if these are on different mounts, but they are files
82   // within the same directory so they can only be in different mounts if one
83   // is a bind mount, in which case `rename` won't work anyway.
84   CF_EXPECTF(rename(tmp_raw_image_path.c_str(), image_path.c_str()) == 0,
85              "rename('{}','{}') failed: {}", tmp_raw_image_path, image_path,
86              strerror(errno));
87 
88   return {};
89 }
90 
91 }  // namespace cuttlefish
92