| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "image-burner/image_burn_service.h" |
| |
| #include <errno.h> |
| #include <glib.h> |
| #include <zlib.h> |
| |
| #include <base/basictypes.h> |
| #include <base/file_util.h> |
| #include <base/logging.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <cros/chromeos_cros_api.h> |
| #include <cros/chromeos_mount.h> |
| #include <dbus/dbus-glib-lowlevel.h> |
| |
| #include <fstream> |
| #include <string> |
| |
| #include "image-burner/image_burner.h" |
| #include "image-burner/marshal.h" |
| |
| namespace imageburn { |
| #include "image-burner/bindings/server.h" |
| |
| const int kBurningBlockSize = 4 * 1024; // 4 KiB |
| // after every kSentSignalRatio IO operations, update signal will be emited |
| const int kSentSignalRatio = 256; |
| const char* kDevPath = "/dev/"; |
| |
| ImageBurnService::ImageBurnService() : image_burner_(), |
| main_loop_(NULL), |
| shutting_down_(false), |
| burning_(false) { |
| LoadLibcros(); |
| LOG(INFO) << "Service initialized"; |
| } |
| |
| ImageBurnService::~ImageBurnService() { |
| Cleanup(); |
| } |
| |
| const char *ImageBurnService::service_name() const { |
| return kImageBurnServiceName; |
| } |
| |
| const char *ImageBurnService::service_path() const { |
| return kImageBurnServicePath; |
| } |
| |
| const char *ImageBurnService::service_interface() const { |
| return kImageBurnServiceInterface; |
| } |
| |
| GObject *ImageBurnService::service_object() const { |
| return G_OBJECT(image_burner_); |
| } |
| |
| bool ImageBurnService::Initialize() { |
| // Install the type-info for the service with dbus. |
| dbus_g_object_type_install_info(image_burner_get_type(), |
| &dbus_glib_image_burner_object_info); |
| |
| signals_[kSignalBurnUpdate] = |
| g_signal_new(kSignalBurnUpdateName, |
| image_burner_get_type(), |
| G_SIGNAL_RUN_FIRST, |
| 0, NULL, NULL, |
| image_burner_VOID__STRING_INT64_INT64, G_TYPE_NONE, |
| 3, G_TYPE_STRING, G_TYPE_INT64, G_TYPE_INT64); |
| signals_[kSignalBurnFinished] = |
| g_signal_new(kSignalBurnFinishedName, |
| image_burner_get_type(), |
| G_SIGNAL_RUN_FIRST, |
| 0, NULL, NULL, |
| image_burner_VOID__STRING_BOOLEAN_STRING, G_TYPE_NONE, |
| 3, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING); |
| return Reset(); |
| } |
| |
| bool ImageBurnService::Reset() { |
| Cleanup(); |
| |
| image_burner_ = reinterpret_cast<ImageBurner*>( |
| g_object_new(image_burner_get_type(), NULL)); |
| // Allow references to this instance. |
| image_burner_->service = this; |
| |
| main_loop_ = g_main_loop_new(NULL, false); |
| if (!main_loop_) { |
| LOG(ERROR) << "Failed to create main loop"; |
| return false; |
| } |
| return true; |
| } |
| |
| bool ImageBurnService::Shutdown() { |
| shutting_down_ = true; |
| chromeos::dbus::AbstractDbusService::Shutdown(); |
| } |
| |
| gboolean ImageBurnService::BurnImage(gchar* from_path, gchar* to_path, |
| GError** error) { |
| if (burning_) { |
| g_set_error_literal(error, 0, 0, "Another burn in progress"); |
| return false; |
| } else { |
| burning_ = true; |
| } |
| |
| scoped_ptr<BurnArguments> args(new BurnArguments); |
| args->from_path = from_path; |
| args->to_path = to_path; |
| args->service = this; |
| g_timeout_add(1, &ImageBurnService::BurnImageTimeoutCallback, args.release()); |
| return true; |
| } |
| |
| gboolean ImageBurnService::BurnImageTimeoutCallback(gpointer data) { |
| scoped_ptr<BurnArguments> args(static_cast<BurnArguments*>(data)); |
| args->service->InitiateBurning(args->from_path.c_str(), |
| args->to_path.c_str()); |
| return false; |
| } |
| |
| bool ImageBurnService::LoadLibcros() { |
| std::string load_error_string; |
| static bool loaded = false; |
| if (loaded) { |
| LOG(INFO) << "Already loaded libcros..."; |
| return true; |
| } |
| |
| loaded = chromeos::LoadLibcros(chromeos::kCrosDefaultPath, load_error_string); |
| |
| if (!loaded) { |
| LOG(ERROR) << "Problem loading chromeos shared object: " |
| << load_error_string; |
| } |
| return loaded; |
| } |
| |
| void ImageBurnService::Cleanup() { |
| if (main_loop_) { |
| g_main_loop_unref(main_loop_); |
| main_loop_ = NULL; |
| } |
| if (image_burner_) { |
| g_object_unref(image_burner_); |
| image_burner_ = NULL; |
| } |
| } |
| |
| void ImageBurnService::InitiateBurning(const char* from_path, |
| const char* to_path) { |
| std::string err; |
| bool success = UnmountAndValidateDevice(to_path); |
| if (success) { |
| success = DoBurn(from_path, to_path, &err); |
| LOG(INFO) << (success ? "Burn complete" : "Burn failed"); |
| } else { |
| err = "Unable to unmount target path"; |
| success = false; |
| } |
| |
| SendFinishedSignal(to_path, success, err.c_str()); |
| burning_ = false; |
| } |
| |
| bool ImageBurnService::UnmountAndValidateDevice(const char* device_path) { |
| if (chromeos::IsBootDevicePath(device_path)) { |
| LOG(ERROR) << device_path << " is on root device"; |
| return false; |
| } |
| |
| LOG(INFO) << "Unmounting " << device_path; |
| chromeos::MountStatus* status = chromeos::RetrieveMountInformation(); |
| |
| bool success = false; |
| for (int i = 0; i < status->size; ++i) { |
| FilePath device_system_path |
| = static_cast<FilePath>(status->disks[i].systempath); |
| // system_path looks like: |
| // /sys/devices/pci0000:00/blah/blah/4:0:0:0/block/sdb/sdb1 |
| if (device_system_path.DirName().BaseName().value() != "block") { |
| // Handle unpartitioned device. |
| device_system_path = device_system_path.DirName(); |
| } |
| std::string device_target_path(kDevPath); |
| device_target_path.append(device_system_path.BaseName().value()); |
| |
| if (device_target_path.compare(device_path) == 0) { |
| success = true; // Found a match in mount list. |
| if (status->disks[i].mountpath) { |
| success = chromeos::UnmountDevicePath(status->disks[i].path); |
| LOG(INFO) << "Unmounting " << status->disks[i].path |
| << (success ? " succeeded" : " failed"); |
| } |
| LOG(INFO) << device_path << " is a device path"; |
| if (!success) break; |
| } |
| } |
| |
| chromeos::FreeMountStatus(status); |
| return success; |
| } |
| |
| bool ImageBurnService::DoBurn(const char* from_path, |
| const char* to_path, |
| std::string* err) { |
| LOG(INFO) << "Burning " << from_path << " to " << to_path; |
| |
| bool success = true; |
| |
| gzFile from_file = gzopen(from_path, "rb"); |
| if (!from_file) { |
| int gz_err = Z_OK; |
| err->append("Couldn't open " + static_cast<std::string>(from_path) + |
| "\n" + static_cast<std::string>(gzerror(from_file, &gz_err)) + "\n"); |
| LOG(WARNING) << "Couldn't open " << from_path << " : " << |
| gzerror(from_file, &gz_err); |
| success = false; |
| } else { |
| LOG(INFO) << from_path << " opened"; |
| } |
| |
| FILE* to_file = NULL; |
| if (success) { |
| to_file = fopen(to_path, "wb"); |
| if (!to_file) { |
| err->append("Couldn't open" + static_cast<std::string>(to_path) + |
| "\n" + static_cast<std::string>(strerror(errno)) + "\n"); |
| LOG(WARNING) << "Couldn't open" << to_path << " : " << strerror(errno); |
| success = false; |
| } else { |
| LOG(INFO) << to_path << " opened"; |
| } |
| } |
| if (success) { |
| scoped_array<char> buffer(new char[kBurningBlockSize]); |
| LOG(INFO) << "a" << sizeof(buffer.get()); |
| int len = 0; |
| bool success = true; |
| int64 total_burnt = 0; |
| int64 image_size = GetTotalImageSize(from_path); |
| int i = kSentSignalRatio; |
| while ((len = gzread(from_file, buffer.get(), |
| kBurningBlockSize*sizeof(char))) > 0) { |
| if (shutting_down_) |
| return false; |
| if (fwrite(buffer.get(), 1, len * sizeof(char), to_file) == |
| static_cast<size_t>(len)) { |
| total_burnt += static_cast<int64>(len); |
| fflush(to_file); |
| if (i == kSentSignalRatio) { |
| SendProgressSignal(total_burnt, image_size, to_path); |
| i = 0; |
| } else { |
| ++i; |
| } |
| } else { |
| success = false; |
| err->append("Unable to write to " + static_cast<std::string>(to_path) + |
| "\n" + static_cast<std::string>(strerror(errno)) + "\n"); |
| LOG(WARNING) << "Unable to write to " << to_path << " : " << |
| strerror(errno); |
| break; |
| } |
| } |
| if (len != 0) { |
| int gz_err = Z_OK; |
| err->append(static_cast<std::string>(gzerror(from_file, &gz_err)) + "\n"); |
| LOG(WARNING) << gzerror(from_file, &gz_err); |
| } |
| } |
| |
| if (from_file) { |
| if ((gzclose(from_file)) != 0) { |
| success = false; |
| int gz_err = Z_OK; |
| err->append("Couldn't close " + static_cast<std::string>(from_path) + |
| "\n" + static_cast<std::string>(gzerror(from_file, &gz_err)) + "\n"); |
| LOG(WARNING) << "Couldn't close" << from_path << " : " << |
| gzerror(from_file, &gz_err); |
| } else { |
| LOG(INFO) << from_path << " closed"; |
| } |
| } |
| |
| if (to_file) { |
| if (fclose(to_file) != 0) { |
| success = false; |
| err->append("Couldn't close " + static_cast<std::string>(to_path) + |
| "\n" + static_cast<std::string>(strerror(errno)) + "\n"); |
| LOG(WARNING) << "Couldn't close" << to_path << " : " << strerror(errno); |
| } else { |
| LOG(INFO) << to_path << " closed"; |
| } |
| } |
| |
| return success; |
| } |
| |
| void ImageBurnService::SendFinishedSignal(const char* target_path, bool success, |
| const char* error) { |
| g_signal_emit(image_burner_, |
| signals_[kSignalBurnFinished], |
| 0, target_path, success, error); |
| } |
| |
| void ImageBurnService::SendProgressSignal(int64 amount_burnt, int64 total_size, |
| const char* target_path) { |
| g_signal_emit(image_burner_, |
| signals_[kSignalBurnUpdate], |
| 0, target_path, amount_burnt, total_size); |
| } |
| |
| int64 ImageBurnService::GetTotalImageSize(const char* from_path) { |
| FILE* from_file = fopen(from_path, "rb"); |
| int result = 0; |
| fseek(from_file, -sizeof(result), SEEK_END); |
| if (fread(&result, 1, sizeof(result), from_file) != sizeof(result)) { |
| result = 0; |
| } |
| fclose(from_file); |
| return static_cast<int64>(result); |
| } |
| |
| } // namespace imageburn |
| |