summaryrefslogtreecommitdiff
path: root/filesystem_copier_action.h
blob: 8f0dc06d547d2cf12f47638c03ea5234eb968cec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__
#define CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__

#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include <glib.h>
#include "update_engine/action.h"
#include "update_engine/install_plan.h"

// This action will only do real work if it's a delta update. It will
// format the install partition as ext3/4, copy the root filesystem into it,
// and then terminate.

// Implementation notes: This action uses a helper thread, which seems to
// violate the design decision to only have a single thread and use
// asynchronous i/o. The issue is that (to the best of my knowledge),
// there are no linux APIs to crawl a filesystem's metadata asynchronously.
// The suggested way seems to be to open the raw device and parse the ext
// filesystem. That's not a good approach for a number of reasons:
// - ties us to ext filesystem
// - although this wouldn't happen at the time of writing, it may not handle
//   changes to the source fs during the copy as gracefully.
// - requires us to have read-access to the source filesystem device, which
//   may be a security issue.
//
// Having said this, using a helper thread is not ideal, but it's acceptable:
// we still honor the Action API. That is, all interaction between the action
// and other objects in the system (e.g. the ActionProcessor) happens on the
// main thread. The helper thread is fully encapsulated by the action.

namespace chromeos_update_engine {

class FilesystemCopierAction;

template<>
class ActionTraits<FilesystemCopierAction> {
 public:
  // Takes the install plan as input
  typedef InstallPlan InputObjectType;
  // Passes the install plan as output
  typedef InstallPlan OutputObjectType;
};

class FilesystemCopierAction : public Action<FilesystemCopierAction> {
 public:
  FilesystemCopierAction()
      : thread_should_exit_(0),
        is_mounted_(false),
        copy_source_("/"),
        skipped_copy_(false) {}
  typedef ActionTraits<FilesystemCopierAction>::InputObjectType
  InputObjectType;
  typedef ActionTraits<FilesystemCopierAction>::OutputObjectType
  OutputObjectType;
  void PerformAction();
  void TerminateProcessing();

  // Used for testing, so we can copy from somewhere other than root
  void set_copy_source(const string& path) {
    copy_source_ = path;
  }
  // Returns true if we detected that a copy was unneeded and thus skipped it.
  bool skipped_copy() { return skipped_copy_; }

  // Debugging/logging
  static std::string StaticType() { return "FilesystemCopierAction"; }
  std::string Type() const { return StaticType(); }

 private:
  // These synchronously mount or unmount the given mountpoint
  bool Mount(const string& device, const string& mountpoint);
  bool Unmount(const string& mountpoint);

  // Performs a recursive file/directory copy from copy_source_ to dest_path_.
  // Doesn't return until the copy has completed. Returns true on success
  // or false on error.
  bool CopySynchronously();

  // There are helper functions for CopySynchronously. They handle creating
  // various types of files. They return true on success.
  bool CreateDirSynchronously(const std::string& new_path,
                              const struct stat& stbuf);
  bool CopyFileSynchronously(const std::string& old_path,
                             const std::string& new_path,
                             const struct stat& stbuf);
  bool CreateHardLinkSynchronously(const std::string& old_path,
                                   const std::string& new_path);
  // Note: Here, old_path is an existing symlink that will be copied to
  // new_path. Thus, old_path is *not* the same as the old_path from
  // the symlink() syscall.
  bool CopySymlinkSynchronously(const std::string& old_path,
                                const std::string& new_path,
                                const struct stat& stbuf);
  bool CreateNodeSynchronously(const std::string& new_path,
                               const struct stat& stbuf);

  // Returns NULL on success
  void* HelperThreadMain();
  static void* HelperThreadMainStatic(void* data) {
    FilesystemCopierAction* self =
        reinterpret_cast<FilesystemCopierAction*>(data);
    return self->HelperThreadMain();
  }

  // Joins the thread and tells the processor that we're done
  void CollectThread();
  // GMainLoop callback function:
  static gboolean CollectThreadStatic(gpointer data) {
    FilesystemCopierAction* self =
        reinterpret_cast<FilesystemCopierAction*>(data);
    self->CollectThread();
    return FALSE;
  }

  pthread_t helper_thread_;

  volatile gint thread_should_exit_;

  static const char* kCompleteFilesystemMarker;

  // Whether or not the destination device is currently mounted.
  bool is_mounted_;

  // Where the destination device is mounted.
  string dest_path_;

  // The path to copy from. Usually left as the default "/", but tests can
  // change it.
  string copy_source_;

  // The install plan we're passed in via the input pipe.
  InstallPlan install_plan_;

  // Set to true if we detected the copy was unneeded and thus we skipped it.
  bool skipped_copy_;

  DISALLOW_COPY_AND_ASSIGN(FilesystemCopierAction);
};

}  // namespace chromeos_update_engine

#endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_FILESYSTEM_COPIER_ACTION_H__