summaryrefslogtreecommitdiffhomepage
path: root/util-linux/losetup.c
blob: cc6c2b1d54e67a263ac3af91dd0684fbf84fc77f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/* 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:	select PLATFORM_LINUX
//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, 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;*/
}