]>
Commit | Line | Data |
---|---|---|
6c40a53d KZ |
1 | /* |
2 | * Copyright (C) 2010 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 | /** | |
30493710 | 9 | * SECTION: update |
0f32f1e2 KZ |
10 | * @title: mtab managment |
11 | * @short_description: userspace mount information management. | |
12 | * | |
13 | * The mnt_update provides abstraction to manage mount options in userspace independently on | |
14 | * system configuration. This low-level API works on system with and without /etc/mtab. On | |
15 | * systems without the regular /etc/mtab file are userspace mount options (e.g. user=) | |
16 | * stored to the /dev/.mount/utab file. | |
17 | * | |
18 | * It's recommended to use high-level mnt_context API. | |
6c40a53d KZ |
19 | */ |
20 | ||
21 | #include <stdio.h> | |
22 | #include <stdlib.h> | |
23 | #include <sys/types.h> | |
24 | #include <sys/stat.h> | |
1d0cd73f KZ |
25 | #include <sys/file.h> |
26 | #include <fcntl.h> | |
6c40a53d KZ |
27 | #include <unistd.h> |
28 | #include <string.h> | |
29 | #include <errno.h> | |
30 | ||
31 | #include "c.h" | |
32 | #include "mountP.h" | |
33 | #include "mangle.h" | |
34 | #include "pathnames.h" | |
35 | ||
30493710 | 36 | struct _mnt_update { |
1d0cd73f KZ |
37 | char *target; |
38 | mnt_fs *fs; | |
77417bc0 | 39 | char *filename; |
1d0cd73f KZ |
40 | unsigned long mountflags; |
41 | int userspace_only; | |
42 | int ready; | |
6c40a53d KZ |
43 | }; |
44 | ||
1d0cd73f | 45 | static int utab_new_entry(mnt_fs *fs, unsigned long mountflags, mnt_fs **ent); |
766af80b | 46 | static int set_fs_root(mnt_fs *result, mnt_fs *fs, unsigned long mountflags); |
1d0cd73f | 47 | |
6c40a53d | 48 | /** |
30493710 | 49 | * mnt_new_update: |
6c40a53d | 50 | * |
1d0cd73f | 51 | * Returns: newly allocated update handler |
6c40a53d | 52 | */ |
77417bc0 | 53 | mnt_update *mnt_new_update(void) |
6c40a53d | 54 | { |
30493710 | 55 | mnt_update *upd; |
6c40a53d | 56 | |
30493710 KZ |
57 | upd = calloc(1, sizeof(struct _mnt_update)); |
58 | if (!upd) | |
6c40a53d KZ |
59 | return NULL; |
60 | ||
4e92d2b0 | 61 | DBG(UPDATE, mnt_debug_h(upd, "allocate")); |
6c40a53d | 62 | |
30493710 | 63 | return upd; |
6c40a53d KZ |
64 | } |
65 | ||
66 | /** | |
30493710 KZ |
67 | * mnt_free_update: |
68 | * @upd: update | |
6c40a53d | 69 | * |
30493710 | 70 | * Deallocates mnt_update handler. |
6c40a53d | 71 | */ |
30493710 | 72 | void mnt_free_update(mnt_update *upd) |
6c40a53d | 73 | { |
30493710 | 74 | if (!upd) |
6c40a53d KZ |
75 | return; |
76 | ||
4e92d2b0 | 77 | DBG(UPDATE, mnt_debug_h(upd, "free")); |
6c40a53d | 78 | |
1d0cd73f KZ |
79 | mnt_free_fs(upd->fs); |
80 | free(upd->target); | |
77417bc0 | 81 | free(upd->filename); |
30493710 | 82 | free(upd); |
6c40a53d KZ |
83 | } |
84 | ||
77417bc0 KZ |
85 | /* |
86 | * Returns 0 on success, 1 if not file available, -1 in case of error. | |
87 | */ | |
88 | int mnt_update_set_filename(mnt_update *upd, const char *filename, int userspace_only) | |
89 | { | |
90 | const char *path = NULL; | |
91 | int rw = 0; | |
92 | ||
93 | assert(upd); | |
94 | ||
95 | /* filename explicitly defined */ | |
96 | if (filename) { | |
97 | char *p = strdup(filename); | |
98 | if (!p) | |
99 | return -ENOMEM; | |
100 | ||
101 | upd->userspace_only = userspace_only; | |
102 | free(upd->filename); | |
103 | upd->filename = p; | |
104 | } | |
105 | ||
106 | if (upd->filename) | |
107 | return 0; | |
108 | ||
109 | /* detect tab filename -- /etc/mtab or /dev/.mount/utab | |
110 | */ | |
111 | mnt_has_regular_mtab(&path, &rw); | |
112 | if (!rw) { | |
113 | path = NULL; | |
114 | mnt_has_regular_utab(&path, &rw); | |
115 | if (!rw) | |
116 | return 1; | |
117 | upd->userspace_only = TRUE; | |
118 | } | |
119 | upd->filename = strdup(path); | |
120 | if (!upd->filename) | |
121 | return -ENOMEM; | |
122 | ||
123 | return 0; | |
124 | } | |
125 | ||
36bda5cb KZ |
126 | /** |
127 | * mnt_update_get_filename: | |
128 | * @upd: update | |
129 | * | |
130 | * This function returns file name (e.g. /etc/mtab) if the update | |
131 | * should be covered by mnt_lock, otherwise returne NULL. | |
132 | * | |
133 | * Returns: pointer to filename that will be updated or NULL in case of error. | |
134 | */ | |
135 | const char *mnt_update_get_filename(mnt_update *upd) | |
136 | { | |
137 | return upd && !upd->userspace_only ? upd->filename : NULL; | |
138 | } | |
139 | ||
6c40a53d | 140 | /** |
1d0cd73f KZ |
141 | * mnt_update_is_ready: |
142 | * @upd: update handler | |
d300992d | 143 | * |
1d0cd73f KZ |
144 | * Returns: 1 if entry described by @upd is successfully prepared and will be |
145 | * written to mtab/utab file. | |
d300992d | 146 | */ |
1d0cd73f | 147 | int mnt_update_is_ready(mnt_update *upd) |
d300992d | 148 | { |
1d0cd73f | 149 | return upd ? upd->ready : FALSE; |
6c40a53d KZ |
150 | } |
151 | ||
6c40a53d | 152 | /** |
30493710 | 153 | * mnt_update_set_fs: |
1d0cd73f KZ |
154 | * @upd: update handler |
155 | * @mountflags: MS_* flags | |
2915bdc0 KZ |
156 | * @target: umount target, must be num for mount |
157 | * @fs: mount filesystem description, must be NULL for umount | |
d300992d | 158 | * |
1d0cd73f | 159 | * Returns: -1 in case on error, 0 on success, 1 if update is unnecessary. |
6c40a53d | 160 | */ |
36bda5cb | 161 | int mnt_update_set_fs(mnt_update *upd, unsigned long mountflags, |
1d0cd73f | 162 | const char *target, mnt_fs *fs) |
6c40a53d | 163 | { |
77417bc0 KZ |
164 | int rc; |
165 | ||
30493710 | 166 | assert(upd); |
1d0cd73f | 167 | assert(target || fs); |
7714c689 | 168 | |
30493710 | 169 | if (!upd) |
3a5b1b1d | 170 | return -EINVAL; |
2915bdc0 KZ |
171 | if ((mountflags & MS_MOVE) && (!fs || !mnt_fs_get_srcpath(fs))) |
172 | return -EINVAL; | |
173 | if (target && fs) | |
174 | return -EINVAL; | |
6c40a53d | 175 | |
1d0cd73f | 176 | DBG(UPDATE, mnt_debug_h(upd, |
36bda5cb | 177 | "reseting FS [fs=0x%p, target=%s, flags=0x%08lx]", |
1d0cd73f | 178 | fs, target, mountflags)); |
87a07a4c KZ |
179 | if (fs) { |
180 | DBG(UPDATE, mnt_debug_h(upd, "FS template:")); | |
181 | DBG(UPDATE, mnt_fs_print_debug(fs, stderr)); | |
182 | } | |
6c40a53d | 183 | |
1d0cd73f KZ |
184 | mnt_free_fs(upd->fs); |
185 | free(upd->target); | |
186 | upd->ready = FALSE; | |
187 | upd->fs = NULL; | |
188 | upd->target = NULL; | |
36bda5cb KZ |
189 | upd->mountflags = 0; |
190 | ||
191 | if (mountflags & MS_PROPAGATION) | |
192 | return 1; | |
193 | ||
1d0cd73f | 194 | upd->mountflags = mountflags; |
6c40a53d | 195 | |
77417bc0 KZ |
196 | rc = mnt_update_set_filename(upd, NULL, 0); |
197 | if (rc) | |
198 | return rc; /* error or no file available (rc = 1) */ | |
199 | ||
2915bdc0 KZ |
200 | if (target) { |
201 | upd->target = strdup(target); | |
202 | if (!upd->target) | |
203 | return -ENOMEM; | |
204 | ||
205 | } else if (fs) { | |
1d0cd73f KZ |
206 | if (upd->userspace_only && !(mountflags & MS_MOVE)) { |
207 | int rc = utab_new_entry(fs, mountflags, &upd->fs); | |
208 | if (rc) | |
209 | return rc; | |
210 | } else { | |
f84fa6f7 | 211 | upd->fs = mnt_copy_mtab_fs(fs); |
1d0cd73f KZ |
212 | if (!upd->fs) |
213 | return -ENOMEM; | |
f84fa6f7 | 214 | |
1d0cd73f KZ |
215 | } |
216 | } | |
6c40a53d | 217 | |
1d0cd73f KZ |
218 | |
219 | DBG(UPDATE, mnt_debug_h(upd, "ready")); | |
220 | upd->ready = TRUE; | |
7714c689 | 221 | return 0; |
6c40a53d KZ |
222 | } |
223 | ||
36bda5cb | 224 | /** |
f84fa6f7 KZ |
225 | * mnt_update_get_fs: |
226 | * @upd: update | |
227 | * | |
228 | * Returns: update filesystem entry or NULL | |
97e23b5e KZ |
229 | */ |
230 | mnt_fs *mnt_update_get_fs(mnt_update *upd) | |
231 | { | |
232 | return upd ? upd->fs : NULL; | |
233 | } | |
1d0cd73f | 234 | |
36bda5cb KZ |
235 | /** |
236 | * mnt_update_get_mountflags: | |
237 | * @upd: update | |
238 | * | |
239 | * Returns: mount flags as was set by mnt_update_set_fs() | |
240 | */ | |
241 | unsigned long mnt_update_get_mountflags(mnt_update *upd) | |
242 | { | |
243 | return upd ? upd->mountflags : 0; | |
244 | } | |
245 | ||
246 | /** | |
247 | * mnt_update_force_rdonly: | |
248 | * @upd: update | |
249 | * @rdonly: is read-only? | |
250 | * | |
251 | * Returns: 0 on success and negative number in case of error. | |
252 | */ | |
253 | int mnt_update_force_rdonly(mnt_update *upd, int rdonly) | |
254 | { | |
255 | int rc = 0; | |
256 | ||
257 | if (!upd || !upd->fs) | |
258 | return -EINVAL; | |
259 | ||
260 | if (rdonly && (upd->mountflags & MS_RDONLY)) | |
261 | return 0; | |
262 | if (!rdonly && !(upd->mountflags & MS_RDONLY)) | |
263 | return 0; | |
264 | ||
265 | if (!upd->userspace_only) { | |
266 | /* /etc/mtab -- we care about VFS options there */ | |
267 | const char *o = mnt_fs_get_vfs_options(upd->fs); | |
268 | char *n = o ? strdup(o) : NULL; | |
269 | ||
270 | if (n) | |
271 | mnt_optstr_remove_option(&n, rdonly ? "rw" : "ro"); | |
272 | if (!mnt_optstr_prepend_option(&n, rdonly ? "ro" : "rw", NULL)) | |
273 | rc = mnt_fs_set_vfs_options(upd->fs, n); | |
274 | ||
275 | free(n); | |
276 | } | |
277 | ||
278 | if (rdonly) | |
279 | upd->mountflags &= ~MS_RDONLY; | |
280 | else | |
281 | upd->mountflags |= MS_RDONLY; | |
282 | ||
283 | return rc; | |
284 | } | |
285 | ||
6c40a53d | 286 | /* |
1d0cd73f KZ |
287 | * Allocates (but does not write) utab entry for mount/remount. This function |
288 | * should be called *before* mount(2) syscall. | |
289 | * | |
290 | * Returns: 0 on success, negative number on error, 1 if utabs update is | |
291 | * unnecessary. | |
6c40a53d | 292 | */ |
1d0cd73f | 293 | static int utab_new_entry(mnt_fs *fs, unsigned long mountflags, mnt_fs **ent) |
6c40a53d | 294 | { |
1d0cd73f | 295 | int rc = 0; |
76a06ca4 | 296 | const char *o = NULL, *a = NULL; |
f84fa6f7 | 297 | char *u = NULL; |
6c40a53d KZ |
298 | |
299 | assert(fs); | |
1d0cd73f KZ |
300 | assert(ent); |
301 | assert(!(mountflags & MS_MOVE)); | |
6c40a53d | 302 | |
1d0cd73f | 303 | if (!fs || !ent) |
3a5b1b1d | 304 | return -EINVAL; |
1d0cd73f | 305 | *ent = NULL; |
6c40a53d | 306 | |
1d0cd73f | 307 | DBG(UPDATE, mnt_debug("prepare utab entry")); |
6c40a53d | 308 | |
76a06ca4 KZ |
309 | o = mnt_fs_get_userspace_options(fs); |
310 | a = mnt_fs_get_attributes(fs); | |
311 | ||
f84fa6f7 KZ |
312 | if (o) { |
313 | /* remove non-mtab options */ | |
314 | rc = mnt_optstr_get_options(o, &u, | |
315 | mnt_get_builtin_optmap(MNT_USERSPACE_MAP), | |
316 | MNT_NOMTAB); | |
317 | if (rc) | |
318 | goto err; | |
319 | } | |
320 | ||
321 | if (!u && !a) { | |
322 | DBG(UPDATE, mnt_debug("utab entry unnecessary (no options)")); | |
323 | return 1; | |
324 | } | |
1d0cd73f KZ |
325 | |
326 | /* allocate the entry */ | |
327 | *ent = mnt_copy_fs(fs); | |
328 | if (!*ent) { | |
329 | rc = -ENOMEM; | |
330 | goto err; | |
6c40a53d KZ |
331 | } |
332 | ||
f84fa6f7 | 333 | rc = mnt_fs_set_userspace_options(*ent, u); |
76a06ca4 KZ |
334 | if (rc) |
335 | goto err; | |
336 | rc = mnt_fs_set_attributes(*ent, a); | |
1d0cd73f KZ |
337 | if (rc) |
338 | goto err; | |
6c40a53d | 339 | |
1d0cd73f | 340 | if (!(mountflags & MS_REMOUNT)) { |
766af80b | 341 | rc = set_fs_root(*ent, fs, mountflags); |
1d0cd73f KZ |
342 | if (rc) |
343 | goto err; | |
344 | } | |
6c40a53d | 345 | |
f84fa6f7 | 346 | free(u); |
1d0cd73f | 347 | DBG(UPDATE, mnt_debug("utab entry OK")); |
6c40a53d | 348 | return 0; |
1d0cd73f | 349 | err: |
1d0cd73f | 350 | mnt_free_fs(*ent); |
f84fa6f7 | 351 | free(u); |
76a06ca4 | 352 | *ent = NULL; |
1d0cd73f | 353 | return rc; |
6c40a53d KZ |
354 | } |
355 | ||
766af80b | 356 | static int set_fs_root(mnt_fs *result, mnt_fs *fs, unsigned long mountflags) |
6c40a53d KZ |
357 | { |
358 | char *root = NULL, *mnt = NULL; | |
76a06ca4 | 359 | const char *fstype; |
6c40a53d | 360 | mnt_tab *tb = NULL; |
1d0cd73f | 361 | int rc = -ENOMEM; |
6c40a53d | 362 | |
1d0cd73f | 363 | assert(fs); |
766af80b | 364 | assert(result); |
1d0cd73f KZ |
365 | |
366 | DBG(UPDATE, mnt_debug("setting FS root")); | |
6c40a53d | 367 | |
6c40a53d KZ |
368 | fstype = mnt_fs_get_fstype(fs); |
369 | ||
370 | /* | |
371 | * bind-mount -- get fs-root and source device for the source filesystem | |
372 | */ | |
1d0cd73f | 373 | if (mountflags & MS_BIND) { |
6c40a53d KZ |
374 | const char *src, *src_root; |
375 | mnt_fs *src_fs; | |
376 | ||
f84fa6f7 KZ |
377 | DBG(UPDATE, mnt_debug("setting FS root: bind")); |
378 | ||
6c40a53d | 379 | src = mnt_fs_get_srcpath(fs); |
dd369652 | 380 | if (src) { |
766af80b | 381 | rc = mnt_fs_set_bindsrc(result, src); |
dd369652 KZ |
382 | if (rc) |
383 | goto err; | |
1d0cd73f | 384 | mnt = mnt_get_mountpoint(src); |
dd369652 | 385 | } |
1d0cd73f KZ |
386 | if (!mnt) { |
387 | rc = -EINVAL; | |
6c40a53d | 388 | goto err; |
1d0cd73f | 389 | } |
6c40a53d KZ |
390 | root = mnt_get_fs_root(src, mnt); |
391 | ||
dd369652 | 392 | tb = __mnt_new_tab_from_file(_PATH_PROC_MOUNTINFO, MNT_FMT_MOUNTINFO); |
f84fa6f7 KZ |
393 | if (!tb) { |
394 | DBG(UPDATE, mnt_debug("failed to parse mountinfo -- using default")); | |
6c40a53d | 395 | goto dflt; |
f84fa6f7 | 396 | } |
6c40a53d | 397 | src_fs = mnt_tab_find_target(tb, mnt, MNT_ITER_BACKWARD); |
f84fa6f7 KZ |
398 | if (!src_fs) { |
399 | DBG(UPDATE, mnt_debug("not found '%s' in mountinfo -- using default", mnt)); | |
6c40a53d | 400 | goto dflt; |
f84fa6f7 | 401 | } |
6c40a53d KZ |
402 | |
403 | /* set device name and fs */ | |
404 | src = mnt_fs_get_srcpath(src_fs); | |
766af80b | 405 | rc = mnt_fs_set_source(result, src); |
dd369652 KZ |
406 | if (rc) |
407 | goto err; | |
6c40a53d | 408 | |
766af80b | 409 | mnt_fs_set_fstype(result, mnt_fs_get_fstype(src_fs)); |
6c40a53d KZ |
410 | |
411 | /* on btrfs the subvolume is used as fs-root in | |
412 | * /proc/self/mountinfo, so we have get the original subvolume | |
413 | * name from src_fs and prepend the subvolume name to the | |
414 | * fs-root path | |
415 | */ | |
416 | src_root = mnt_fs_get_root(src_fs); | |
417 | if (src_root && !startswith(root, src_root)) { | |
418 | size_t sz = strlen(root) + strlen(src_root) + 1; | |
419 | char *tmp = malloc(sz); | |
420 | ||
421 | if (!tmp) | |
422 | goto err; | |
423 | snprintf(tmp, sz, "%s%s", src_root, root); | |
424 | free(root); | |
425 | root = tmp; | |
426 | } | |
427 | } | |
428 | ||
429 | /* | |
430 | * btrfs-subvolume mount -- get subvolume name and use it as a root-fs path | |
431 | */ | |
432 | else if (fstype && !strcmp(fstype, "btrfs")) { | |
433 | char *vol = NULL, *p; | |
434 | size_t sz, volsz = 0; | |
435 | ||
76a06ca4 | 436 | if (mnt_fs_get_option(fs, "subvol", &vol, &volsz)) |
6c40a53d KZ |
437 | goto dflt; |
438 | ||
f84fa6f7 KZ |
439 | DBG(UPDATE, mnt_debug("setting FS root: btrfs subvol")); |
440 | ||
6c40a53d KZ |
441 | sz = volsz; |
442 | if (*vol != '/') | |
443 | sz++; | |
444 | root = malloc(sz + 1); | |
445 | if (!root) | |
446 | goto err; | |
447 | p = root; | |
448 | if (*vol != '/') | |
449 | *p++ = '/'; | |
450 | memcpy(p, vol, volsz); | |
451 | *(root + sz) = '\0'; | |
452 | } | |
6c40a53d KZ |
453 | dflt: |
454 | mnt_free_tab(tb); | |
1d0cd73f | 455 | if (!root) { |
6c40a53d | 456 | root = strdup("/"); |
1d0cd73f KZ |
457 | if (!root) |
458 | goto err; | |
459 | } | |
766af80b | 460 | result->root = root; |
dd369652 KZ |
461 | |
462 | DBG(UPDATE, mnt_debug("FS root result: %s", root)); | |
463 | ||
6c40a53d KZ |
464 | free(mnt); |
465 | return 0; | |
466 | err: | |
467 | free(root); | |
468 | free(mnt); | |
1d0cd73f | 469 | return rc; |
d300992d KZ |
470 | } |
471 | ||
1d0cd73f KZ |
472 | /* mtab and fstab update */ |
473 | static int fprintf_mtab_fs(FILE *f, mnt_fs *fs) | |
6c40a53d | 474 | { |
76a06ca4 | 475 | char *o; |
1d0cd73f KZ |
476 | char *m1, *m2, *m3, *m4; |
477 | int rc; | |
6c40a53d | 478 | |
1d0cd73f KZ |
479 | assert(fs); |
480 | assert(f); | |
1b56aae8 | 481 | |
76a06ca4 KZ |
482 | o = mnt_fs_strdup_options(fs); |
483 | if (!o) | |
484 | return -ENOMEM; | |
485 | ||
1d0cd73f KZ |
486 | m1 = mangle(mnt_fs_get_source(fs)); |
487 | m2 = mangle(mnt_fs_get_target(fs)); | |
488 | m3 = mangle(mnt_fs_get_fstype(fs)); | |
76a06ca4 | 489 | m4 = mangle(o); |
1b56aae8 | 490 | |
1d0cd73f KZ |
491 | if (m1 && m2 && m3 && m4) |
492 | rc = !fprintf(f, "%s %s %s %s %d %d\n", | |
493 | m1, m2, m3, m4, | |
494 | mnt_fs_get_freq(fs), | |
495 | mnt_fs_get_passno(fs)); | |
496 | else | |
497 | rc = -ENOMEM; | |
6c40a53d | 498 | |
76a06ca4 | 499 | free(o); |
1d0cd73f KZ |
500 | free(m1); |
501 | free(m2); | |
502 | free(m3); | |
503 | free(m4); | |
3a5b1b1d | 504 | |
1d0cd73f KZ |
505 | return rc; |
506 | } | |
6c40a53d | 507 | |
1d0cd73f KZ |
508 | static int fprintf_utab_fs(FILE *f, mnt_fs *fs) |
509 | { | |
dd369652 | 510 | char *p; |
4e92d2b0 | 511 | |
1d0cd73f KZ |
512 | assert(fs); |
513 | assert(f); | |
6c40a53d | 514 | |
1d0cd73f KZ |
515 | if (!fs || !f) |
516 | return -EINVAL; | |
dd369652 KZ |
517 | |
518 | p = mangle(mnt_fs_get_source(fs)); | |
519 | if (p) { | |
520 | fprintf(f, "SRC=%s ", p); | |
521 | free(p); | |
522 | } | |
523 | p = mangle(mnt_fs_get_target(fs)); | |
524 | if (p) { | |
525 | fprintf(f, "TARGET=%s ", p); | |
526 | free(p); | |
527 | } | |
528 | p = mangle(mnt_fs_get_root(fs)); | |
529 | if (p) { | |
530 | fprintf(f, "ROOT=%s ", p); | |
531 | free(p); | |
532 | } | |
533 | p = mangle(mnt_fs_get_bindsrc(fs)); | |
534 | if (p) { | |
535 | fprintf(f, "BINDSRC=%s ", p); | |
536 | free(p); | |
537 | } | |
76a06ca4 KZ |
538 | p = mangle(mnt_fs_get_attributes(fs)); |
539 | if (p) { | |
540 | fprintf(f, "ATTRS=%s ", p); | |
541 | free(p); | |
542 | } | |
543 | p = mangle(mnt_fs_get_userspace_options(fs)); | |
dd369652 KZ |
544 | if (p) { |
545 | fprintf(f, "OPTS=%s", p); | |
546 | free(p); | |
547 | } | |
dd369652 KZ |
548 | fputc('\n', f); |
549 | ||
550 | return 0; | |
6c40a53d KZ |
551 | } |
552 | ||
77417bc0 | 553 | static int update_tab(mnt_update *upd, mnt_tab *tb) |
6c40a53d KZ |
554 | { |
555 | FILE *f; | |
1d0cd73f KZ |
556 | int rc, fd; |
557 | char *uq = NULL; | |
6c40a53d | 558 | |
77417bc0 | 559 | if (!tb || !upd->filename) |
1d0cd73f | 560 | return -EINVAL; |
6c40a53d | 561 | |
77417bc0 | 562 | DBG(UPDATE, mnt_debug_h(upd, "%s: updating", upd->filename)); |
3f31a959 | 563 | |
77417bc0 | 564 | fd = mnt_open_uniq_filename(upd->filename, &uq, O_WRONLY); |
1d0cd73f KZ |
565 | if (fd < 0) |
566 | return fd; /* error */ | |
567 | ||
568 | f = fdopen(fd, "w"); | |
6c40a53d | 569 | if (f) { |
1d0cd73f KZ |
570 | struct stat st; |
571 | mnt_iter itr; | |
572 | mnt_fs *fs; | |
573 | int fd; | |
574 | ||
575 | mnt_reset_iter(&itr, MNT_ITER_FORWARD); | |
576 | while(mnt_tab_next_fs(tb, &itr, &fs) == 0) { | |
577 | if (upd->userspace_only) | |
578 | fprintf_utab_fs(f, fs); | |
579 | else | |
580 | fprintf_mtab_fs(f, fs); | |
581 | } | |
582 | fd = fileno(f); | |
583 | rc = fchmod(fd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) ? -errno : 0; | |
584 | ||
77417bc0 | 585 | if (!rc && stat(upd->filename, &st) == 0) |
1d0cd73f KZ |
586 | /* Copy uid/gid from the present file before renaming. */ |
587 | rc = fchown(fd, st.st_uid, st.st_gid) ? -errno : 0; | |
588 | ||
6c40a53d | 589 | fclose(f); |
77417bc0 | 590 | rc = rename(uq, upd->filename) ? -errno : 0; |
1d0cd73f KZ |
591 | } else { |
592 | rc = -errno; | |
593 | close(fd); | |
6c40a53d | 594 | } |
1d0cd73f KZ |
595 | |
596 | unlink(uq); /* be paranoid */ | |
597 | free(uq); | |
6c40a53d KZ |
598 | return rc; |
599 | } | |
600 | ||
1d0cd73f | 601 | static int utab_lock(const char *filename) |
6c40a53d | 602 | { |
1d0cd73f KZ |
603 | char *lfile; |
604 | int fd; | |
6c40a53d | 605 | |
1d0cd73f | 606 | assert(filename); |
6c40a53d | 607 | |
1d0cd73f KZ |
608 | if (asprintf(&lfile, "%s.lock", filename) == -1) |
609 | return -1; | |
6c40a53d | 610 | |
1d0cd73f | 611 | DBG(UPDATE, mnt_debug("%s: locking", lfile)); |
6c40a53d | 612 | |
b0bb8fb6 KZ |
613 | fd = open(lfile, O_RDONLY|O_CREAT|O_CLOEXEC, S_IWUSR| |
614 | S_IRUSR|S_IRGRP|S_IROTH); | |
1d0cd73f KZ |
615 | free(lfile); |
616 | ||
617 | if (fd < 0) | |
618 | return -errno; | |
619 | if (flock(fd, LOCK_EX)) { | |
620 | int errsv = errno; | |
621 | close(fd); | |
622 | return -errsv; | |
6c40a53d | 623 | } |
1d0cd73f KZ |
624 | return fd; |
625 | } | |
6c40a53d | 626 | |
1d0cd73f KZ |
627 | static void utab_unlock(int fd) |
628 | { | |
629 | if (fd >= 0) { | |
630 | DBG(UPDATE, mnt_debug("unlocking utab")); | |
631 | close(fd); | |
632 | } | |
6c40a53d KZ |
633 | } |
634 | ||
77417bc0 | 635 | static int update_add_entry(mnt_update *upd, mnt_lock *lc) |
6c40a53d | 636 | { |
1d0cd73f KZ |
637 | FILE *f; |
638 | int rc = 0, u_lc = -1; | |
6c40a53d | 639 | |
30493710 | 640 | assert(upd); |
1d0cd73f | 641 | assert(upd->fs); |
6c40a53d | 642 | |
77417bc0 | 643 | DBG(UPDATE, mnt_debug_h(upd, "%s: add entry", upd->filename)); |
6c40a53d | 644 | |
1d0cd73f KZ |
645 | if (lc) |
646 | mnt_lock_file(lc); | |
647 | else if (upd->userspace_only) | |
77417bc0 | 648 | u_lc = utab_lock(upd->filename); |
6c40a53d | 649 | |
77417bc0 | 650 | f = fopen(upd->filename, "a+"); |
1d0cd73f KZ |
651 | if (f) { |
652 | rc = upd->userspace_only ? fprintf_utab_fs(f, upd->fs) : | |
653 | fprintf_mtab_fs(f, upd->fs); | |
77417bc0 | 654 | DBG(UPDATE, mnt_debug_h(upd, "%s: add [rc=%d]", upd->filename, rc)); |
1d0cd73f KZ |
655 | fclose(f); |
656 | } else { | |
77417bc0 | 657 | DBG(UPDATE, mnt_debug_h(upd, "%s: failed: %m", upd->filename)); |
1d0cd73f | 658 | rc = -errno; |
6c40a53d | 659 | } |
1d0cd73f KZ |
660 | if (lc) |
661 | mnt_unlock_file(lc); | |
662 | else if (u_lc != -1) | |
663 | utab_unlock(u_lc); | |
664 | return rc; | |
665 | } | |
6c40a53d | 666 | |
77417bc0 | 667 | static int update_remove_entry(mnt_update *upd, mnt_lock *lc) |
1d0cd73f KZ |
668 | { |
669 | mnt_tab *tb; | |
dd369652 | 670 | int rc = 0, u_lc = -1; |
6c40a53d | 671 | |
1d0cd73f KZ |
672 | assert(upd); |
673 | assert(upd->target); | |
674 | ||
77417bc0 | 675 | DBG(UPDATE, mnt_debug_h(upd, "%s: remove entry", upd->filename)); |
1d0cd73f KZ |
676 | |
677 | if (lc) | |
678 | mnt_lock_file(lc); | |
679 | else if (upd->userspace_only) | |
77417bc0 | 680 | u_lc = utab_lock(upd->filename); |
1d0cd73f | 681 | |
77417bc0 | 682 | tb = __mnt_new_tab_from_file(upd->filename, |
dd369652 | 683 | upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); |
1d0cd73f KZ |
684 | if (tb) { |
685 | mnt_fs *rem = mnt_tab_find_target(tb, upd->target, MNT_ITER_BACKWARD); | |
686 | if (rem) { | |
687 | mnt_tab_remove_fs(tb, rem); | |
77417bc0 | 688 | rc = update_tab(upd, tb); |
1d0cd73f KZ |
689 | mnt_free_fs(rem); |
690 | } | |
691 | mnt_free_tab(tb); | |
692 | } | |
693 | if (lc) | |
694 | mnt_unlock_file(lc); | |
695 | else if (u_lc != -1) | |
696 | utab_unlock(u_lc); | |
6c40a53d KZ |
697 | return rc; |
698 | } | |
699 | ||
77417bc0 | 700 | static int update_modify_target(mnt_update *upd, mnt_lock *lc) |
6c40a53d KZ |
701 | { |
702 | mnt_tab *tb = NULL; | |
dd369652 | 703 | int rc = 0, u_lc = -1; |
1d0cd73f | 704 | |
77417bc0 | 705 | DBG(UPDATE, mnt_debug_h(upd, "%s: modify target", upd->filename)); |
1d0cd73f KZ |
706 | |
707 | if (lc) | |
708 | mnt_lock_file(lc); | |
709 | else if (upd->userspace_only) | |
77417bc0 | 710 | u_lc = utab_lock(upd->filename); |
1d0cd73f | 711 | |
77417bc0 | 712 | tb = __mnt_new_tab_from_file(upd->filename, |
dd369652 | 713 | upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); |
1d0cd73f | 714 | if (tb) { |
2915bdc0 KZ |
715 | mnt_fs *cur = mnt_tab_find_target(tb, |
716 | mnt_fs_get_srcpath(upd->fs), MNT_ITER_BACKWARD); | |
1d0cd73f KZ |
717 | if (cur) { |
718 | rc = mnt_fs_set_target(cur, mnt_fs_get_target(upd->fs)); | |
719 | if (!rc) | |
77417bc0 | 720 | rc = update_tab(upd, tb); |
1d0cd73f KZ |
721 | } |
722 | mnt_free_tab(tb); | |
723 | } | |
724 | if (lc) | |
725 | mnt_unlock_file(lc); | |
726 | else if (u_lc != -1) | |
727 | utab_unlock(u_lc); | |
728 | return rc; | |
729 | } | |
6c40a53d | 730 | |
77417bc0 | 731 | static int update_modify_options(mnt_update *upd, mnt_lock *lc) |
1d0cd73f KZ |
732 | { |
733 | mnt_tab *tb = NULL; | |
dd369652 | 734 | int rc = 0, u_lc = -1; |
76a06ca4 | 735 | mnt_fs *fs; |
6c40a53d | 736 | |
1d0cd73f KZ |
737 | assert(upd); |
738 | assert(upd->fs); | |
6c40a53d | 739 | |
77417bc0 | 740 | DBG(UPDATE, mnt_debug_h(upd, "%s: modify options", upd->filename)); |
1d0cd73f | 741 | |
76a06ca4 KZ |
742 | fs = upd->fs; |
743 | ||
1d0cd73f KZ |
744 | if (lc) |
745 | mnt_lock_file(lc); | |
746 | else if (upd->userspace_only) | |
77417bc0 | 747 | u_lc = utab_lock(upd->filename); |
1d0cd73f | 748 | |
77417bc0 | 749 | tb = __mnt_new_tab_from_file(upd->filename, |
dd369652 | 750 | upd->userspace_only ? MNT_FMT_UTAB : MNT_FMT_MTAB); |
1d0cd73f KZ |
751 | if (tb) { |
752 | mnt_fs *cur = mnt_tab_find_target(tb, | |
76a06ca4 | 753 | mnt_fs_get_target(fs), |
1d0cd73f KZ |
754 | MNT_ITER_BACKWARD); |
755 | if (cur) { | |
76a06ca4 KZ |
756 | if (upd->userspace_only) |
757 | rc = mnt_fs_set_attributes(cur, mnt_fs_get_attributes(fs)); | |
758 | if (!rc && !upd->userspace_only) | |
759 | rc = mnt_fs_set_vfs_options(cur, mnt_fs_get_vfs_options(fs)); | |
760 | if (!rc && !upd->userspace_only) | |
761 | rc = mnt_fs_set_fs_options(cur, mnt_fs_get_fs_options(fs)); | |
762 | if (!rc) | |
763 | rc = mnt_fs_set_userspace_options(cur, | |
764 | mnt_fs_get_userspace_options(fs)); | |
1d0cd73f | 765 | if (!rc) |
77417bc0 | 766 | rc = update_tab(upd, tb); |
1d0cd73f KZ |
767 | } |
768 | mnt_free_tab(tb); | |
6c40a53d | 769 | } |
76a06ca4 | 770 | |
1d0cd73f KZ |
771 | if (lc) |
772 | mnt_unlock_file(lc); | |
773 | else if (u_lc != -1) | |
774 | utab_unlock(u_lc); | |
6c40a53d KZ |
775 | return rc; |
776 | } | |
777 | ||
778 | /** | |
1d0cd73f | 779 | * mnt_update_tab: |
0f32f1e2 | 780 | * @upd: update |
1d0cd73f | 781 | * @lc: lock |
6c40a53d | 782 | * |
77417bc0 | 783 | * High-level API to update /etc/mtab (or private /dev/.mount/utab file). |
6c40a53d | 784 | * |
1d0cd73f | 785 | * Returns: 0 on success, negative number on error. |
6c40a53d | 786 | */ |
77417bc0 | 787 | int mnt_update_tab(mnt_update *upd, mnt_lock *lc) |
6c40a53d | 788 | { |
1d0cd73f KZ |
789 | int rc = -EINVAL; |
790 | ||
30493710 | 791 | assert(upd); |
6c40a53d | 792 | |
77417bc0 | 793 | if (!upd->filename || !upd) |
1d0cd73f KZ |
794 | return -EINVAL; |
795 | if (!upd->ready) | |
d300992d | 796 | return 0; |
6c40a53d | 797 | |
77417bc0 | 798 | DBG(UPDATE, mnt_debug_h(upd, "%s: update tab", upd->filename)); |
f84fa6f7 KZ |
799 | if (upd->fs) { |
800 | DBG(UPDATE, mnt_fs_print_debug(upd->fs, stderr)); | |
801 | } | |
77417bc0 | 802 | |
1d0cd73f | 803 | if (!upd->fs && upd->target) |
77417bc0 | 804 | rc = update_remove_entry(upd, lc); /* umount */ |
1d0cd73f | 805 | else if (upd->mountflags & MS_MOVE) |
77417bc0 | 806 | rc = update_modify_target(upd, lc); /* move */ |
1d0cd73f | 807 | else if (upd->mountflags & MS_REMOUNT) |
77417bc0 | 808 | rc = update_modify_options(upd, lc); /* remount */ |
1d0cd73f | 809 | else if (upd->fs) |
77417bc0 | 810 | rc = update_add_entry(upd, lc); /* mount */ |
1d0cd73f KZ |
811 | |
812 | upd->ready = FALSE; | |
77417bc0 KZ |
813 | DBG(UPDATE, mnt_debug_h(upd, "%s: update tab: done [rc=%d]", |
814 | upd->filename, rc)); | |
1d0cd73f | 815 | return rc; |
6c40a53d KZ |
816 | } |
817 | ||
818 | #ifdef TEST_PROGRAM | |
819 | ||
820 | #include <errno.h> | |
821 | ||
822 | mnt_lock *lock; | |
823 | ||
824 | static void lock_fallback(void) | |
825 | { | |
826 | if (lock) | |
827 | mnt_unlock_file(lock); | |
828 | } | |
829 | ||
1d0cd73f | 830 | static int update(const char *target, mnt_fs *fs, unsigned long mountflags) |
6c40a53d | 831 | { |
36bda5cb | 832 | int rc; |
1d0cd73f | 833 | mnt_update *upd; |
36bda5cb | 834 | const char *filename; |
6c40a53d | 835 | |
1d0cd73f | 836 | DBG(UPDATE, mnt_debug("update test")); |
3a5b1b1d | 837 | |
77417bc0 KZ |
838 | upd = mnt_new_update(); |
839 | if (!upd) | |
840 | return -ENOMEM; | |
7714c689 | 841 | |
1d0cd73f | 842 | rc = mnt_update_set_fs(upd, mountflags, target, fs); |
1d0cd73f | 843 | if (rc == 1) { |
7c118af7 | 844 | /* update is unnecessary */ |
1d0cd73f KZ |
845 | rc = 0; |
846 | goto done; | |
847 | } | |
7c118af7 KZ |
848 | if (rc) { |
849 | fprintf(stderr, "failed to set FS\n"); | |
850 | goto done; | |
851 | } | |
30493710 | 852 | |
1d0cd73f | 853 | /* [... here should be mount(2) call ...] */ |
30493710 | 854 | |
36bda5cb KZ |
855 | filename = mnt_update_get_filename(upd); |
856 | if (filename) { | |
857 | lock = mnt_new_lock(filename, 0); | |
858 | if (lock) | |
859 | atexit(lock_fallback); | |
860 | } | |
77417bc0 | 861 | rc = mnt_update_tab(upd, lock); |
1d0cd73f | 862 | done: |
30493710 KZ |
863 | return rc; |
864 | } | |
865 | ||
1d0cd73f | 866 | static int test_add(struct mtest *ts, int argc, char *argv[]) |
30493710 KZ |
867 | { |
868 | mnt_fs *fs = mnt_new_fs(); | |
1d0cd73f | 869 | int rc; |
30493710 | 870 | |
1d0cd73f | 871 | if (argc < 5 || !fs) |
30493710 KZ |
872 | return -1; |
873 | mnt_fs_set_source(fs, argv[1]); | |
874 | mnt_fs_set_target(fs, argv[2]); | |
875 | mnt_fs_set_fstype(fs, argv[3]); | |
76a06ca4 | 876 | mnt_fs_set_options(fs, argv[4]); |
30493710 | 877 | |
1d0cd73f | 878 | rc = update(NULL, fs, 0); |
7714c689 | 879 | mnt_free_fs(fs); |
6c40a53d KZ |
880 | return rc; |
881 | } | |
882 | ||
1d0cd73f | 883 | static int test_remove(struct mtest *ts, int argc, char *argv[]) |
6c40a53d | 884 | { |
1d0cd73f | 885 | if (argc < 2) |
6c40a53d | 886 | return -1; |
1d0cd73f | 887 | return update(argv[1], NULL, 0); |
6c40a53d KZ |
888 | } |
889 | ||
1d0cd73f | 890 | static int test_move(struct mtest *ts, int argc, char *argv[]) |
6c40a53d | 891 | { |
7714c689 | 892 | mnt_fs *fs = mnt_new_fs(); |
1d0cd73f | 893 | int rc; |
6c40a53d | 894 | |
1d0cd73f | 895 | if (argc < 3) |
6c40a53d | 896 | return -1; |
25bca458 | 897 | mnt_fs_set_source(fs, argv[1]); |
7714c689 | 898 | mnt_fs_set_target(fs, argv[2]); |
25bca458 KZ |
899 | |
900 | rc = update(NULL, fs, MS_MOVE); | |
7714c689 | 901 | |
7714c689 | 902 | mnt_free_fs(fs); |
6c40a53d KZ |
903 | return rc; |
904 | } | |
905 | ||
1d0cd73f | 906 | static int test_remount(struct mtest *ts, int argc, char *argv[]) |
6c40a53d | 907 | { |
7714c689 | 908 | mnt_fs *fs = mnt_new_fs(); |
1d0cd73f | 909 | int rc; |
6c40a53d | 910 | |
1d0cd73f | 911 | if (argc < 3) |
6c40a53d | 912 | return -1; |
7714c689 | 913 | mnt_fs_set_target(fs, argv[1]); |
76a06ca4 | 914 | mnt_fs_set_options(fs, argv[2]); |
7714c689 | 915 | |
1d0cd73f | 916 | rc = update(NULL, fs, MS_REMOUNT); |
7714c689 | 917 | mnt_free_fs(fs); |
6c40a53d KZ |
918 | return rc; |
919 | } | |
920 | ||
921 | int main(int argc, char *argv[]) | |
922 | { | |
923 | struct mtest tss[] = { | |
924 | { "--add", test_add, "<src> <target> <type> <options> add line to mtab" }, | |
925 | { "--remove", test_remove, "<target> MS_REMOUNT mtab change" }, | |
926 | { "--move", test_move, "<old_target> <target> MS_MOVE mtab change" }, | |
927 | { "--remount",test_remount, "<target> <options> MS_REMOUNT mtab change" }, | |
928 | { NULL } | |
929 | }; | |
930 | ||
931 | return mnt_run_test(tss, argc, argv); | |
932 | } | |
933 | ||
934 | #endif /* TEST_PROGRAM */ |