Skip to content

Commit

Permalink
Userland: Add the losetup utility to manage loop devices
Browse files Browse the repository at this point in the history
The new utility implements an option to create a new loop device based
on path of source image, as well as an option to delete an existing
device.
  • Loading branch information
supercomputer7 committed Jul 6, 2024
1 parent 2c73149 commit eac9b76
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Base/usr/share/man/man1/losetup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Name

losetup - manage loop devices

## Synopsis

```**sh
$ losetup [options...] [path]
```

## Description

losetup is a utility for managing loop devices. It can create new devices or delete
existing ones.

## Options

* `-d`, `--delete`: Remove an existing loop device based on the `path` argument.
* `-c`, `--create`: Create a new loop device based on the `path` argument.

## Files

* `/dev/devctl` - device control device inode, which allows creating and deleting loop devices.

## Examples

```sh
$ losetup -c test.img
Created new device at /dev/loop/0
$ losetup -d /dev/loop/0
```
57 changes: 57 additions & 0 deletions Userland/Utilities/losetup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2024, Liav A. <[email protected]>
*
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <AK/String.h>
#include <AK/StringUtils.h>
#include <AK/StringView.h>
#include <LibCore/ArgsParser.h>
#include <LibCore/File.h>
#include <LibCore/System.h>
#include <LibMain/Main.h>

ErrorOr<int> serenity_main(Main::Arguments arguments)
{
TRY(Core::System::pledge("stdio rpath wpath"));
StringView path;
bool flag_delete_device = false;
bool flag_add_new_device = false;

Core::ArgsParser args_parser;
args_parser.set_general_help("Manage loop devices.");
args_parser.add_option(flag_delete_device, "Delete a loop device", "delete", 'd');
args_parser.add_option(flag_add_new_device, "Add new device", "create", 'c');
args_parser.add_positional_argument(path, "Path", "path", Core::ArgsParser::Required::No);
args_parser.parse(arguments);

if (!(flag_delete_device || flag_add_new_device))
return Error::from_string_literal("No specified option was requested.");

if (path.is_null())
return Error::from_string_literal("No specified path to handle.");

auto devctl_device = TRY(Core::File::open("/dev/devctl"sv, Core::File::OpenMode::Read));
if (flag_delete_device) {
constexpr StringView base_dev_loop_path = "/dev/loop/"sv;
if (!path.starts_with(base_dev_loop_path))
return Error::from_string_literal("Invalid loop device path.");
auto number = path.substring_view(base_dev_loop_path.length());
auto possible_number = number.to_number<u64>();
if (!possible_number.has_value())
return Error::from_string_literal("Invalid loop device number.");
auto loop_device_index = possible_number.release_value();
TRY(Core::System::ioctl(devctl_device->fd(), DEVCTL_DESTROY_LOOP_DEVICE, &loop_device_index));
return 0;
}

VERIFY(flag_add_new_device);
auto value = TRY(Core::System::open(path, O_RDWR));
TRY(Core::System::ioctl(devctl_device->fd(), DEVCTL_CREATE_LOOP_DEVICE, &value));
int loop_device_index = value;

auto loop_device_path = TRY(String::formatted("/dev/loop/{}", loop_device_index));
outln("Created new device at {}", loop_device_path);
return 0;
}

0 comments on commit eac9b76

Please sign in to comment.