blob: 48c058f97ae061365312d7818a404a135b4da0e1 [file] [log] [blame]
// Copyright (c) 2012 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 "cros-disks/format_manager.h"
#include <glib.h>
#include <map>
#include <string>
#include <base/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <chromeos/process.h>
#include "cros-disks/format_manager_observer_interface.h"
using base::FilePath;
using std::string;
namespace {
// Expected locations of an external format program
const char* kFormatProgramPaths[] = {
"/usr/sbin/mkfs.", "/bin/mkfs.", "/sbin/mkfs.", "/usr/bin/mkfs.",
};
// Supported file systems
const char* kSupportedFilesystems[] = {
"vfat"
};
const char kDefaultLabel[] = "UNTITLED";
void OnFormatProcessExit(pid_t pid, gint status, gpointer data) {
cros_disks::FormatManager* format_manager =
reinterpret_cast<cros_disks::FormatManager*>(data);
format_manager->FormattingFinished(pid, status);
}
} // namespace
namespace cros_disks {
FormatManager::FormatManager() {
}
FormatManager::~FormatManager() {
}
FormatErrorType FormatManager::StartFormatting(const string& device_path,
const string& device_file,
const string& filesystem) {
// Check if the file system is supported for formatting
if (!IsFilesystemSupported(filesystem)) {
LOG(WARNING) << filesystem
<< " filesystem is not supported for formatting";
return FORMAT_ERROR_UNSUPPORTED_FILESYSTEM;
}
// Localize mkfs on disk
string format_program = GetFormatProgramPath(filesystem);
if (format_program.empty()) {
LOG(WARNING) << "Could not find a format program for filesystem '"
<< filesystem << "'";
return FORMAT_ERROR_FORMAT_PROGRAM_NOT_FOUND;
}
if (ContainsKey(format_process_, device_path)) {
LOG(WARNING) << "Device '" << device_path
<< "' is already being formatted";
return FORMAT_ERROR_DEVICE_BEING_FORMATTED;
}
chromeos::ProcessImpl* process = &format_process_[device_path];
process->AddArg(format_program);
// Allow to create filesystem across the entire device.
if (filesystem == "vfat") {
process->AddArg("-I");
// FAT type should be predefined, because mkfs autodetection is faulty.
process->AddStringOption("-F", "32");
process->AddStringOption("-n", kDefaultLabel);
}
process->AddArg(device_file);
if (!process->Start()) {
LOG(WARNING) << "Cannot start a process for formatting '"
<< device_path << "' as filesystem '" << filesystem << "'";
format_process_.erase(device_path);
return FORMAT_ERROR_FORMAT_PROGRAM_FAILED;
}
pid_to_device_path_[process->pid()] = device_path;
g_child_watch_add(process->pid(), &OnFormatProcessExit, this);
return FORMAT_ERROR_NONE;
}
void FormatManager::FormattingFinished(pid_t pid, int status) {
string device_path = pid_to_device_path_[pid];
format_process_.erase(device_path);
pid_to_device_path_.erase(pid);
FormatErrorType error_type = FORMAT_ERROR_UNKNOWN;
if (WIFEXITED(status)) {
int exit_status = WEXITSTATUS(status);
if (exit_status == 0) {
error_type = FORMAT_ERROR_NONE;
LOG(INFO) << "Process " << pid << " for formatting '" << device_path
<< "' completed successfully";
} else {
error_type = FORMAT_ERROR_FORMAT_PROGRAM_FAILED;
LOG(ERROR) << "Process " << pid << " for formatting '" << device_path
<< "' exited with a status " << exit_status;
}
} else if (WIFSIGNALED(status)) {
error_type = FORMAT_ERROR_FORMAT_PROGRAM_FAILED;
LOG(ERROR) << "Process " << pid << " for formatting '" << device_path
<< "' killed by a signal " << WTERMSIG(status);
}
if (observer_)
observer_->OnFormatCompleted(device_path, error_type);
}
string FormatManager::GetFormatProgramPath(const string& filesystem) const {
for (size_t i = 0; i < arraysize(kFormatProgramPaths); ++i) {
string path = kFormatProgramPaths[i] + filesystem;
if (base::PathExists(FilePath(path)))
return path;
}
return string();
}
bool FormatManager::IsFilesystemSupported(const string& filesystem) const {
for (size_t i = 0; i < arraysize(kSupportedFilesystems); ++i) {
if (filesystem == kSupportedFilesystems[i])
return true;
}
return false;
}
} // namespace cros_disks