blob: 3a330efd5b3ed6b065c3da5bc01a94d538bd0caa [file] [log] [blame]
// 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 std::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 std::string& device, const std::string& mountpoint);
bool Unmount(const std::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.
std::string dest_path_;
// The path to copy from. Usually left as the default "/", but tests can
// change it.
std::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__