Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: src/platform/update_engine/filesystem_copier_action.cc

Issue 1700018: AU: FilesystemCopierAction: copy bit-exactly (Closed) Base URL: ssh://git@chromiumos-git/chromeos
Patch Set: fixes for review Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "update_engine/filesystem_copier_action.h" 5 #include "update_engine/filesystem_copier_action.h"
6 #include <sys/stat.h> 6 #include <sys/stat.h>
7 #include <sys/types.h> 7 #include <sys/types.h>
8 #include <errno.h> 8 #include <errno.h>
9 #include <fcntl.h> 9 #include <fcntl.h>
10 #include <stdlib.h> 10 #include <stdlib.h>
11 #include <algorithm> 11 #include <algorithm>
12 #include <map> 12 #include <map>
13 #include <string> 13 #include <string>
14 #include <vector> 14 #include <vector>
15 #include <gio/gio.h>
16 #include <gio/gunixinputstream.h>
17 #include <gio/gunixoutputstream.h>
18 #include <glib.h>
15 #include "update_engine/filesystem_iterator.h" 19 #include "update_engine/filesystem_iterator.h"
16 #include "update_engine/subprocess.h" 20 #include "update_engine/subprocess.h"
17 #include "update_engine/utils.h" 21 #include "update_engine/utils.h"
18 22
19 using std::map; 23 using std::map;
20 using std::min; 24 using std::min;
21 using std::string; 25 using std::string;
22 using std::vector; 26 using std::vector;
23 27
24 namespace chromeos_update_engine { 28 namespace chromeos_update_engine {
25 29
26 namespace { 30 namespace {
27 const char* kMountpointTemplate = "/tmp/au_dest_mnt.XXXXXX"; 31 const off_t kCopyFileBufferSize = 2 * 1024 * 1024;
28 const off_t kCopyFileBufferSize = 4 * 1024 * 1024;
29 const char* kCopyExclusionPrefix = "/lost+found";
30 } // namespace {} 32 } // namespace {}
31 33
32 void FilesystemCopierAction::PerformAction() { 34 void FilesystemCopierAction::PerformAction() {
35 // Will tell the ActionProcessor we've failed if we return.
36 ScopedActionCompleter abort_action_completer(processor_, this);
37
33 if (!HasInputObject()) { 38 if (!HasInputObject()) {
34 LOG(ERROR) << "No input object. Aborting."; 39 LOG(ERROR) << "FilesystemCopierAction missing input object.";
35 processor_->ActionComplete(this, false);
36 return; 40 return;
37 } 41 }
38 install_plan_ = GetInputObject(); 42 install_plan_ = GetInputObject();
39 43
40 if (install_plan_.is_full_update) { 44 if (install_plan_.is_full_update) {
41 // No copy needed. 45 // No copy needed. Done!
42 processor_->ActionComplete(this, true); 46 abort_action_completer.set_success(true);
43 return; 47 return;
44 } 48 }
45 49
46 { 50 const string source =
47 // Set up dest_path_ 51 copy_source_.empty() ? utils::BootDevice() : copy_source_;
48 char *dest_path_temp = strdup(kMountpointTemplate); 52 LOG(INFO) << "Copying from " << source << " to "
49 CHECK(dest_path_temp); 53 << install_plan_.install_path;
50 CHECK_EQ(mkdtemp(dest_path_temp), dest_path_temp); 54
51 CHECK_NE(dest_path_temp[0], '\0'); 55 int src_fd = open(source.c_str(), O_RDONLY);
52 dest_path_ = dest_path_temp; 56 if (src_fd < 0) {
53 free(dest_path_temp); 57 PLOG(ERROR) << "Unable to open " << source << " for reading:";
58 return;
59 }
60 int dst_fd = open(install_plan_.install_path.c_str(),
61 O_WRONLY | O_TRUNC | O_CREAT,
62 0644);
63 if (dst_fd < 0) {
64 close(src_fd);
65 PLOG(ERROR) << "Unable to open " << install_plan_.install_path
66 << " for writing:";
67 return;
54 } 68 }
55 69
56 // Make sure we're properly mounted 70 src_stream_ = g_unix_input_stream_new(src_fd, TRUE);
57 if (Mount(install_plan_.install_path, dest_path_)) { 71 dst_stream_ = g_unix_output_stream_new(dst_fd, TRUE);
58 bool done_early = false; 72
59 if (utils::FileExists( 73 buffer_.resize(kCopyFileBufferSize);
60 (dest_path_ + 74
61 FilesystemCopierAction::kCompleteFilesystemMarker).c_str())) { 75 // Set up the first read
62 // We're done! 76 canceller_ = g_cancellable_new();
63 done_early = true; 77
64 skipped_copy_ = true; 78 g_input_stream_read_async(src_stream_,
65 if (HasOutputPipe()) 79 &buffer_[0],
66 SetOutputObject(install_plan_); 80 buffer_.size(),
67 } 81 G_PRIORITY_DEFAULT,
68 if (!Unmount(dest_path_)) { 82 canceller_,
69 LOG(ERROR) << "Unmount failed. Aborting."; 83 &FilesystemCopierAction::StaticAsyncReadyCallback,
70 processor_->ActionComplete(this, false); 84 this);
71 return; 85 read_in_flight_ = true;
72 } 86
73 if (done_early) { 87 abort_action_completer.set_should_complete(false);
74 CHECK(!is_mounted_);
75 if (rmdir(dest_path_.c_str()) != 0)
76 LOG(ERROR) << "Unable to remove " << dest_path_;
77 processor_->ActionComplete(this, true);
78 return;
79 }
80 }
81 LOG(ERROR) << "not mounted; spawning thread";
82 // If we get here, mount failed or we're not done yet. Reformat and copy.
83 CHECK_EQ(pthread_create(&helper_thread_, NULL, HelperThreadMainStatic, this),
84 0);
85 } 88 }
86 89
87 void FilesystemCopierAction::TerminateProcessing() { 90 void FilesystemCopierAction::TerminateProcessing() {
88 if (is_mounted_) { 91 if (canceller_) {
89 LOG(ERROR) << "Aborted processing, but left a filesystem mounted."; 92 g_cancellable_cancel(canceller_);
90 } 93 }
91 } 94 }
92 95
93 bool FilesystemCopierAction::Mount(const string& device, 96 void FilesystemCopierAction::Cleanup(bool success, bool was_cancelled) {
94 const string& mountpoint) { 97 g_object_unref(src_stream_);
95 CHECK(!is_mounted_); 98 src_stream_ = NULL;
96 if(utils::MountFilesystem(device, mountpoint, 0)) 99 g_object_unref(dst_stream_);
97 is_mounted_ = true; 100 dst_stream_ = NULL;
98 return is_mounted_; 101 if (was_cancelled)
99 } 102 return;
100 103 if (success && HasOutputPipe())
101 bool FilesystemCopierAction::Unmount(const string& mountpoint) {
102 CHECK(is_mounted_);
103 if (utils::UnmountFilesystem(mountpoint))
104 is_mounted_ = false;
105 return !is_mounted_;
106 }
107
108 void* FilesystemCopierAction::HelperThreadMain() {
109 // First, format the drive
110 vector<string> cmd;
111 cmd.push_back("/sbin/mkfs.ext3");
112 cmd.push_back("-F");
113 cmd.push_back(install_plan_.install_path);
114 int return_code = 1;
115 bool success = Subprocess::SynchronousExec(cmd, &return_code);
116 if (return_code != 0) {
117 LOG(INFO) << "Format of " << install_plan_.install_path
118 << " failed. Exit code: " << return_code;
119 success = false;
120 }
121 if (success) {
122 if (!Mount(install_plan_.install_path, dest_path_)) {
123 LOG(ERROR) << "Mount failed. Aborting";
124 success = false;
125 }
126 }
127 if (success) {
128 success = CopySynchronously();
129 }
130 if (success) {
131 // Place our marker to avoid copies again in the future
132 int r = open((dest_path_ +
133 FilesystemCopierAction::kCompleteFilesystemMarker).c_str(),
134 O_CREAT | O_WRONLY, 0644);
135 if (r >= 0)
136 close(r);
137 }
138 // Unmount
139 if (!Unmount(dest_path_)) {
140 LOG(ERROR) << "Unmount failed. Aborting";
141 success = false;
142 }
143 if (HasOutputPipe())
144 SetOutputObject(install_plan_); 104 SetOutputObject(install_plan_);
145
146 // Tell main thread that we're done
147 g_timeout_add(0, CollectThreadStatic, this);
148 return reinterpret_cast<void*>(success ? 0 : 1);
149 }
150
151 void FilesystemCopierAction::CollectThread() {
152 void *thread_ret_value = NULL;
153 CHECK_EQ(pthread_join(helper_thread_, &thread_ret_value), 0);
154 bool success = (thread_ret_value == 0);
155 CHECK(!is_mounted_);
156 if (rmdir(dest_path_.c_str()) != 0)
157 LOG(INFO) << "Unable to remove " << dest_path_;
158 LOG(INFO) << "FilesystemCopierAction done";
159 processor_->ActionComplete(this, success); 105 processor_->ActionComplete(this, success);
160 } 106 }
161 107
162 bool FilesystemCopierAction::CreateDirSynchronously(const std::string& new_path, 108 void FilesystemCopierAction::AsyncReadyCallback(GObject *source_object,
163 const struct stat& stbuf) { 109 GAsyncResult *res) {
164 int r = mkdir(new_path.c_str(), stbuf.st_mode); 110 GError* error = NULL;
165 TEST_AND_RETURN_FALSE_ERRNO(r == 0); 111 CHECK(canceller_);
166 return true; 112 bool was_cancelled = g_cancellable_is_cancelled(canceller_) == TRUE;
113 g_object_unref(canceller_);
114 canceller_ = NULL;
115
116 if (read_in_flight_) {
117 ssize_t bytes_read = g_input_stream_read_finish(src_stream_, res, &error);
118 if (bytes_read < 0) {
119 LOG(ERROR) << "Read failed:" << utils::GetGErrorMessage(error);
120 Cleanup(false, was_cancelled);
121 return;
122 }
123
124 if (bytes_read == 0) {
125 // We're done!
126 Cleanup(true, was_cancelled);
127 return;
128 }
129 // Kick off a write
130 read_in_flight_ = false;
131 buffer_valid_size_ = bytes_read;
132 canceller_ = g_cancellable_new();
133 g_output_stream_write_async(
134 dst_stream_,
135 &buffer_[0],
136 bytes_read,
137 G_PRIORITY_DEFAULT,
138 canceller_,
139 &FilesystemCopierAction::StaticAsyncReadyCallback,
140 this);
141 return;
142 }
143
144 ssize_t bytes_written = g_output_stream_write_finish(dst_stream_,
145 res,
146 &error);
147 if (bytes_written < static_cast<ssize_t>(buffer_valid_size_)) {
148 if (bytes_written < 0) {
149 LOG(ERROR) << "Write failed:" << utils::GetGErrorMessage(error);
150 } else {
151 LOG(ERROR) << "Write was short: wrote " << bytes_written
152 << " but expected to write " << buffer_valid_size_;
153 }
154 Cleanup(false, was_cancelled);
155 return;
156 }
157
158 // Kick off a read
159 read_in_flight_ = true;
160 canceller_ = g_cancellable_new();
161 g_input_stream_read_async(
162 src_stream_,
163 &buffer_[0],
164 buffer_.size(),
165 G_PRIORITY_DEFAULT,
166 canceller_,
167 &FilesystemCopierAction::StaticAsyncReadyCallback,
168 this);
167 } 169 }
168 170
169 bool FilesystemCopierAction::CopyFileSynchronously(const std::string& old_path,
170 const std::string& new_path,
171 const struct stat& stbuf) {
172 int fd_out = open(new_path.c_str(), O_CREAT | O_EXCL | O_WRONLY,
173 stbuf.st_mode);
174 TEST_AND_RETURN_FALSE_ERRNO(fd_out >= 0);
175 ScopedFdCloser fd_out_closer(&fd_out);
176 int fd_in = open(old_path.c_str(), O_RDONLY, 0);
177 TEST_AND_RETURN_FALSE_ERRNO(fd_in >= 0);
178 ScopedFdCloser fd_in_closer(&fd_in);
179
180 vector<char> buf(min(kCopyFileBufferSize, stbuf.st_size));
181 off_t bytes_written = 0;
182 while (true) {
183 // Make sure we don't need to abort early:
184 TEST_AND_RETURN_FALSE(!g_atomic_int_get(&thread_should_exit_));
185
186 ssize_t read_size = read(fd_in, &buf[0], buf.size());
187 TEST_AND_RETURN_FALSE_ERRNO(read_size >= 0);
188 if (0 == read_size) // EOF
189 break;
190
191 ssize_t write_size = 0;
192 while (write_size < read_size) {
193 ssize_t r = write(fd_out, &buf[write_size], read_size - write_size);
194 TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
195 write_size += r;
196 }
197 CHECK_EQ(write_size, read_size);
198 bytes_written += write_size;
199 CHECK_LE(bytes_written, stbuf.st_size);
200 if (bytes_written == stbuf.st_size)
201 break;
202 }
203 CHECK_EQ(bytes_written, stbuf.st_size);
204 return true;
205 }
206
207 bool FilesystemCopierAction::CreateHardLinkSynchronously(
208 const std::string& old_path,
209 const std::string& new_path) {
210 int r = link(old_path.c_str(), new_path.c_str());
211 TEST_AND_RETURN_FALSE_ERRNO(r == 0);
212 return true;
213 }
214
215 bool FilesystemCopierAction::CopySymlinkSynchronously(
216 const std::string& old_path,
217 const std::string& new_path,
218 const struct stat& stbuf) {
219 vector<char> buf(PATH_MAX + 1);
220 ssize_t r = readlink(old_path.c_str(), &buf[0], buf.size());
221 TEST_AND_RETURN_FALSE_ERRNO(r >= 0);
222 // Make sure we got the entire link
223 TEST_AND_RETURN_FALSE(static_cast<unsigned>(r) < buf.size());
224 buf[r] = '\0';
225 int rc = symlink(&buf[0], new_path.c_str());
226 TEST_AND_RETURN_FALSE_ERRNO(rc == 0);
227 return true;
228 }
229
230 bool FilesystemCopierAction::CreateNodeSynchronously(
231 const std::string& new_path,
232 const struct stat& stbuf) {
233 int r = mknod(new_path.c_str(), stbuf.st_mode, stbuf.st_rdev);
234 TEST_AND_RETURN_FALSE_ERRNO(r == 0);
235 return true;
236 }
237
238 // Returns true on success
239 bool FilesystemCopierAction::CopySynchronously() {
240 // This map is a map from inode # to new_path.
241 map<ino_t, string> hard_links;
242 FilesystemIterator iter(copy_source_,
243 utils::SetWithValue<string>(kCopyExclusionPrefix));
244 bool success = true;
245 for (; !g_atomic_int_get(&thread_should_exit_) &&
246 !iter.IsEnd(); iter.Increment()) {
247 const string old_path = iter.GetFullPath();
248 const string new_path = dest_path_ + iter.GetPartialPath();
249 LOG(INFO) << "copying " << old_path << " to " << new_path;
250 const struct stat stbuf = iter.GetStat();
251 success = false;
252
253 // Skip lost+found
254 CHECK_NE(kCopyExclusionPrefix, iter.GetPartialPath());
255
256 // Directories can't be hard-linked, so check for directories first
257 if (iter.GetPartialPath().empty()) {
258 // Root has an empty path.
259 // We don't need to create anything for the root, which is the first
260 // thing we get from the iterator.
261 success = true;
262 } else if (S_ISDIR(stbuf.st_mode)) {
263 success = CreateDirSynchronously(new_path, stbuf);
264 } else {
265 if (stbuf.st_nlink > 1 &&
266 utils::MapContainsKey(hard_links, stbuf.st_ino)) {
267 success = CreateHardLinkSynchronously(hard_links[stbuf.st_ino],
268 new_path);
269 } else {
270 if (stbuf.st_nlink > 1)
271 hard_links[stbuf.st_ino] = new_path;
272 if (S_ISREG(stbuf.st_mode)) {
273 success = CopyFileSynchronously(old_path, new_path, stbuf);
274 } else if (S_ISLNK(stbuf.st_mode)) {
275 success = CopySymlinkSynchronously(old_path, new_path, stbuf);
276 } else if (S_ISFIFO(stbuf.st_mode) ||
277 S_ISCHR(stbuf.st_mode) ||
278 S_ISBLK(stbuf.st_mode) ||
279 S_ISSOCK(stbuf.st_mode)) {
280 success = CreateNodeSynchronously(new_path, stbuf);
281 } else {
282 CHECK(false) << "Unable to copy file " << old_path << " with mode "
283 << stbuf.st_mode;
284 }
285 }
286 }
287 TEST_AND_RETURN_FALSE(success);
288
289 // chmod new file
290 if (!S_ISLNK(stbuf.st_mode)) {
291 int r = chmod(new_path.c_str(), stbuf.st_mode);
292 TEST_AND_RETURN_FALSE_ERRNO(r == 0);
293 }
294
295 // Set uid/gid.
296 int r = lchown(new_path.c_str(), stbuf.st_uid, stbuf.st_gid);
297 TEST_AND_RETURN_FALSE_ERRNO(r == 0);
298 }
299 TEST_AND_RETURN_FALSE(!iter.IsErr());
300 // Success!
301 return true;
302 }
303
304 const char* FilesystemCopierAction::kCompleteFilesystemMarker(
305 "/update_engine_copy_success");
306
307 } // namespace chromeos_update_engine 171 } // namespace chromeos_update_engine
OLDNEW
« no previous file with comments | « src/platform/update_engine/filesystem_copier_action.h ('k') | src/platform/update_engine/filesystem_copier_action_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698