blob: 4c5a195cf4982246ce06d1c68f1ced5a12856b5a [file] [log] [blame]
Darin Petkov296889c2010-07-23 16:20:54 -07001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/utils.h"
6#include <sys/mount.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9#include <dirent.h>
10#include <errno.h>
Andrew de los Reyes970bb282009-12-09 16:34:04 -080011#include <fcntl.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <unistd.h>
16#include <algorithm>
17#include "chromeos/obsolete_logging.h"
Andrew de los Reyes970bb282009-12-09 16:34:04 -080018#include "update_engine/file_writer.h"
Darin Petkov296889c2010-07-23 16:20:54 -070019#include "update_engine/subprocess.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000020
21using std::min;
22using std::string;
23using std::vector;
24
25namespace chromeos_update_engine {
26
27namespace utils {
28
Andrew de los Reyes970bb282009-12-09 16:34:04 -080029bool WriteFile(const char* path, const char* data, int data_len) {
30 DirectFileWriter writer;
31 TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
32 O_WRONLY | O_CREAT | O_TRUNC,
33 0666));
34 ScopedFileWriterCloser closer(&writer);
35 TEST_AND_RETURN_FALSE_ERRNO(data_len == writer.Write(data, data_len));
36 return true;
37}
38
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070039bool WriteAll(int fd, const void* buf, size_t count) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070040 const char* c_buf = static_cast<const char*>(buf);
41 ssize_t bytes_written = 0;
42 while (bytes_written < static_cast<ssize_t>(count)) {
43 ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
44 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
45 bytes_written += rc;
46 }
47 return true;
48}
49
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070050bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
51 const char* c_buf = static_cast<const char*>(buf);
52 ssize_t bytes_written = 0;
53 while (bytes_written < static_cast<ssize_t>(count)) {
54 ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
55 offset + bytes_written);
56 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
57 bytes_written += rc;
58 }
59 return true;
60}
61
62bool PReadAll(int fd, void* buf, size_t count, off_t offset,
63 ssize_t* out_bytes_read) {
64 char* c_buf = static_cast<char*>(buf);
65 ssize_t bytes_read = 0;
66 while (bytes_read < static_cast<ssize_t>(count)) {
67 ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
68 offset + bytes_read);
69 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
70 if (rc == 0) {
71 break;
72 }
73 bytes_read += rc;
74 }
75 *out_bytes_read = bytes_read;
76 return true;
Darin Petkov296889c2010-07-23 16:20:54 -070077
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070078}
79
adlr@google.com3defe6a2009-12-04 20:57:17 +000080bool ReadFile(const std::string& path, std::vector<char>* out) {
81 CHECK(out);
82 FILE* fp = fopen(path.c_str(), "r");
83 if (!fp)
84 return false;
85 const size_t kChunkSize = 1024;
86 size_t read_size;
87 do {
88 char buf[kChunkSize];
89 read_size = fread(buf, 1, kChunkSize, fp);
90 if (read_size == 0)
91 break;
92 out->insert(out->end(), buf, buf + read_size);
93 } while (read_size == kChunkSize);
94 bool success = !ferror(fp);
95 TEST_AND_RETURN_FALSE_ERRNO(fclose(fp) == 0);
96 return success;
97}
98
99bool ReadFileToString(const std::string& path, std::string* out) {
100 vector<char> data;
101 bool success = ReadFile(path, &data);
102 if (!success) {
103 return false;
104 }
105 (*out) = string(&data[0], data.size());
106 return true;
107}
108
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700109off_t FileSize(const string& path) {
110 struct stat stbuf;
111 int rc = stat(path.c_str(), &stbuf);
112 CHECK_EQ(rc, 0);
113 if (rc < 0)
114 return rc;
115 return stbuf.st_size;
116}
117
adlr@google.com3defe6a2009-12-04 20:57:17 +0000118void HexDumpArray(const unsigned char* const arr, const size_t length) {
119 const unsigned char* const char_arr =
120 reinterpret_cast<const unsigned char* const>(arr);
121 LOG(INFO) << "Logging array of length: " << length;
122 const unsigned int bytes_per_line = 16;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700123 for (uint32_t i = 0; i < length; i += bytes_per_line) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000124 const unsigned int bytes_remaining = length - i;
125 const unsigned int bytes_per_this_line = min(bytes_per_line,
126 bytes_remaining);
127 char header[100];
128 int r = snprintf(header, sizeof(header), "0x%08x : ", i);
129 TEST_AND_RETURN(r == 13);
130 string line = header;
131 for (unsigned int j = 0; j < bytes_per_this_line; j++) {
132 char buf[20];
133 unsigned char c = char_arr[i + j];
134 r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
135 TEST_AND_RETURN(r == 3);
136 line += buf;
137 }
138 LOG(INFO) << line;
139 }
140}
141
142namespace {
143class ScopedDirCloser {
144 public:
145 explicit ScopedDirCloser(DIR** dir) : dir_(dir) {}
146 ~ScopedDirCloser() {
147 if (dir_ && *dir_) {
148 int r = closedir(*dir_);
149 TEST_AND_RETURN_ERRNO(r == 0);
150 *dir_ = NULL;
151 dir_ = NULL;
152 }
153 }
154 private:
155 DIR** dir_;
156};
157} // namespace {}
158
159bool RecursiveUnlinkDir(const std::string& path) {
160 struct stat stbuf;
161 int r = lstat(path.c_str(), &stbuf);
162 TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT));
163 if ((r < 0) && (errno == ENOENT))
164 // path request is missing. that's fine.
165 return true;
166 if (!S_ISDIR(stbuf.st_mode)) {
167 TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) ||
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700168 (errno == ENOENT));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000169 // success or path disappeared before we could unlink.
170 return true;
171 }
172 {
173 // We have a dir, unlink all children, then delete dir
174 DIR *dir = opendir(path.c_str());
175 TEST_AND_RETURN_FALSE_ERRNO(dir);
176 ScopedDirCloser dir_closer(&dir);
177 struct dirent dir_entry;
178 struct dirent *dir_entry_p;
179 int err = 0;
180 while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) {
181 if (dir_entry_p == NULL) {
182 // end of stream reached
183 break;
184 }
185 // Skip . and ..
186 if (!strcmp(dir_entry_p->d_name, ".") ||
187 !strcmp(dir_entry_p->d_name, ".."))
188 continue;
189 TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" +
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700190 dir_entry_p->d_name));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000191 }
192 TEST_AND_RETURN_FALSE(err == 0);
193 }
194 // unlink dir
195 TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT));
196 return true;
197}
198
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700199string RootDevice(const string& partition_device) {
200 CHECK(!partition_device.empty());
201 string::const_iterator it = --partition_device.end();
202 for (; it >= partition_device.begin(); --it) {
203 if (!isdigit(*it))
204 break;
205 }
206 // Some devices contain a p before the partitions. For example:
207 // /dev/mmc0p4 should be shortened to /dev/mmc0.
208 if (*it == 'p')
209 --it;
210 return string(partition_device.begin(), it + 1);
211}
212
213string PartitionNumber(const string& partition_device) {
214 CHECK(!partition_device.empty());
215 string::const_iterator it = --partition_device.end();
216 for (; it >= partition_device.begin(); --it) {
217 if (!isdigit(*it))
218 break;
219 }
220 return string(it + 1, partition_device.end());
221}
222
adlr@google.com3defe6a2009-12-04 20:57:17 +0000223std::string ErrnoNumberAsString(int err) {
224 char buf[100];
225 buf[0] = '\0';
226 return strerror_r(err, buf, sizeof(buf));
227}
228
229std::string NormalizePath(const std::string& path, bool strip_trailing_slash) {
230 string ret;
231 bool last_insert_was_slash = false;
232 for (string::const_iterator it = path.begin(); it != path.end(); ++it) {
233 if (*it == '/') {
234 if (last_insert_was_slash)
235 continue;
236 last_insert_was_slash = true;
237 } else {
238 last_insert_was_slash = false;
239 }
240 ret.push_back(*it);
241 }
242 if (strip_trailing_slash && last_insert_was_slash) {
243 string::size_type last_non_slash = ret.find_last_not_of('/');
244 if (last_non_slash != string::npos) {
245 ret.resize(last_non_slash + 1);
246 } else {
247 ret = "";
248 }
249 }
250 return ret;
251}
252
253bool FileExists(const char* path) {
254 struct stat stbuf;
255 return 0 == lstat(path, &stbuf);
256}
257
258std::string TempFilename(string path) {
259 static const string suffix("XXXXXX");
260 CHECK(StringHasSuffix(path, suffix));
261 do {
262 string new_suffix;
263 for (unsigned int i = 0; i < suffix.size(); i++) {
264 int r = rand() % (26 * 2 + 10); // [a-zA-Z0-9]
265 if (r < 26)
266 new_suffix.append(1, 'a' + r);
267 else if (r < (26 * 2))
268 new_suffix.append(1, 'A' + r - 26);
269 else
270 new_suffix.append(1, '0' + r - (26 * 2));
271 }
272 CHECK_EQ(new_suffix.size(), suffix.size());
273 path.resize(path.size() - new_suffix.size());
274 path.append(new_suffix);
275 } while (FileExists(path.c_str()));
276 return path;
277}
278
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700279bool MakeTempFile(const std::string& filename_template,
280 std::string* filename,
281 int* fd) {
282 DCHECK(filename || fd);
283 vector<char> buf(filename_template.size() + 1);
284 memcpy(&buf[0], filename_template.data(), filename_template.size());
285 buf[filename_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700286
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700287 int mkstemp_fd = mkstemp(&buf[0]);
288 TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
289 if (filename) {
290 *filename = &buf[0];
291 }
292 if (fd) {
293 *fd = mkstemp_fd;
294 } else {
295 close(mkstemp_fd);
296 }
297 return true;
298}
299
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700300bool MakeTempDirectory(const std::string& dirname_template,
301 std::string* dirname) {
302 DCHECK(dirname);
303 vector<char> buf(dirname_template.size() + 1);
304 memcpy(&buf[0], dirname_template.data(), dirname_template.size());
305 buf[dirname_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700306
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700307 char* return_code = mkdtemp(&buf[0]);
308 TEST_AND_RETURN_FALSE_ERRNO(return_code != NULL);
309 *dirname = &buf[0];
310 return true;
311}
312
adlr@google.com3defe6a2009-12-04 20:57:17 +0000313bool StringHasSuffix(const std::string& str, const std::string& suffix) {
314 if (suffix.size() > str.size())
315 return false;
316 return 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
317}
318
319bool StringHasPrefix(const std::string& str, const std::string& prefix) {
320 if (prefix.size() > str.size())
321 return false;
322 return 0 == str.compare(0, prefix.size(), prefix);
323}
324
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700325const string BootDevice() {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000326 string proc_cmdline;
327 if (!ReadFileToString("/proc/cmdline", &proc_cmdline))
328 return "";
329 // look for "root=" in the command line
330 string::size_type pos = 0;
331 if (!StringHasPrefix(proc_cmdline, "root=")) {
332 pos = proc_cmdline.find(" root=") + 1;
333 }
334 if (pos == string::npos) {
335 // can't find root=
336 return "";
337 }
338 // at this point, pos is the point in the string where "root=" starts
339 string ret;
340 pos += strlen("root="); // advance to the device name itself
341 while (pos < proc_cmdline.size()) {
342 char c = proc_cmdline[pos];
343 if (c == ' ')
344 break;
345 ret += c;
346 pos++;
347 }
348 return ret;
349 // TODO(adlr): use findfs to figure out UUID= or LABEL= filesystems
350}
351
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700352const string BootKernelDevice(const std::string& boot_device) {
353 // Currntly this assumes the last digit of the boot device is
354 // 3, 5, or 7, and changes it to 2, 4, or 6, respectively, to
355 // get the kernel device.
356 string ret = boot_device;
357 if (ret.empty())
358 return ret;
359 char last_char = ret[ret.size() - 1];
360 if (last_char == '3' || last_char == '5' || last_char == '7') {
361 ret[ret.size() - 1] = last_char - 1;
362 return ret;
363 }
364 return "";
365}
366
adlr@google.com3defe6a2009-12-04 20:57:17 +0000367bool MountFilesystem(const string& device,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700368 const string& mountpoint,
369 unsigned long mountflags) {
370 int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", mountflags, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000371 if (rc < 0) {
372 string msg = ErrnoNumberAsString(errno);
373 LOG(ERROR) << "Unable to mount destination device: " << msg << ". "
374 << device << " on " << mountpoint;
375 return false;
376 }
377 return true;
378}
379
380bool UnmountFilesystem(const string& mountpoint) {
381 TEST_AND_RETURN_FALSE_ERRNO(umount(mountpoint.c_str()) == 0);
382 return true;
383}
384
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700385bool GetBootloader(BootLoader* out_bootloader) {
386 // For now, hardcode to syslinux.
387 *out_bootloader = BootLoader_SYSLINUX;
388 return true;
389}
390
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700391const char* GetGErrorMessage(const GError* error) {
392 if (!error)
393 return "Unknown error.";
394 return error->message;
395}
396
Darin Petkov296889c2010-07-23 16:20:54 -0700397bool Reboot() {
398 vector<string> command;
399 command.push_back("/sbin/shutdown");
400 command.push_back("-r");
401 command.push_back("now");
402 int rc = 0;
403 Subprocess::SynchronousExec(command, &rc);
404 TEST_AND_RETURN_FALSE(rc == 0);
405 return true;
406}
407
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800408const char* const kStatefulPartition = "/mnt/stateful_partition";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000409
410} // namespace utils
411
412} // namespace chromeos_update_engine