blob: c3e54a610f51301340cb80a244eb90ce79245cb7 [file] [log] [blame]
// Copyright 2016 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 "arc/obb-mounter/mount.h"
#include <mntent.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <memory>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_number_conversions.h>
namespace arc {
namespace obb_mounter {
namespace {
const int kRootUid = 0;
const int kUidNamespaceOffset = 655360;
const int kGidNamespaceOffset = 655360;
const char kMountObbExecutableName[] = "mount-obb";
bool IsObbMountedAt(const std::string& mount_path) {
std::unique_ptr<FILE, int (*)(FILE*)> fp(setmntent("/proc/mounts", "r"),
endmntent);
if (!fp) {
LOG(ERROR) << "setmntent failed.";
return false;
}
while (mntent* mentry = getmntent(fp.get())) {
if (strcmp(kMountObbExecutableName, mentry->mnt_fsname) == 0 &&
mount_path == mentry->mnt_dir) {
return true;
}
}
return false;
}
} // namespace
bool MountObb(const std::string& obb_file,
const std::string& mount_path,
gid_t owner_gid) {
if (IsObbMountedAt(mount_path)) {
LOG(ERROR) << mount_path << " is already occupied.";
return false;
}
// Make destination directory.
if (!base::CreateDirectory(base::FilePath(mount_path))) {
PLOG(ERROR) << "Failed to create the destination directory.";
return false;
}
// Add UID namespace offsets.
pid_t owner_uid = kRootUid + kUidNamespaceOffset;
owner_gid += kGidNamespaceOffset;
// Run mount-obb.
std::string owner_uid_string = base::IntToString(owner_uid);
std::string owner_gid_string = base::IntToString(owner_gid);
pid_t pid = fork();
if (pid == -1) {
PLOG(ERROR) << "fork failed";
return false;
}
if (pid == 0) {
const char* argv[] = {
kMountObbExecutableName, obb_file.c_str(), mount_path.c_str(),
owner_uid_string.c_str(), owner_gid_string.c_str(), nullptr,
};
execvp(argv[0], const_cast<char**>(argv));
_exit(EXIT_FAILURE);
}
// Wait for mount-obb.
const int kMaxRetries = 5000;
const int kIntervalMicroseconds = 1000;
for (int i = 0; i < kMaxRetries; ++i) {
// Check if mount-obb is still running.
if (waitpid(pid, nullptr, WNOHANG) != 0) {
LOG(ERROR) << "mount-obb encounted an error and exited.";
return false;
}
// Try to find the new mount point.
if (IsObbMountedAt(mount_path)) {
return true;
}
// Wait for a while.
usleep(kIntervalMicroseconds);
}
LOG(ERROR) << "Mount timeout.";
// Note: Because signal(SIGCHLD, SIG_IGN) is called in main(), here we may end
// up killing a wrong process with the same PID (i.e. the child process exited
// and the same PID was assigned to a new process). However, as long as we are
// in our own PID namespace and no fork() happens on any threads other than
// this one, we don't have to worry about it.
kill(pid, SIGKILL);
return false;
}
bool UnmountObb(const std::string& mount_path) {
if (!IsObbMountedAt(mount_path)) {
LOG(ERROR) << "OBB not mounted at " << mount_path;
return false;
}
// Note: This doesn't match with Vold's corresponding behavior where it tries
// to kill all processes which are accessing the file system being unmounted.
if (umount2(mount_path.c_str(), MNT_DETACH)) {
PLOG(ERROR) << "umount failed";
return false;
}
if (!base::DeleteFile(base::FilePath(mount_path), true /* recursive */)) {
LOG(ERROR) << "Failed to delete the destination directory.";
return false;
}
return true;
}
} // namespace obb_mounter
} // namespace arc