]>
Commit | Line | Data |
---|---|---|
7f8b2bf3 KZ |
1 | /* |
2 | * Copyright (C) 2011 Karel Zak <kzak@redhat.com> | |
3 | * | |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
6 | */ | |
7 | ||
8 | /* | |
9 | * DOCS: - "lo@" prefix for fstype is unsupported | |
10 | * - encyption= mount option for loop device is unssuported | |
11 | */ | |
12 | ||
13 | #include <blkid.h> | |
14 | ||
15 | #include "mountP.h" | |
16 | #include "loopdev.h" | |
17 | #include "linux_version.h" | |
18 | ||
19 | ||
20 | int mnt_context_is_loopdev(struct libmnt_context *cxt) | |
21 | { | |
22 | const char *type, *src; | |
7f8b2bf3 KZ |
23 | |
24 | assert(cxt); | |
c8512236 | 25 | |
7f8b2bf3 KZ |
26 | /* The mount flags have to be merged, otherwise we have to use |
27 | * expensive mnt_context_get_user_mflags() instead of cxt->user_mountflags. */ | |
28 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
29 | ||
30 | if (!cxt->fs) | |
31 | return 0; | |
32 | src = mnt_fs_get_srcpath(cxt->fs); | |
33 | if (!src) | |
34 | return 0; /* backing file not set */ | |
35 | ||
36 | if (cxt->user_mountflags & (MNT_MS_LOOP | | |
37 | MNT_MS_OFFSET | | |
1a7a421e | 38 | MNT_MS_SIZELIMIT | |
c8512236 KZ |
39 | MNT_MS_ENCRYPTION)) { |
40 | ||
41 | DBG(CXT, mnt_debug_h(cxt, "loopdev specific options detected")); | |
7f8b2bf3 | 42 | return 1; |
c8512236 | 43 | } |
7f8b2bf3 KZ |
44 | |
45 | if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) | |
46 | return 0; | |
47 | ||
c8512236 KZ |
48 | /* Automatically create a loop device from a regular file if a |
49 | * filesystem is not specified or the filesystem is known for libblkid | |
50 | * (these filesystems work with block devices only). The file size | |
51 | * should be at least 1KiB otherwise we will create empty loopdev where | |
52 | * is no mountable filesystem... | |
7f8b2bf3 KZ |
53 | * |
54 | * Note that there is not a restriction (on kernel side) that prevents regular | |
55 | * file as a mount(2) source argument. A filesystem that is able to mount | |
56 | * regular files could be implemented. | |
57 | */ | |
58 | type = mnt_fs_get_fstype(cxt->fs); | |
7f8b2bf3 | 59 | |
c70d9d76 | 60 | if (mnt_fs_is_regular(cxt->fs) && |
7f8b2bf3 KZ |
61 | (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { |
62 | struct stat st; | |
63 | ||
c8512236 KZ |
64 | if (stat(src, &st) == 0 && S_ISREG(st.st_mode) && |
65 | st.st_size > 1024) | |
66 | return 1; | |
7f8b2bf3 KZ |
67 | } |
68 | ||
c8512236 | 69 | return 0; |
7f8b2bf3 KZ |
70 | } |
71 | ||
8b470b20 KZ |
72 | |
73 | /* Check, if there already exists a mounted loop device on the mountpoint node | |
74 | * with the same parameters. | |
75 | */ | |
76 | static int is_mounted_same_loopfile(struct libmnt_context *cxt, | |
77 | const char *target, | |
78 | const char *backing_file, | |
79 | uint64_t offset) | |
80 | { | |
81 | struct libmnt_table *tb; | |
82 | struct libmnt_iter itr; | |
83 | struct libmnt_fs *fs; | |
84 | struct libmnt_cache *cache; | |
85 | ||
86 | assert(cxt); | |
87 | assert(cxt->fs); | |
88 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
89 | ||
90 | if (!target || !backing_file || mnt_context_get_mtab(cxt, &tb)) | |
91 | return 0; | |
92 | ||
93 | DBG(CXT, mnt_debug_h(cxt, "checking if %s mounted on %d", | |
94 | backing_file, target)); | |
95 | ||
96 | cache = mnt_context_get_cache(cxt); | |
97 | mnt_reset_iter(&itr, MNT_ITER_BACKWARD); | |
98 | ||
99 | /* Search for mountpoint node in mtab, procceed if any of these has the | |
100 | * loop option set or the device is a loop device | |
101 | */ | |
102 | while (mnt_table_next_fs(tb, &itr, &fs) == 0) { | |
103 | const char *src = mnt_fs_get_source(fs); | |
104 | const char *opts = mnt_fs_get_user_options(fs); | |
105 | char *val; | |
106 | size_t len; | |
107 | int res = 0; | |
108 | ||
109 | if (!src || !mnt_fs_match_target(fs, target, cache)) | |
110 | continue; | |
111 | ||
112 | if (strncmp(src, "/dev/loop", 9) == 0) { | |
113 | res = loopdev_is_used((char *) src, backing_file, | |
114 | offset, LOOPDEV_FL_OFFSET); | |
115 | ||
116 | } else if (opts && (cxt->user_mountflags & MNT_MS_LOOP) && | |
117 | mnt_optstr_get_option(opts, "loop", &val, &len) == 0 && val) { | |
118 | ||
119 | val = strndup(val, len); | |
120 | res = loopdev_is_used((char *) val, backing_file, | |
121 | offset, LOOPDEV_FL_OFFSET); | |
122 | free(val); | |
123 | } | |
124 | ||
125 | if (res) { | |
126 | DBG(CXT, mnt_debug_h(cxt, "%s already mounted", backing_file)); | |
127 | return 1; | |
128 | } | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
7f8b2bf3 KZ |
134 | int mnt_context_setup_loopdev(struct libmnt_context *cxt) |
135 | { | |
f0d3ff0a | 136 | const char *backing_file, *optstr, *loopdev = NULL; |
1a7a421e | 137 | char *val = NULL, *enc = NULL, *pwd = NULL; |
7f8b2bf3 KZ |
138 | size_t len; |
139 | struct loopdev_cxt lc; | |
b9fd3340 KZ |
140 | int rc = 0, lo_flags = 0; |
141 | uint64_t offset = 0, sizelimit = 0; | |
7f8b2bf3 KZ |
142 | |
143 | assert(cxt); | |
144 | assert(cxt->fs); | |
145 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
146 | ||
147 | backing_file = mnt_fs_get_srcpath(cxt->fs); | |
148 | if (!backing_file) | |
149 | return -EINVAL; | |
150 | ||
151 | DBG(CXT, mnt_debug_h(cxt, "trying to setup loopdev for %s", backing_file)); | |
152 | ||
153 | if (cxt->mountflags & MS_RDONLY) { | |
154 | DBG(CXT, mnt_debug_h(cxt, "enabling READ-ONLY flag")); | |
155 | lo_flags |= LO_FLAGS_READ_ONLY; | |
156 | } | |
365e5a7c | 157 | |
7f8b2bf3 KZ |
158 | loopcxt_init(&lc, 0); |
159 | ||
365e5a7c KZ |
160 | ON_DBG(CXT, loopcxt_enable_debug(&lc, 1)); |
161 | ||
b9fd3340 | 162 | optstr = mnt_fs_get_user_options(cxt->fs); |
7f8b2bf3 | 163 | |
b9fd3340 KZ |
164 | /* |
165 | * loop= | |
166 | */ | |
167 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) && | |
168 | mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) { | |
169 | ||
170 | val = strndup(val, len); | |
171 | rc = val ? loopcxt_set_device(&lc, val) : -ENOMEM; | |
172 | free(val); | |
f0d3ff0a KZ |
173 | |
174 | if (rc == 0) | |
175 | loopdev = loopcxt_get_device(&lc); | |
b9fd3340 KZ |
176 | } |
177 | ||
178 | /* | |
179 | * offset= | |
180 | */ | |
181 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) && | |
182 | mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) { | |
183 | rc = mnt_parse_offset(val, len, &offset); | |
184 | if (rc) | |
185 | DBG(CXT, mnt_debug_h(cxt, "failed to parse offset=")); | |
7f8b2bf3 KZ |
186 | } |
187 | ||
b9fd3340 KZ |
188 | /* |
189 | * sizelimit= | |
190 | */ | |
191 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) && | |
192 | mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) { | |
193 | rc = mnt_parse_offset(val, len, &sizelimit); | |
194 | if (rc) | |
195 | DBG(CXT, mnt_debug_h(cxt, "failed to parse sizelimit=")); | |
196 | } | |
197 | ||
1a7a421e KZ |
198 | /* |
199 | * encryption= | |
200 | */ | |
201 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_ENCRYPTION) && | |
202 | mnt_optstr_get_option(optstr, "encryption", &val, &len) == 0) { | |
203 | enc = strndup(val, len); | |
204 | if (val && !enc) | |
205 | rc = -ENOMEM; | |
206 | if (enc && cxt->pwd_get_cb) { | |
207 | DBG(CXT, mnt_debug_h(cxt, "asking for pass")); | |
208 | pwd = cxt->pwd_get_cb(cxt); | |
209 | } | |
210 | } | |
211 | ||
8b470b20 KZ |
212 | if (rc == 0 && is_mounted_same_loopfile(cxt, |
213 | mnt_context_get_target(cxt), | |
214 | backing_file, offset)) | |
215 | rc = -EBUSY; | |
216 | ||
b9fd3340 KZ |
217 | if (rc) |
218 | goto done; | |
219 | ||
7f8b2bf3 KZ |
220 | /* since 2.6.37 we don't have to store backing filename to mtab |
221 | * because kernel provides the name in /sys. | |
222 | */ | |
223 | if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || | |
224 | !cxt->mtab_writable) { | |
225 | DBG(CXT, mnt_debug_h(cxt, "enabling AUTOCLEAR flag")); | |
226 | lo_flags |= LO_FLAGS_AUTOCLEAR; | |
227 | } | |
228 | ||
229 | do { | |
230 | /* found free device */ | |
231 | if (!loopdev) { | |
232 | rc = loopcxt_find_unused(&lc); | |
233 | if (rc) | |
234 | goto done; | |
235 | DBG(CXT, mnt_debug_h(cxt, "trying to use %s", | |
236 | loopcxt_get_device(&lc))); | |
237 | } | |
238 | ||
b9fd3340 KZ |
239 | /* set device attributes |
240 | * -- note that loopcxt_find_unused() resets "lc" | |
241 | */ | |
7f8b2bf3 | 242 | rc = loopcxt_set_backing_file(&lc, backing_file); |
7f8b2bf3 | 243 | |
b9fd3340 KZ |
244 | if (!rc && offset) |
245 | rc = loopcxt_set_offset(&lc, offset); | |
246 | if (!rc && sizelimit) | |
247 | rc = loopcxt_set_sizelimit(&lc, sizelimit); | |
1a7a421e KZ |
248 | if (!rc && enc && pwd) |
249 | loopcxt_set_encryption(&lc, enc, pwd); | |
b9fd3340 KZ |
250 | if (!rc) |
251 | loopcxt_set_flags(&lc, lo_flags); | |
252 | if (rc) { | |
253 | DBG(CXT, mnt_debug_h(cxt, "failed to set loopdev attributes")); | |
254 | goto done; | |
255 | } | |
7f8b2bf3 KZ |
256 | |
257 | /* setup the device */ | |
258 | rc = loopcxt_setup_device(&lc); | |
259 | if (!rc) | |
260 | break; /* success */ | |
261 | ||
262 | if (loopdev || rc != -EBUSY) { | |
263 | DBG(CXT, mnt_debug_h(cxt, "failed to setup device")); | |
b9fd3340 | 264 | goto done; |
7f8b2bf3 KZ |
265 | } |
266 | DBG(CXT, mnt_debug_h(cxt, "loopdev stolen...trying again")); | |
267 | } while (1); | |
268 | ||
269 | if (!rc) | |
270 | rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); | |
271 | ||
272 | if (!rc) { | |
273 | /* success */ | |
274 | cxt->flags |= MNT_FL_LOOPDEV_READY; | |
275 | ||
276 | if ((cxt->user_mountflags & MNT_MS_LOOP) && | |
f0d3ff0a | 277 | loopcxt_is_autoclear(&lc)) { |
7f8b2bf3 KZ |
278 | /* |
279 | * autoclear flag accepted by kernel, don't store | |
280 | * the "loop=" option to mtab. | |
281 | */ | |
282 | cxt->user_mountflags &= ~MNT_MS_LOOP; | |
f0d3ff0a KZ |
283 | mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop"); |
284 | } | |
7f8b2bf3 KZ |
285 | |
286 | if (!(cxt->mountflags & MS_RDONLY) && | |
287 | loopcxt_is_readonly(&lc)) | |
288 | /* | |
289 | * mount planned read-write, but loopdev is read-only, | |
290 | * let's fix mount options... | |
291 | */ | |
292 | cxt->mountflags |= MS_RDONLY; | |
293 | ||
294 | ||
295 | /* we have to keep the device open until mount(1), | |
296 | * otherwise it will auto-cleared by kernel | |
297 | */ | |
298 | cxt->loopdev_fd = loopcxt_get_fd(&lc); | |
299 | loopcxt_set_fd(&lc, -1, 0); | |
300 | } | |
301 | done: | |
1a7a421e KZ |
302 | free(enc); |
303 | if (pwd && cxt->pwd_release_cb) { | |
304 | DBG(CXT, mnt_debug_h(cxt, "release pass")); | |
305 | cxt->pwd_release_cb(cxt, pwd); | |
306 | } | |
7f8b2bf3 KZ |
307 | loopcxt_deinit(&lc); |
308 | return rc; | |
309 | } | |
310 | ||
311 | /* | |
312 | * Deletes loop device | |
313 | */ | |
314 | int mnt_context_delete_loopdev(struct libmnt_context *cxt) | |
315 | { | |
316 | const char *src; | |
317 | int rc; | |
318 | ||
319 | assert(cxt); | |
320 | assert(cxt->fs); | |
321 | ||
322 | src = mnt_fs_get_srcpath(cxt->fs); | |
323 | if (!src) | |
324 | return -EINVAL; | |
325 | ||
326 | if (cxt->loopdev_fd > -1) | |
327 | close(cxt->loopdev_fd); | |
328 | ||
329 | rc = loopdev_delete(src); | |
330 | cxt->flags &= ~MNT_FL_LOOPDEV_READY; | |
331 | cxt->loopdev_fd = -1; | |
332 | ||
333 | DBG(CXT, mnt_debug_h(cxt, "loopdev deleted [rc=%d]", rc)); | |
334 | return rc; | |
335 | } | |
336 | ||
337 | /* | |
338 | * Clears loopdev stuff in context, should be called after | |
339 | * failed or successful mount(2). | |
340 | */ | |
341 | int mnt_context_clear_loopdev(struct libmnt_context *cxt) | |
342 | { | |
343 | assert(cxt); | |
344 | ||
345 | if (mnt_context_get_status(cxt) == 0 && | |
346 | (cxt->flags & MNT_FL_LOOPDEV_READY)) { | |
347 | /* | |
348 | * mount(2) failed, delete loopdev | |
349 | */ | |
350 | mnt_context_delete_loopdev(cxt); | |
351 | ||
352 | } else if (cxt->loopdev_fd > -1) { | |
353 | /* | |
354 | * mount(2) success, close the device | |
355 | */ | |
356 | DBG(CXT, mnt_debug_h(cxt, "closing loopdev FD")); | |
357 | close(cxt->loopdev_fd); | |
358 | } | |
359 | cxt->loopdev_fd = -1; | |
360 | return 0; | |
361 | } | |
362 |