blob: b01e93cee589ba8c627f628b46ade62108330d86 [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 "loop_mounter.h"
#include <string>
#include <fcntl.h>
#include <linux/loop.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <base/strings/stringprintf.h>
namespace imageloader {
namespace {
const int MAX_RETRY = 5;
enum class MountStatus { FAIL, RETRY, SUCCESS };
MountStatus MountLoopDevice(const base::ScopedFD& image_fd,
const base::FilePath& mount_point) {
base::ScopedFD loopctl_fd(open("/dev/loop-control", O_RDONLY | O_CLOEXEC));
if (!loopctl_fd.is_valid()) {
PLOG(ERROR) << "loopctl_fd";
return MountStatus::FAIL;
}
int device_free_number = ioctl(loopctl_fd.get(), LOOP_CTL_GET_FREE);
if (device_free_number < 0) {
PLOG(ERROR) << "ioctl : LOOP_CTL_GET_FREE";
return MountStatus::FAIL;
}
std::string device_path =
base::StringPrintf("/dev/loop%d", device_free_number);
base::ScopedFD loop_device_fd(
open(device_path.c_str(), O_RDONLY | O_CLOEXEC));
if (!loop_device_fd.is_valid()) {
PLOG(ERROR) << "loop_device_fd";
LOG(ERROR) << "mount_point: " << mount_point.value();
return MountStatus::FAIL;
}
if (ioctl(loop_device_fd.get(), LOOP_SET_FD, image_fd.get()) < 0) {
if (errno == EBUSY) {
// We need to retry because another program could grap the loop device,
// resulting in an EBUSY error. If that happens, run again and grab a new
// device.
return MountStatus::RETRY;
}
PLOG(ERROR) << "ioctl: LOOP_SET_FD";
LOG(ERROR) << "mount_point: " << mount_point.value();
return MountStatus::FAIL;
}
if (mount(device_path.c_str(), mount_point.value().c_str(), "squashfs",
MS_RDONLY | MS_NOSUID | MS_NODEV, "") < 0) {
PLOG(ERROR) << "mount";
LOG(ERROR) << "mount_point: " << mount_point.value();
ioctl(loop_device_fd.get(), LOOP_CLR_FD, 0);
return MountStatus::FAIL;
}
return MountStatus::SUCCESS;
}
} // namespace
bool LoopMounter::Mount(const base::ScopedFD& image_fd,
const base::FilePath& mount_point) {
for (int i = 0; i < MAX_RETRY; i++) {
MountStatus status = MountLoopDevice(image_fd, mount_point);
switch (status) {
case MountStatus::FAIL:
return false;
case MountStatus::RETRY:
continue;
case MountStatus::SUCCESS:
return true;
}
}
return false;
}
} // namespace imageloader