blob: fe6ed13fddc75edf06eb407d2658232c2c1ffbd5 [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 <string>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/stl_util.h>
#include <brillo/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* const kFormatProgramPaths[] = {
"/usr/sbin/mkfs.", "/bin/mkfs.", "/sbin/mkfs.", "/usr/bin/mkfs.",
};
// Supported file systems
const char* const kSupportedFilesystems[] = {
"vfat",
};
const char kDefaultLabel[] = "UNTITLED";
} // namespace
namespace cros_disks {
FormatManager::FormatManager(brillo::ProcessReaper* process_reaper)
: process_reaper_(process_reaper), weak_ptr_factory_(this) {}
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 (base::ContainsKey(format_process_, device_path)) {
LOG(WARNING) << "Device '" << device_path << "' is already being formatted";
return FORMAT_ERROR_DEVICE_BEING_FORMATTED;
}
SandboxedProcess* process = &format_process_[device_path];
process->SetNoNewPrivileges();
process->NewMountNamespace();
process->NewIpcNamespace();
process->NewNetworkNamespace();
process->SetCapabilities(0);
process->AddArgument(format_program);
// Allow to create filesystem across the entire device.
if (filesystem == "vfat") {
process->AddArgument("-I");
// FAT type should be predefined, because mkfs autodetection is faulty.
process->AddArgument("-F");
process->AddArgument("32");
process->AddArgument("-n");
process->AddArgument(kDefaultLabel);
}
process->AddArgument(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;
}
process_reaper_->WatchForChild(
FROM_HERE, process->pid(),
base::Bind(&FormatManager::OnFormatProcessTerminated,
weak_ptr_factory_.GetWeakPtr(), device_path));
return FORMAT_ERROR_NONE;
}
void FormatManager::OnFormatProcessTerminated(const string& device_path,
const siginfo_t& info) {
format_process_.erase(device_path);
FormatErrorType error_type = FORMAT_ERROR_UNKNOWN;
switch (info.si_code) {
case CLD_EXITED:
if (info.si_status == 0) {
error_type = FORMAT_ERROR_NONE;
LOG(INFO) << "Process " << info.si_pid << " for formatting '"
<< device_path << "' completed successfully";
} else {
error_type = FORMAT_ERROR_FORMAT_PROGRAM_FAILED;
LOG(ERROR) << "Process " << info.si_pid << " for formatting '"
<< device_path << "' exited with a status "
<< info.si_status;
}
break;
case CLD_DUMPED:
case CLD_KILLED:
error_type = FORMAT_ERROR_FORMAT_PROGRAM_FAILED;
LOG(ERROR) << "Process " << info.si_pid << " for formatting '"
<< device_path << "' killed by a signal " << info.si_status;
break;
default:
break;
}
if (observer_)
observer_->OnFormatCompleted(device_path, error_type);
}
string FormatManager::GetFormatProgramPath(const string& filesystem) const {
for (const char* program_path : kFormatProgramPaths) {
string path = program_path + filesystem;
if (base::PathExists(FilePath(path)))
return path;
}
return string();
}
bool FormatManager::IsFilesystemSupported(const string& filesystem) const {
for (const char* supported_filesystem : kSupportedFilesystems) {
if (filesystem == supported_filesystem)
return true;
}
return false;
}
} // namespace cros_disks