]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libmount/src/context_loopdev.c
libmount: consolidate MNT_FS_* code
[thirdparty/util-linux.git] / libmount / src / context_loopdev.c
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;
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);
52
53 if (mnt_fs_is_regular(cxt->fs) &&
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 {
66 const char *backing_file, *optstr, *loopdev = NULL;
67 char *val = NULL;
68 size_t len;
69 struct loopdev_cxt lc;
70 int rc = 0, lo_flags = 0;
71 uint64_t offset = 0, sizelimit = 0;
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 }
87
88 loopcxt_init(&lc, 0);
89
90 ON_DBG(CXT, loopcxt_enable_debug(&lc, 1));
91
92 optstr = mnt_fs_get_user_options(cxt->fs);
93
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);
103
104 if (rc == 0)
105 loopdev = loopcxt_get_device(&lc);
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="));
116 }
117
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
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
150 /* set device attributes
151 * -- note that loopcxt_find_unused() resets "lc"
152 */
153 rc = loopcxt_set_backing_file(&lc, backing_file);
154
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 }
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"));
173 goto done;
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) &&
186 loopcxt_is_autoclear(&lc)) {
187 /*
188 * autoclear flag accepted by kernel, don't store
189 * the "loop=" option to mtab.
190 */
191 cxt->user_mountflags &= ~MNT_MS_LOOP;
192 mnt_optstr_remove_option(&cxt->fs->user_optstr, "loop");
193 }
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