]>
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); | |
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 | | |
37 | MNT_MS_SIZELIMIT)) | |
38 | return 1; | |
39 | ||
40 | if (cxt->mountflags & (MS_BIND | MS_MOVE | MS_PROPAGATION)) | |
41 | return 0; | |
42 | ||
43 | /* Automatically create a loop device from a regular file if a filesystem | |
44 | * is not specified or the filesystem is known for libblkid (these | |
45 | * filesystems work with block devices only). | |
46 | * | |
47 | * Note that there is not a restriction (on kernel side) that prevents regular | |
48 | * file as a mount(2) source argument. A filesystem that is able to mount | |
49 | * regular files could be implemented. | |
50 | */ | |
51 | type = mnt_fs_get_fstype(cxt->fs); | |
7f8b2bf3 | 52 | |
c70d9d76 | 53 | if (mnt_fs_is_regular(cxt->fs) && |
7f8b2bf3 KZ |
54 | (!type || strcmp(type, "auto") == 0 || blkid_known_fstype(type))) { |
55 | struct stat st; | |
56 | ||
57 | if (stat(src, &st) || !S_ISREG(st.st_mode)) | |
58 | return 0; | |
59 | } | |
60 | ||
61 | return 1; | |
62 | } | |
63 | ||
64 | int mnt_context_setup_loopdev(struct libmnt_context *cxt) | |
65 | { | |
f0d3ff0a KZ |
66 | const char *backing_file, *optstr, *loopdev = NULL; |
67 | char *val = NULL; | |
7f8b2bf3 KZ |
68 | size_t len; |
69 | struct loopdev_cxt lc; | |
b9fd3340 KZ |
70 | int rc = 0, lo_flags = 0; |
71 | uint64_t offset = 0, sizelimit = 0; | |
7f8b2bf3 KZ |
72 | |
73 | assert(cxt); | |
74 | assert(cxt->fs); | |
75 | assert((cxt->flags & MNT_FL_MOUNTFLAGS_MERGED)); | |
76 | ||
77 | backing_file = mnt_fs_get_srcpath(cxt->fs); | |
78 | if (!backing_file) | |
79 | return -EINVAL; | |
80 | ||
81 | DBG(CXT, mnt_debug_h(cxt, "trying to setup loopdev for %s", backing_file)); | |
82 | ||
83 | if (cxt->mountflags & MS_RDONLY) { | |
84 | DBG(CXT, mnt_debug_h(cxt, "enabling READ-ONLY flag")); | |
85 | lo_flags |= LO_FLAGS_READ_ONLY; | |
86 | } | |
365e5a7c | 87 | |
7f8b2bf3 KZ |
88 | loopcxt_init(&lc, 0); |
89 | ||
365e5a7c KZ |
90 | ON_DBG(CXT, loopcxt_enable_debug(&lc, 1)); |
91 | ||
b9fd3340 | 92 | optstr = mnt_fs_get_user_options(cxt->fs); |
7f8b2bf3 | 93 | |
b9fd3340 KZ |
94 | /* |
95 | * loop= | |
96 | */ | |
97 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_LOOP) && | |
98 | mnt_optstr_get_option(optstr, "loop", &val, &len) == 0 && val) { | |
99 | ||
100 | val = strndup(val, len); | |
101 | rc = val ? loopcxt_set_device(&lc, val) : -ENOMEM; | |
102 | free(val); | |
f0d3ff0a KZ |
103 | |
104 | if (rc == 0) | |
105 | loopdev = loopcxt_get_device(&lc); | |
b9fd3340 KZ |
106 | } |
107 | ||
108 | /* | |
109 | * offset= | |
110 | */ | |
111 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_OFFSET) && | |
112 | mnt_optstr_get_option(optstr, "offset", &val, &len) == 0) { | |
113 | rc = mnt_parse_offset(val, len, &offset); | |
114 | if (rc) | |
115 | DBG(CXT, mnt_debug_h(cxt, "failed to parse offset=")); | |
7f8b2bf3 KZ |
116 | } |
117 | ||
b9fd3340 KZ |
118 | /* |
119 | * sizelimit= | |
120 | */ | |
121 | if (rc == 0 && (cxt->user_mountflags & MNT_MS_SIZELIMIT) && | |
122 | mnt_optstr_get_option(optstr, "sizelimit", &val, &len) == 0) { | |
123 | rc = mnt_parse_offset(val, len, &sizelimit); | |
124 | if (rc) | |
125 | DBG(CXT, mnt_debug_h(cxt, "failed to parse sizelimit=")); | |
126 | } | |
127 | ||
128 | if (rc) | |
129 | goto done; | |
130 | ||
7f8b2bf3 KZ |
131 | /* since 2.6.37 we don't have to store backing filename to mtab |
132 | * because kernel provides the name in /sys. | |
133 | */ | |
134 | if (get_linux_version() >= KERNEL_VERSION(2, 6, 37) || | |
135 | !cxt->mtab_writable) { | |
136 | DBG(CXT, mnt_debug_h(cxt, "enabling AUTOCLEAR flag")); | |
137 | lo_flags |= LO_FLAGS_AUTOCLEAR; | |
138 | } | |
139 | ||
140 | do { | |
141 | /* found free device */ | |
142 | if (!loopdev) { | |
143 | rc = loopcxt_find_unused(&lc); | |
144 | if (rc) | |
145 | goto done; | |
146 | DBG(CXT, mnt_debug_h(cxt, "trying to use %s", | |
147 | loopcxt_get_device(&lc))); | |
148 | } | |
149 | ||
b9fd3340 KZ |
150 | /* set device attributes |
151 | * -- note that loopcxt_find_unused() resets "lc" | |
152 | */ | |
7f8b2bf3 | 153 | rc = loopcxt_set_backing_file(&lc, backing_file); |
7f8b2bf3 | 154 | |
b9fd3340 KZ |
155 | if (!rc && offset) |
156 | rc = loopcxt_set_offset(&lc, offset); | |
157 | if (!rc && sizelimit) | |
158 | rc = loopcxt_set_sizelimit(&lc, sizelimit); | |
159 | if (!rc) | |
160 | loopcxt_set_flags(&lc, lo_flags); | |
161 | if (rc) { | |
162 | DBG(CXT, mnt_debug_h(cxt, "failed to set loopdev attributes")); | |
163 | goto done; | |
164 | } | |
7f8b2bf3 KZ |
165 | |
166 | /* setup the device */ | |
167 | rc = loopcxt_setup_device(&lc); | |
168 | if (!rc) | |
169 | break; /* success */ | |
170 | ||
171 | if (loopdev || rc != -EBUSY) { | |
172 | DBG(CXT, mnt_debug_h(cxt, "failed to setup device")); | |
b9fd3340 | 173 | goto done; |
7f8b2bf3 KZ |
174 | } |
175 | DBG(CXT, mnt_debug_h(cxt, "loopdev stolen...trying again")); | |
176 | } while (1); | |
177 | ||
178 | if (!rc) | |
179 | rc = mnt_fs_set_source(cxt->fs, loopcxt_get_device(&lc)); | |
180 | ||
181 | if (!rc) { | |
182 | /* success */ | |
183 | cxt->flags |= MNT_FL_LOOPDEV_READY; | |
184 | ||
185 | if ((cxt->user_mountflags & MNT_MS_LOOP) && | |
f0d3ff0a | 186 | loopcxt_is_autoclear(&lc)) { |
7f8b2bf3 KZ |
187 | /* |
188 | * autoclear flag accepted by kernel, don't store | |
189 | * the "loop=" option to mtab. | |
190 | */ | |
191 | cxt->user_mountflags &= ~MNT_MS_LOOP; | |
f0d3ff0a KZ |
192 | mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop"); |
193 | } | |
7f8b2bf3 KZ |
194 | |
195 | if (!(cxt->mountflags & MS_RDONLY) && | |
196 | loopcxt_is_readonly(&lc)) | |
197 | /* | |
198 | * mount planned read-write, but loopdev is read-only, | |
199 | * let's fix mount options... | |
200 | */ | |
201 | cxt->mountflags |= MS_RDONLY; | |
202 | ||
203 | ||
204 | /* we have to keep the device open until mount(1), | |
205 | * otherwise it will auto-cleared by kernel | |
206 | */ | |
207 | cxt->loopdev_fd = loopcxt_get_fd(&lc); | |
208 | loopcxt_set_fd(&lc, -1, 0); | |
209 | } | |
210 | done: | |
211 | loopcxt_deinit(&lc); | |
212 | return rc; | |
213 | } | |
214 | ||
215 | /* | |
216 | * Deletes loop device | |
217 | */ | |
218 | int mnt_context_delete_loopdev(struct libmnt_context *cxt) | |
219 | { | |
220 | const char *src; | |
221 | int rc; | |
222 | ||
223 | assert(cxt); | |
224 | assert(cxt->fs); | |
225 | ||
226 | src = mnt_fs_get_srcpath(cxt->fs); | |
227 | if (!src) | |
228 | return -EINVAL; | |
229 | ||
230 | if (cxt->loopdev_fd > -1) | |
231 | close(cxt->loopdev_fd); | |
232 | ||
233 | rc = loopdev_delete(src); | |
234 | cxt->flags &= ~MNT_FL_LOOPDEV_READY; | |
235 | cxt->loopdev_fd = -1; | |
236 | ||
237 | DBG(CXT, mnt_debug_h(cxt, "loopdev deleted [rc=%d]", rc)); | |
238 | return rc; | |
239 | } | |
240 | ||
241 | /* | |
242 | * Clears loopdev stuff in context, should be called after | |
243 | * failed or successful mount(2). | |
244 | */ | |
245 | int mnt_context_clear_loopdev(struct libmnt_context *cxt) | |
246 | { | |
247 | assert(cxt); | |
248 | ||
249 | if (mnt_context_get_status(cxt) == 0 && | |
250 | (cxt->flags & MNT_FL_LOOPDEV_READY)) { | |
251 | /* | |
252 | * mount(2) failed, delete loopdev | |
253 | */ | |
254 | mnt_context_delete_loopdev(cxt); | |
255 | ||
256 | } else if (cxt->loopdev_fd > -1) { | |
257 | /* | |
258 | * mount(2) success, close the device | |
259 | */ | |
260 | DBG(CXT, mnt_debug_h(cxt, "closing loopdev FD")); | |
261 | close(cxt->loopdev_fd); | |
262 | } | |
263 | cxt->loopdev_fd = -1; | |
264 | return 0; | |
265 | } | |
266 |