blob: 3a330efd5b3ed6b065c3da5bc01a94d538bd0caa [file] [log] [blame]
adlr@google.com3defe6a2009-12-04 20:57:17 +00001// Copyright (c) 2009 The Chromium 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
5#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__
6#define CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__
7
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <string>
11#include <glib.h>
12#include "update_engine/action.h"
13#include "update_engine/install_plan.h"
14
15// This action will only do real work if it's a delta update. It will
16// format the install partition as ext3/4, copy the root filesystem into it,
17// and then terminate.
18
19// Implementation notes: This action uses a helper thread, which seems to
20// violate the design decision to only have a single thread and use
21// asynchronous i/o. The issue is that (to the best of my knowledge),
22// there are no linux APIs to crawl a filesystem's metadata asynchronously.
23// The suggested way seems to be to open the raw device and parse the ext
24// filesystem. That's not a good approach for a number of reasons:
25// - ties us to ext filesystem
26// - although this wouldn't happen at the time of writing, it may not handle
27// changes to the source fs during the copy as gracefully.
28// - requires us to have read-access to the source filesystem device, which
29// may be a security issue.
30//
31// Having said this, using a helper thread is not ideal, but it's acceptable:
32// we still honor the Action API. That is, all interaction between the action
33// and other objects in the system (e.g. the ActionProcessor) happens on the
34// main thread. The helper thread is fully encapsulated by the action.
35
36namespace chromeos_update_engine {
37
38class FilesystemCopierAction;
39
40template<>
41class ActionTraits<FilesystemCopierAction> {
42 public:
43 // Takes the install plan as input
44 typedef InstallPlan InputObjectType;
45 // Passes the install plan as output
46 typedef InstallPlan OutputObjectType;
47};
48
49class FilesystemCopierAction : public Action<FilesystemCopierAction> {
50 public:
51 FilesystemCopierAction()
52 : thread_should_exit_(0),
53 is_mounted_(false),
54 copy_source_("/"),
55 skipped_copy_(false) {}
56 typedef ActionTraits<FilesystemCopierAction>::InputObjectType
57 InputObjectType;
58 typedef ActionTraits<FilesystemCopierAction>::OutputObjectType
59 OutputObjectType;
60 void PerformAction();
61 void TerminateProcessing();
62
63 // Used for testing, so we can copy from somewhere other than root
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080064 void set_copy_source(const std::string& path) {
adlr@google.com3defe6a2009-12-04 20:57:17 +000065 copy_source_ = path;
66 }
67 // Returns true if we detected that a copy was unneeded and thus skipped it.
68 bool skipped_copy() { return skipped_copy_; }
69
70 // Debugging/logging
71 static std::string StaticType() { return "FilesystemCopierAction"; }
72 std::string Type() const { return StaticType(); }
73
74 private:
75 // These synchronously mount or unmount the given mountpoint
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -080076 bool Mount(const std::string& device, const std::string& mountpoint);
77 bool Unmount(const std::string& mountpoint);
adlr@google.com3defe6a2009-12-04 20:57:17 +000078
79 // Performs a recursive file/directory copy from copy_source_ to dest_path_.
80 // Doesn't return until the copy has completed. Returns true on success
81 // or false on error.
82 bool CopySynchronously();
83
84 // There are helper functions for CopySynchronously. They handle creating
85 // various types of files. They return true on success.
86 bool CreateDirSynchronously(const std::string& new_path,
87 const struct stat& stbuf);
88 bool CopyFileSynchronously(const std::string& old_path,
89 const std::string& new_path,
90 const struct stat& stbuf);
91 bool CreateHardLinkSynchronously(const std::string& old_path,
92 const std::string& new_path);
93 // Note: Here, old_path is an existing symlink that will be copied to
94 // new_path. Thus, old_path is *not* the same as the old_path from
95 // the symlink() syscall.
96 bool CopySymlinkSynchronously(const std::string& old_path,
97 const std::string& new_path,
98 const struct stat& stbuf);
99 bool CreateNodeSynchronously(const std::string& new_path,
100 const struct stat& stbuf);
101
102 // Returns NULL on success
103 void* HelperThreadMain();
104 static void* HelperThreadMainStatic(void* data) {
105 FilesystemCopierAction* self =
106 reinterpret_cast<FilesystemCopierAction*>(data);
107 return self->HelperThreadMain();
108 }
109
110 // Joins the thread and tells the processor that we're done
111 void CollectThread();
112 // GMainLoop callback function:
113 static gboolean CollectThreadStatic(gpointer data) {
114 FilesystemCopierAction* self =
115 reinterpret_cast<FilesystemCopierAction*>(data);
116 self->CollectThread();
117 return FALSE;
118 }
119
120 pthread_t helper_thread_;
121
122 volatile gint thread_should_exit_;
123
124 static const char* kCompleteFilesystemMarker;
125
126 // Whether or not the destination device is currently mounted.
127 bool is_mounted_;
128
129 // Where the destination device is mounted.
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800130 std::string dest_path_;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000131
132 // The path to copy from. Usually left as the default "/", but tests can
133 // change it.
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800134 std::string copy_source_;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000135
136 // The install plan we're passed in via the input pipe.
137 InstallPlan install_plan_;
138
139 // Set to true if we detected the copy was unneeded and thus we skipped it.
140 bool skipped_copy_;
141
142 DISALLOW_COPY_AND_ASSIGN(FilesystemCopierAction);
143};
144
145} // namespace chromeos_update_engine
146
147#endif // CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__