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
26 #include "common/libs/fs/shared_fd.h"
27 #include "common/libs/fs/shared_select.h"
28 #include "common/libs/utils/subprocess.h"
29 #include "host/libs/config/cuttlefish_config.h"
30
31
32 const char ANDROID_SPARSE_IMAGE_MAGIC[] = "\x3A\xFF\x26\xED";
33 namespace cuttlefish {
34
ReleaseLock(const SharedFD & fd,const std::string & tmp_lock_image_path)35 void ReleaseLock(const SharedFD& fd,
36 const std::string& tmp_lock_image_path) {
37 auto funlock_result = fd->Flock(LOCK_UN | LOCK_NB);
38 fd->Close();
39 if (!funlock_result.ok()) {
40 LOG(FATAL) << "It failed to unlock file " << tmp_lock_image_path;
41 }
42 }
43
AcquireLock(SharedFD & fd,const std::string & tmp_lock_image_path)44 bool AcquireLock(SharedFD& fd, const std::string& tmp_lock_image_path) {
45 fd = SharedFD::Open(tmp_lock_image_path.c_str(),
46 O_RDWR | O_CREAT, 0666);
47 if (!fd->IsOpen()) {
48 LOG(FATAL) << tmp_lock_image_path << " file open failed";
49 return false;
50 }
51 auto flock_result = fd->Flock(LOCK_EX);
52 if (!flock_result.ok()) {
53 LOG(FATAL) << "flock failed";
54 return false;
55 }
56 return true;
57 }
58
IsSparseImage(const std::string & image_path)59 bool IsSparseImage(const std::string& image_path) {
60 std::ifstream file(image_path, std::ios::binary);
61 if (!file) {
62 LOG(FATAL) << "Could not open '" << image_path << "'";
63 return false;
64 }
65 char buffer[5] = {0};
66 file.read(buffer, 4);
67 file.close();
68 return strcmp(ANDROID_SPARSE_IMAGE_MAGIC, buffer) == 0;
69 }
70
ConvertToRawImage(const std::string & image_path)71 bool ConvertToRawImage(const std::string& image_path) {
72 SharedFD fd;
73 std::string tmp_lock_image_path = image_path + ".lock";
74
75 if(AcquireLock(fd, tmp_lock_image_path) == false) {
76 return false;
77 }
78
79 if (!IsSparseImage(image_path)) {
80 // Release lock before return
81 LOG(DEBUG) << "Skip non-sparse image " << image_path;
82 return false;
83 }
84
85 auto simg2img_path = HostBinaryPath("simg2img");
86 Command simg2img_cmd(simg2img_path);
87 std::string tmp_raw_image_path = image_path + ".raw";
88 simg2img_cmd.AddParameter(image_path);
89 simg2img_cmd.AddParameter(tmp_raw_image_path);
90
91 // Use simg2img to convert sparse image to raw image.
92 int success = simg2img_cmd.Start().Wait();
93 if (success != 0) {
94 // Release lock before FATAL and return
95 LOG(FATAL) << "Unable to convert Android sparse image " << image_path
96 << " to raw image. " << success;
97 return false;
98 }
99
100 // Replace the original sparse image with the raw image.
101 if (unlink(image_path.c_str()) != 0) {
102 // Release lock before FATAL and return
103 PLOG(FATAL) << "Unable to delete original sparse image";
104 }
105
106 Command mv_cmd("/bin/mv");
107 mv_cmd.AddParameter("-f");
108 mv_cmd.AddParameter(tmp_raw_image_path);
109 mv_cmd.AddParameter(image_path);
110 success = mv_cmd.Start().Wait();
111 // Release lock and leave critical section
112 ReleaseLock(fd, tmp_lock_image_path);
113 if (success != 0) {
114 LOG(FATAL) << "Unable to rename raw image " << success;
115 return false;
116 }
117
118 return true;
119 }
120
121 } // namespace cuttlefish
122