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