busybox/util-linux/losetup.c
Steffen Trumtrar 6561e07460 mount: support the sizelimit and offset option for loop devices
Starting with linux kernel v5.4 squashfs has a more strict parameter
checking implemented. Unlike util-linux mount, busybox never supported
the sizelimit option but simply forwards it to the kernel.
Since v5.4 mounting will fail with

    squashfs: Unknown parameter 'sizelimit'

Support the sizelimit parameter by setting it in the LOOP_SET_STATUS64
structure before handing it to the kernel.

While at it also add support for the offset option, which currently will
always be set to 0.

function                                             old     new   delta
cut_out_ull_opt                                        -     167    +167
singlemount                                         1230    1266     +36
set_loop                                             834     862     +28
losetup_main                                         479     483      +4
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 3/0 up/down: 235/0)             Total: 235 bytes

Signed-off-by: Steffen Trumtrar <s.trumtrar@pengutronix.de>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
2020-10-01 00:31:11 +02:00

187 lines
5.6 KiB
C

/* vi: set sw=4 ts=4: */
/*
* Mini losetup implementation for busybox
*
* Copyright (C) 2002 Matt Kraai.
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
//config:config LOSETUP
//config: bool "losetup (5.5 kb)"
//config: default y
//config: help
//config: losetup is used to associate or detach a loop device with a regular
//config: file or block device, and to query the status of a loop device. This
//config: version does not currently support enabling data encryption.
//applet:IF_LOSETUP(APPLET_NOEXEC(losetup, losetup, BB_DIR_SBIN, BB_SUID_DROP, losetup))
//kbuild:lib-$(CONFIG_LOSETUP) += losetup.o
//usage:#define losetup_trivial_usage
//usage: "[-rP] [-o OFS] {-f|LOOPDEV} FILE: associate loop devices\n"
//usage: " losetup -c LOOPDEV: reread file size\n"
//usage: " losetup -d LOOPDEV: disassociate\n"
//usage: " losetup -a: show status\n"
//usage: " losetup -f: show next free loop device"
//usage:#define losetup_full_usage "\n\n"
//usage: " -o OFS Start OFS bytes into FILE"
//usage: "\n -P Scan for partitions"
//usage: "\n -r Read-only"
//usage: "\n -f Show/use next free loop device"
//usage:
//usage:#define losetup_notes_usage
//usage: "One argument (losetup /dev/loop1) will display the current association\n"
//usage: "(if any), or disassociate it (with -d). The display shows the offset\n"
//usage: "and filename of the file the loop device is currently bound to.\n\n"
//usage: "Two arguments (losetup /dev/loop1 file.img) create a new association,\n"
//usage: "with optional partition scanning (creates /dev/loop1p1, /dev/loop1p2\n"
//usage: "etc. with -P) and with an optional offset (-o 12345). Encryption is\n"
//usage: "not yet supported. losetup -f will show the first free loop device\n\n"
#include "libbb.h"
/* 1048575 is a max possible minor number in Linux circa 2010 */
/* for now use something less extreme */
#define MAX_LOOP_NUM 1023
int losetup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int losetup_main(int argc UNUSED_PARAM, char **argv)
{
unsigned opt;
char *opt_o;
char dev[LOOP_NAMESIZE];
enum {
OPT_c = (1 << 0),
OPT_d = (1 << 1),
OPT_P = (1 << 2),
OPT_o = (1 << 3),
OPT_f = (1 << 4),
OPT_a = (1 << 5),
OPT_r = (1 << 6),
};
opt = getopt32(argv, "^" "cdPo:far" "\0" "?2:d--Pofar:a--Pofr", &opt_o);
argv += optind;
/* LOOPDEV */
if (!opt && argv[0] && !argv[1]) {
char *s;
s = query_loop(argv[0]);
if (!s)
bb_simple_perror_msg_and_die(argv[0]);
printf("%s: %s\n", argv[0], s);
if (ENABLE_FEATURE_CLEAN_UP)
free(s);
return EXIT_SUCCESS;
}
/* -c LOOPDEV */
if (opt == OPT_c && argv[0]) {
int fd = xopen(argv[0], O_RDONLY);
#ifndef LOOP_SET_CAPACITY
# define LOOP_SET_CAPACITY 0x4C07
#endif
xioctl(fd, LOOP_SET_CAPACITY, /*ignored:*/0);
return EXIT_SUCCESS;
}
/* -d LOOPDEV */
if (opt == OPT_d && argv[0]) {
if (del_loop(argv[0]))
bb_simple_perror_msg_and_die(argv[0]);
return EXIT_SUCCESS;
}
/* -a */
if (opt == OPT_a) {
int n;
for (n = 0; n < MAX_LOOP_NUM; n++) {
char *s;
sprintf(dev, LOOP_FORMAT, n);
s = query_loop(dev);
if (s) {
printf("%s: %s\n", dev, s);
free(s);
}
}
return EXIT_SUCCESS;
}
/* contains -f */
if (opt & OPT_f) {
char *s;
int n;
n = get_free_loop();
if (n == -1)
bb_simple_error_msg_and_die("no free loop devices");
if (n < 0) /* n == -2: no /dev/loop-control, use legacy method */
n = 0;
/* or: n >= 0: the number of next free loopdev, just verify it */
do {
if (n > MAX_LOOP_NUM)
bb_simple_error_msg_and_die("no free loop devices");
sprintf(dev, LOOP_FORMAT, n++);
s = query_loop(dev);
free(s);
} while (s);
/* now: dev is next free "/dev/loopN" */
if ((opt == OPT_f) && !argv[0]) {
puts(dev);
return EXIT_SUCCESS;
}
}
/* [-rP] [-o OFS] {-f|LOOPDEV} FILE */
if (argv[0] && ((opt & OPT_f) || argv[1])) {
unsigned long long offset = 0;
char *d = dev;
if (opt & OPT_o)
offset = xatoull(opt_o);
if (!(opt & OPT_f))
d = *argv++;
if (argv[0]) {
unsigned flags = (opt & OPT_r) ? BB_LO_FLAGS_READ_ONLY : 0;
if (opt & OPT_P) {
flags |= BB_LO_FLAGS_PARTSCAN;
}
if (set_loop(&d, argv[0], offset, 0, flags) < 0)
bb_simple_perror_msg_and_die(argv[0]);
return EXIT_SUCCESS;
}
}
/* TODO: util-linux 2.28 shows this when run w/o params:
* NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO
* /dev/loop0 0 0 1 0 /PATH/TO/FILE 0
*
* implemented by reading /sys:
*
* open("/sys/block", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3
* newfstatat(3, "loop0/loop/backing_file", {st_mode=S_IFREG|0444, st_size=4096, ...}, 0) = 0
* stat("/dev/loop0", {st_mode=S_IFBLK|0660, st_rdev=makedev(7, 0), ...}) = 0
* open("/sys/dev/block/7:0/loop/offset", O_RDONLY|O_CLOEXEC) = 5
* read(5, "0\n", 4096) = 2
* open("/sys/dev/block/7:0/loop/sizelimit", O_RDONLY|O_CLOEXEC) = 5
* read(5, "0\n", 4096) = 2
* open("/sys/dev/block/7:0/loop/offset", O_RDONLY|O_CLOEXEC) = 5
* read(5, "0\n", 4096) = 2
* open("/sys/dev/block/7:0/loop/autoclear", O_RDONLY|O_CLOEXEC) = 5
* read(5, "1\n", 4096) = 2
* open("/sys/dev/block/7:0/ro", O_RDONLY|O_CLOEXEC) = 5
* read(5, "0\n", 4096) = 2
* open("/sys/dev/block/7:0/loop/backing_file", O_RDONLY|O_CLOEXEC) = 5
* read(5, "/PATH/TO/FILE", 4096) = 37
* open("/sys/dev/block/7:0/loop/dio", O_RDONLY|O_CLOEXEC) = 5
* read(5, "0\n", 4096) = 2
*/
bb_show_usage(); /* does not return */
/*return EXIT_FAILURE;*/
}