1
2
3
4
5
6
7
8
9
10#include "libbb.h"
11#include <linux/version.h>
12
13#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
14
15
16
17
18# include <linux/loop.h>
19typedef struct loop_info64 bb_loop_info;
20# define BB_LOOP_SET_STATUS LOOP_SET_STATUS64
21# define BB_LOOP_GET_STATUS LOOP_GET_STATUS64
22
23#else
24
25
26
27# include <linux/posix_types.h>
28# define LO_NAME_SIZE 64
29# define LO_KEY_SIZE 32
30# define LOOP_SET_FD 0x4C00
31# define LOOP_CLR_FD 0x4C01
32# define BB_LOOP_SET_STATUS 0x4C02
33# define BB_LOOP_GET_STATUS 0x4C03
34typedef struct {
35 int lo_number;
36 __kernel_dev_t lo_device;
37 unsigned long lo_inode;
38 __kernel_dev_t lo_rdevice;
39 int lo_offset;
40 int lo_encrypt_type;
41 int lo_encrypt_key_size;
42 int lo_flags;
43 char lo_file_name[LO_NAME_SIZE];
44 unsigned char lo_encrypt_key[LO_KEY_SIZE];
45 unsigned long lo_init[2];
46 char reserved[4];
47} bb_loop_info;
48#endif
49
50char* FAST_FUNC query_loop(const char *device)
51{
52 int fd;
53 bb_loop_info loopinfo;
54 char *dev = NULL;
55
56 fd = open(device, O_RDONLY);
57 if (fd >= 0) {
58 if (ioctl(fd, BB_LOOP_GET_STATUS, &loopinfo) == 0) {
59 dev = xasprintf("%"OFF_FMT"u %s", (off_t) loopinfo.lo_offset,
60 (char *)loopinfo.lo_file_name);
61 }
62 close(fd);
63 }
64
65 return dev;
66}
67
68int FAST_FUNC del_loop(const char *device)
69{
70 int fd, rc;
71
72 fd = open(device, O_RDONLY);
73 if (fd < 0)
74 return fd;
75 rc = ioctl(fd, LOOP_CLR_FD, 0);
76 close(fd);
77
78 return rc;
79}
80
81
82int FAST_FUNC get_free_loop(void)
83{
84 int fd;
85 int loopdevno;
86
87 fd = open("/dev/loop-control", O_RDWR | O_CLOEXEC);
88 if (fd == -1)
89 return fd - 1;
90
91#ifndef LOOP_CTL_GET_FREE
92# define LOOP_CTL_GET_FREE 0x4C82
93#endif
94 loopdevno = ioctl(fd, LOOP_CTL_GET_FREE);
95 close(fd);
96 return loopdevno;
97}
98
99static int get_next_free_loop(char *dev, int id)
100{
101 int loopdevno;
102
103 loopdevno = get_free_loop();
104 if (loopdevno != -1) {
105
106 if (loopdevno >= 0)
107 id = loopdevno;
108 sprintf(dev, LOOP_FORMAT, id);
109 }
110 return loopdevno;
111}
112
113#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
114# define LOOP_CONFIGURE 0x4C0A
115struct bb_loop_config {
116 uint32_t fd;
117 uint32_t block_size;
118 struct loop_info64 info;
119 uint64_t __reserved[8];
120};
121#endif
122
123static int set_loopdev_params(int lfd,
124 int ffd, const char *file,
125 unsigned long long offset,
126 unsigned long long sizelimit,
127 unsigned flags)
128{
129 int rc;
130#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
131 struct bb_loop_config lconfig;
132# define loopinfo lconfig.info
133#else
134 bb_loop_info loopinfo;
135#endif
136
137 rc = ioctl(lfd, BB_LOOP_GET_STATUS, &loopinfo);
138
139
140 if (rc && errno == ENXIO) {
141#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
142 memset(&lconfig, 0, sizeof(lconfig));
143#else
144 memset(&loopinfo, 0, sizeof(loopinfo));
145#endif
146 safe_strncpy((char *)loopinfo.lo_file_name, file, LO_NAME_SIZE);
147 loopinfo.lo_offset = offset;
148 loopinfo.lo_sizelimit = sizelimit;
149
150
151
152
153 loopinfo.lo_flags = (flags & ~BB_LO_FLAGS_READ_ONLY);
154
155#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_LOOP_CONFIGURE
156 lconfig.fd = ffd;
157 rc = ioctl(lfd, LOOP_CONFIGURE, &lconfig);
158 if (rc == 0)
159 return rc;
160# if ENABLE_TRY_LOOP_CONFIGURE
161 if (errno != EINVAL)
162 return rc;
163
164# endif
165#endif
166#if ENABLE_TRY_LOOP_CONFIGURE || ENABLE_NO_LOOP_CONFIGURE
167
168 rc = ioctl(lfd, LOOP_SET_FD, ffd);
169 if (rc != 0) {
170
171 return rc;
172 }
173 rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo);
174 if (rc != 0 && (loopinfo.lo_flags & BB_LO_FLAGS_AUTOCLEAR)) {
175
176
177 loopinfo.lo_flags -= BB_LO_FLAGS_AUTOCLEAR;
178 rc = ioctl(lfd, BB_LOOP_SET_STATUS, &loopinfo);
179 }
180 if (rc == 0)
181 return rc;
182
183 ioctl(lfd, LOOP_CLR_FD, 0);
184#endif
185 }
186 return -1;
187#undef loopinfo
188}
189
190
191
192
193
194int FAST_FUNC set_loop(char **device, const char *file, unsigned long long offset,
195 unsigned long long sizelimit, unsigned flags)
196{
197 char dev[LOOP_NAMESIZE];
198 char *try;
199 struct stat statbuf;
200 int i, lfd, ffd, mode, rc;
201
202
203 mode = (flags & BB_LO_FLAGS_READ_ONLY) ? O_RDONLY : O_RDWR;
204 open_ffd:
205 ffd = open(file, mode);
206 if (ffd < 0) {
207 if (mode != O_RDONLY) {
208 mode = O_RDONLY;
209 goto open_ffd;
210 }
211 return -errno;
212 }
213
214 try = *device;
215 if (!try) {
216 try = dev;
217 }
218
219
220
221 for (i = 0; i <= 0xfffff; i++) {
222 if (!*device) {
223 rc = get_next_free_loop(dev, i);
224 if (rc == -1)
225 break;
226 if (rc >= 0)
227
228 goto open_lfd;
229
230 }
231
232 IF_FEATURE_MOUNT_LOOP_CREATE(errno = 0;)
233 if (stat(try, &statbuf) != 0 || !S_ISBLK(statbuf.st_mode)) {
234 if (ENABLE_FEATURE_MOUNT_LOOP_CREATE
235 && errno == ENOENT
236 && (!*device)
237 ) {
238
239 if (mknod(dev, S_IFBLK|0644, makedev(7, i)) == 0)
240 goto open_lfd;
241 }
242
243 rc = -1;
244 break;
245 }
246 open_lfd:
247
248 lfd = rc = open(try, mode);
249 if (lfd < 0 && errno == EROFS) {
250 mode = O_RDONLY;
251 lfd = rc = open(try, mode);
252 }
253 if (lfd < 0) {
254 if (errno == ENXIO) {
255
256
257 break;
258 }
259 goto try_next_loopN;
260 }
261
262 rc = set_loopdev_params(lfd, ffd, file, offset, sizelimit, flags);
263 if (rc == 0) {
264
265 if (!*device)
266 *device = xstrdup(dev);
267
268
269
270
271
272 rc = lfd;
273 break;
274 }
275 close(lfd);
276 try_next_loopN:
277 rc = -1;
278 if (*device)
279 break;
280 }
281
282 close(ffd);
283 return rc;
284}
285