blob: ee6052f554c9394a3f5b39922fcbe2ae565aac08 [file] [log] [blame]
tbarzic340a4aa2011-06-09 20:27:58 -07001// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Ben Chan617dbb82014-06-17 22:46:34 -07005#include "image-burner/image_burner_utils.h"
tbarzic340a4aa2011-06-09 20:27:58 -07006
Ben Chan58869eb2017-03-20 12:40:33 -07007#include <memory>
8
Toni Barzicaced0132020-10-19 20:06:27 -07009#include <base/bind.h>
Toni Barzic9345c102016-09-09 17:36:01 -070010#include <base/files/file_path.h>
tbarzic340a4aa2011-06-09 20:27:58 -070011#include <base/logging.h>
Ben Chan58869eb2017-03-20 12:40:33 -070012#include <base/memory/free_deleter.h>
Ben Chandbace9f2017-04-04 15:30:44 -070013#include <base/strings/stringprintf.h>
Ben Chan617dbb82014-06-17 22:46:34 -070014#include <rootdev/rootdev.h>
tbarzic340a4aa2011-06-09 20:27:58 -070015
16namespace imageburn {
Ben Chandbace9f2017-04-04 15:30:44 -070017namespace {
tbarzic340a4aa2011-06-09 20:27:58 -070018
tbarzic340a4aa2011-06-09 20:27:58 -070019const int kFsyncRatio = 1024;
20
Ben Chandbace9f2017-04-04 15:30:44 -070021bool RealPath(const char* path, std::string* real_path) {
22 std::unique_ptr<char, base::FreeDeleter> result(realpath(path, nullptr));
23 if (!result) {
24 PLOG(ERROR) << "Couldn't get real path of " << path;
25 return false;
26 }
27 *real_path = result.get();
28 return true;
29}
30
31} // namespace
32
Toni Barzicaced0132020-10-19 20:06:27 -070033BurnWriter::BurnWriter()
34 : fstat_callback_(base::BindRepeating(&base::File::Fstat)) {}
tbarzic340a4aa2011-06-09 20:27:58 -070035
36bool BurnWriter::Open(const char* path) {
Toni Barzic9345c102016-09-09 17:36:01 -070037 if (file_.IsValid())
38 return false;
39
40 file_.Initialize(base::FilePath(path),
41 base::File::FLAG_WRITE | base::File::FLAG_OPEN);
42 if (!file_.IsValid()) {
Ben Chan617dbb82014-06-17 22:46:34 -070043 PLOG(ERROR) << "Couldn't open target path " << path;
tbarzic340a4aa2011-06-09 20:27:58 -070044 return false;
tbarzic340a4aa2011-06-09 20:27:58 -070045 }
Toni Barzicaced0132020-10-19 20:06:27 -070046
47 base::stat_wrapper_t st = {};
48 if (fstat_callback_.Run(file_.GetPlatformFile(), &st) != 0) {
49 PLOG(ERROR) << "Unable to stat file for path " << path;
50 Close();
51 return false;
52 }
53
54 if (!S_ISBLK(st.st_mode)) {
55 PLOG(ERROR) << "Attempt to write to non-block device " << path;
56 Close();
57 return false;
58 }
59
60 LOG(INFO) << path << " opened";
61 return true;
tbarzic340a4aa2011-06-09 20:27:58 -070062}
63
64bool BurnWriter::Close() {
Toni Barzic9345c102016-09-09 17:36:01 -070065 if (!file_.IsValid())
66 return false;
67 file_.Close();
tbarzic340a4aa2011-06-09 20:27:58 -070068 return true;
69}
70
Toni Barzic9345c102016-09-09 17:36:01 -070071int BurnWriter::Write(const char* data_block, int data_size) {
72 const int written = file_.WriteAtCurrentPos(data_block, data_size);
73 if (written != data_size) {
Ben Chan617dbb82014-06-17 22:46:34 -070074 PLOG(ERROR) << "Error writing to target file";
tbarzic340a4aa2011-06-09 20:27:58 -070075 return written;
76 }
77
Toni Barzic9345c102016-09-09 17:36:01 -070078 if (!writes_count_ && !file_.Flush()) {
79 PLOG(ERROR) << "Error flushing target file.";
tbarzic340a4aa2011-06-09 20:27:58 -070080 return -1;
81 }
Toni Barzic9345c102016-09-09 17:36:01 -070082
tbarzic340a4aa2011-06-09 20:27:58 -070083 writes_count_++;
84 if (writes_count_ == kFsyncRatio)
85 writes_count_ = 0;
86
87 return written;
88}
89
Toni Barzic9345c102016-09-09 17:36:01 -070090BurnReader::BurnReader() {}
tbarzic340a4aa2011-06-09 20:27:58 -070091
92bool BurnReader::Open(const char* path) {
Toni Barzic9345c102016-09-09 17:36:01 -070093 if (file_.IsValid())
94 return false;
95
96 file_.Initialize(base::FilePath(path),
97 base::File::FLAG_READ | base::File::FLAG_OPEN);
98 if (!file_.IsValid()) {
Ben Chan617dbb82014-06-17 22:46:34 -070099 PLOG(ERROR) << "Couldn't open source path " << path;
tbarzic340a4aa2011-06-09 20:27:58 -0700100 return false;
tbarzic340a4aa2011-06-09 20:27:58 -0700101 }
Ben Chandbace9f2017-04-04 15:30:44 -0700102
103 // Obtains the real path of the file associated with the opened file
104 // descriptor by resolving the /proc/self/fd/<descriptor> symlink. Compares
105 // |path| against the real path determined by /proc/self/fd/<descriptor> to
106 // make sure |path| specifies the real path and avoids a potential TOCTOU
107 // race between the underlying realpath() and open() call.
108 int fd = file_.GetPlatformFile();
109 std::string fd_path;
110 if (!RealPath(base::StringPrintf("/proc/self/fd/%d", fd).c_str(), &fd_path)) {
111 file_.Close();
112 return false;
113 }
114 if (fd_path != path) {
115 LOG(ERROR) << path << " isn't a fully resolved path";
116 file_.Close();
117 return false;
118 }
119
120 LOG(INFO) << path << " opened";
121 return true;
tbarzic340a4aa2011-06-09 20:27:58 -0700122}
123
124bool BurnReader::Close() {
Toni Barzic9345c102016-09-09 17:36:01 -0700125 if (!file_.IsValid())
126 return false;
127 file_.Close();
tbarzic340a4aa2011-06-09 20:27:58 -0700128 return true;
129}
130
131int BurnReader::Read(char* data_block, int data_size) {
Toni Barzic9345c102016-09-09 17:36:01 -0700132 const int read = file_.ReadAtCurrentPos(data_block, data_size);
tbarzic340a4aa2011-06-09 20:27:58 -0700133 if (read < 0)
Ben Chan617dbb82014-06-17 22:46:34 -0700134 PLOG(ERROR) << "Error reading from source file";
tbarzic340a4aa2011-06-09 20:27:58 -0700135 return read;
136}
137
Ben Chan26c33e72014-08-07 00:30:44 -0700138int64_t BurnReader::GetSize() {
Toni Barzic9345c102016-09-09 17:36:01 -0700139 if (!file_.IsValid())
140 return -1;
141 return file_.GetLength();
tbarzic340a4aa2011-06-09 20:27:58 -0700142}
143
Ben Chan58869eb2017-03-20 12:40:33 -0700144bool BurnPathGetter::GetRealPath(const char* path, std::string* real_path) {
Ben Chandbace9f2017-04-04 15:30:44 -0700145 return RealPath(path, real_path);
Ben Chan58869eb2017-03-20 12:40:33 -0700146}
Toni Barzic9345c102016-09-09 17:36:01 -0700147
Ben Chan58869eb2017-03-20 12:40:33 -0700148bool BurnPathGetter::GetRootPath(std::string* path) {
tbarzic340a4aa2011-06-09 20:27:58 -0700149 char root_path[PATH_MAX];
150 if (rootdev(root_path, sizeof(root_path), true, true)) {
151 // Coult not get root path.
152 return false;
153 }
154 *path = root_path;
155 return true;
156}
157
Ben Chan617dbb82014-06-17 22:46:34 -0700158} // namespace imageburn