Line data Source code
1 : /*
2 : * Copyright (C) 2017 Red Hat, Inc.
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Lesser General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2.1 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Lesser General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Lesser General Public
15 : * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Author: Vratislav Podzimek <vpodzime@redhat.com>
18 : */
19 :
20 : #define _GNU_SOURCE
21 : #include <unistd.h>
22 :
23 : #include <glib.h>
24 :
25 : #include <libmount/libmount.h>
26 : #include <sys/types.h>
27 : #include <sys/wait.h>
28 : #include <stdlib.h>
29 : #include <errno.h>
30 : #include <string.h>
31 :
32 : #include <blockdev/utils.h>
33 :
34 : #include "fs.h"
35 : #include "mount.h"
36 :
37 : #define MOUNT_ERR_BUF_SIZE 1024
38 :
39 : #ifdef __clang__
40 : #define ZERO_INIT {}
41 : #else
42 : #define ZERO_INIT {0}
43 : #endif
44 :
45 : typedef struct MountArgs {
46 : const gchar *mountpoint;
47 : const gchar *device;
48 : const gchar *fstype;
49 : const gchar *options;
50 : const gchar *spec;
51 : gboolean lazy;
52 : gboolean force;
53 : } MountArgs;
54 :
55 : typedef gboolean (*MountFunc) (MountArgs *args, GError **error);
56 :
57 : static gboolean do_mount (MountArgs *args, GError **error);
58 :
59 : #ifndef LIBMOUNT_NEW_ERR_API
60 : static gboolean get_unmount_error_old (struct libmnt_context *cxt, int rc, const gchar *spec, GError **error) {
61 : int syscall_errno = 0;
62 : int helper_status = 0;
63 :
64 : if (mnt_context_syscall_called (cxt) == 1) {
65 : syscall_errno = mnt_context_get_syscall_errno (cxt);
66 : switch (syscall_errno) {
67 : case 0:
68 : return TRUE;
69 : case EBUSY:
70 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
71 : "Target busy.");
72 : break;
73 : case EINVAL:
74 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
75 : "Not a mount point.");
76 : break;
77 : case EPERM:
78 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
79 : "Operation not permitted.");
80 : break;
81 : default:
82 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
83 : "Unmount syscall failed: %d.", syscall_errno);
84 : break;
85 : }
86 : } else if (mnt_context_helper_executed (cxt) == 1) {
87 : helper_status = mnt_context_get_helper_status (cxt);
88 : if (helper_status != 0) {
89 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
90 : "Unmount helper program failed: %d.", helper_status);
91 : return FALSE;
92 : } else
93 : return TRUE;
94 : } else {
95 : if (rc == 0)
96 : return TRUE;
97 : else if (rc == -EPERM) {
98 : if (mnt_context_tab_applied (cxt))
99 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
100 : "Operation not permitted.");
101 : else
102 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
103 : "Not mounted.");
104 : } else {
105 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
106 : "Failed to unmount %s.", spec);
107 : }
108 : }
109 : return FALSE;
110 : }
111 : #else
112 27 : static gboolean get_unmount_error_new (struct libmnt_context *cxt, int rc, const gchar *spec, GError **error) {
113 27 : int ret = 0;
114 27 : int syscall_errno = 0;
115 27 : char buf[MOUNT_ERR_BUF_SIZE] = {0};
116 27 : gboolean permission = FALSE;
117 :
118 27 : ret = mnt_context_get_excode (cxt, rc, buf, MOUNT_ERR_BUF_SIZE - 1);
119 27 : if (ret != 0) {
120 : /* check whether the call failed because of lack of permission */
121 0 : if (mnt_context_syscall_called (cxt)) {
122 0 : syscall_errno = mnt_context_get_syscall_errno (cxt);
123 0 : permission = syscall_errno == EPERM;
124 : } else
125 0 : permission = ret == MNT_EX_USAGE && mnt_context_tab_applied (cxt);
126 :
127 0 : if (permission)
128 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
129 : "Operation not permitted.");
130 : else {
131 0 : if (*buf == '\0')
132 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
133 : "Unknown error when unmounting %s", spec);
134 : else
135 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
136 : "%s", buf);
137 : }
138 :
139 0 : return FALSE;
140 : }
141 :
142 27 : return TRUE;
143 : }
144 : #endif
145 :
146 27 : static gboolean do_unmount (MountArgs *args, GError **error) {
147 27 : struct libmnt_context *cxt = NULL;
148 27 : int ret = 0;
149 27 : gboolean success = FALSE;
150 :
151 27 : cxt = mnt_new_context ();
152 :
153 27 : if (mnt_context_set_target (cxt, args->spec) != 0) {
154 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
155 : "Failed to set '%s' as target for umount", args->spec);
156 0 : mnt_free_context (cxt);
157 0 : return FALSE;
158 : }
159 :
160 27 : if (args->lazy) {
161 0 : if (mnt_context_enable_lazy (cxt, TRUE) != 0) {
162 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
163 : "Failed to set lazy unmount for '%s'", args->spec);
164 0 : mnt_free_context (cxt);
165 0 : return FALSE;
166 : }
167 : }
168 :
169 27 : if (args->force) {
170 0 : if (mnt_context_enable_force (cxt, TRUE) != 0) {
171 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
172 : "Failed to set force unmount for '%s'", args->spec);
173 0 : mnt_free_context (cxt);
174 0 : return FALSE;
175 : }
176 : }
177 :
178 27 : bd_utils_log_format (BD_UTILS_LOG_INFO, "Unmounting %s", args->spec);
179 :
180 27 : ret = mnt_context_umount (cxt);
181 : #ifdef LIBMOUNT_NEW_ERR_API
182 27 : success = get_unmount_error_new (cxt, ret, args->spec, error);
183 : #else
184 : success = get_unmount_error_old (cxt, ret, args->spec, error);
185 : #endif
186 :
187 27 : mnt_free_context (cxt);
188 27 : return success;
189 : }
190 :
191 : #ifndef LIBMOUNT_NEW_ERR_API
192 : static gboolean get_mount_error_old (struct libmnt_context *cxt, int rc, MountArgs *args, GError **error) {
193 : int syscall_errno = 0;
194 : int helper_status = 0;
195 : unsigned long mflags = 0;
196 :
197 : if (mnt_context_get_mflags (cxt, &mflags) != 0) {
198 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
199 : "Failed to get options from string '%s'.", args->options);
200 : return FALSE;
201 : }
202 :
203 : if (mnt_context_syscall_called (cxt) == 1) {
204 : syscall_errno = mnt_context_get_syscall_errno (cxt);
205 : switch (syscall_errno) {
206 : case 0:
207 : return TRUE;
208 : case EBUSY:
209 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
210 : "Source is already mounted or target is busy.");
211 : break;
212 : case EINVAL:
213 : if (mflags & MS_REMOUNT)
214 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
215 : "Remount attempted, but %s is not mounted at %s.", args->device, args->mountpoint);
216 : else if (mflags & MS_MOVE)
217 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
218 : "Move attempted, but %s is not a mount point.", args->device);
219 : else
220 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
221 : "Wrong fs type, %s has an invalid superblock or missing helper program.", args->device);
222 : break;
223 : case EPERM:
224 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
225 : "Operation not permitted.");
226 : break;
227 : case ENOTBLK:
228 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
229 : "%s is not a block device.", args->device);
230 : break;
231 : case ENOTDIR:
232 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
233 : "%s is not a directory.", args->mountpoint);
234 : break;
235 : case ENODEV:
236 : if (args->fstype == NULL || strlen (args->fstype) == 0)
237 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
238 : "Filesystem type not specified");
239 : else
240 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNKNOWN_FS,
241 : "Filesystem type %s not configured in kernel.", args->fstype);
242 : break;
243 : case EROFS:
244 : case EACCES:
245 : if (mflags & MS_RDONLY) {
246 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
247 : "Cannot mount %s read-only.", args->device);
248 : break;
249 : } else if (args->options && (mnt_optstr_get_option (args->options, "rw", NULL, NULL) == 0)) {
250 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
251 : "%s is write-protected but `rw' option given.", args->device);
252 : break;
253 : } else if (mflags & MS_BIND) {
254 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
255 : "Mount %s on %s failed.", args->device, args->mountpoint);
256 : break;
257 : }
258 : /* new versions of libmount do this automatically */
259 : else {
260 : MountArgs ro_args = ZERO_INIT;
261 : gboolean success = FALSE;
262 :
263 : ro_args.device = args->device;
264 : ro_args.mountpoint = args->mountpoint;
265 : ro_args.fstype = args->fstype;
266 : if (!args->options)
267 : ro_args.options = g_strdup ("ro");
268 : else
269 : ro_args.options = g_strdup_printf ("%s,ro", args->options);
270 :
271 : success = do_mount (&ro_args, error);
272 :
273 : g_free ((gchar*) ro_args.options);
274 :
275 : return success;
276 : }
277 : default:
278 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
279 : "Mount syscall failed: %d.", syscall_errno);
280 : break;
281 : }
282 : } else if (mnt_context_helper_executed (cxt) == 1) {
283 : helper_status = mnt_context_get_helper_status (cxt);
284 : if (helper_status != 0) {
285 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
286 : "Mount helper program failed: %d.", helper_status);
287 : return FALSE;
288 : } else
289 : return TRUE;
290 : } else {
291 : switch (rc) {
292 : case 0:
293 : return TRUE;
294 : case -EPERM:
295 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
296 : "Only root can mount %s.", args->device);
297 : break;
298 : case -EBUSY:
299 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
300 : "%s is already mounted.", args->device);
301 : break;
302 : /* source or target explicitly defined and not found in fstab */
303 : case -MNT_ERR_NOFSTAB:
304 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
305 : "Can't find %s in %s.", args->device ? args->device : args->mountpoint, mnt_get_fstab_path ());
306 : break;
307 : case -MNT_ERR_MOUNTOPT:
308 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
309 : "Failed to parse mount options");
310 : break;
311 : case -MNT_ERR_NOSOURCE:
312 : if (args->device)
313 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
314 : "Can't find %s.", args->device);
315 : else
316 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
317 : "Mount source not defined.");
318 : break;
319 : case -MNT_ERR_LOOPDEV:
320 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
321 : "Failed to setup loop device");
322 : break;
323 : case -MNT_ERR_NOFSTYPE:
324 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
325 : "Filesystem type not specified");
326 : break;
327 : default:
328 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
329 : "Failed to mount %s.", args->device ? args->device : args->mountpoint);
330 : break;
331 : }
332 : }
333 :
334 : return FALSE;
335 : }
336 :
337 : #else
338 32 : static gboolean get_mount_error_new (struct libmnt_context *cxt, int rc, MountArgs *args, GError **error) {
339 32 : int ret = 0;
340 32 : int syscall_errno = 0;
341 32 : char buf[MOUNT_ERR_BUF_SIZE] = {0};
342 32 : gboolean permission = FALSE;
343 :
344 32 : ret = mnt_context_get_excode (cxt, rc, buf, MOUNT_ERR_BUF_SIZE - 1);
345 32 : if (ret != 0) {
346 : /* check whether the call failed because of lack of permission */
347 2 : if (mnt_context_syscall_called (cxt) == 1) {
348 2 : syscall_errno = mnt_context_get_syscall_errno (cxt);
349 2 : permission = syscall_errno == EPERM;
350 : } else
351 0 : permission = ret == MNT_EX_USAGE && mnt_context_tab_applied (cxt);
352 :
353 2 : if (permission)
354 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_AUTH,
355 : "Operation not permitted.");
356 2 : else if (syscall_errno == ENODEV)
357 1 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_UNKNOWN_FS,
358 : "Filesystem type %s not configured in kernel.", args->fstype);
359 : else {
360 1 : if (*buf == '\0')
361 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
362 0 : "Unknown error when mounting %s", args->device ? args->device : args->mountpoint);
363 : else
364 1 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
365 : "%s", buf);
366 : }
367 :
368 2 : return FALSE;
369 : }
370 :
371 30 : return TRUE;
372 : }
373 : #endif
374 :
375 32 : static gboolean do_mount (MountArgs *args, GError **error) {
376 32 : struct libmnt_context *cxt = NULL;
377 32 : int ret = 0;
378 32 : gboolean success = FALSE;
379 :
380 32 : cxt = mnt_new_context ();
381 :
382 32 : if (!args->mountpoint && !args->device) {
383 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
384 : "You must specify at least one of: mount point, device.");
385 0 : mnt_free_context (cxt);
386 0 : return FALSE;
387 : }
388 :
389 32 : if (args->mountpoint) {
390 30 : if (mnt_context_set_target (cxt, args->mountpoint) != 0) {
391 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
392 : "Failed to set '%s' as target for mount", args->mountpoint);
393 0 : mnt_free_context (cxt);
394 0 : return FALSE;
395 : }
396 : }
397 :
398 32 : if (args->device) {
399 31 : if (mnt_context_set_source (cxt, args->device) != 0) {
400 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
401 : "Failed to set '%s' as source for mount", args->device);
402 0 : mnt_free_context (cxt);
403 0 : return FALSE;
404 : }
405 : }
406 :
407 32 : if (args->fstype) {
408 23 : if (mnt_context_set_fstype (cxt, args->fstype) != 0) {
409 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
410 : "Failed to set '%s' as fstype for mount", args->fstype);
411 0 : mnt_free_context (cxt);
412 0 : return FALSE;
413 : }
414 : }
415 :
416 32 : if (args->options) {
417 20 : if (mnt_context_set_options (cxt, args->options) != 0) {
418 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
419 : "Failed to set '%s' as options for mount", args->options);
420 0 : mnt_free_context (cxt);
421 0 : return FALSE;
422 : }
423 : }
424 :
425 : #ifdef LIBMOUNT_NEW_ERR_API
426 : /* we don't want libmount to try RDONLY mounts if we were explicitly given the "rw" option */
427 32 : if (args->options && (mnt_optstr_get_option (args->options, "rw", NULL, NULL) == 0))
428 1 : mnt_context_enable_rwonly_mount (cxt, TRUE);
429 : #endif
430 :
431 128 : bd_utils_log_format (BD_UTILS_LOG_INFO, "Mounting %s (fstype %s) to %s with options: %s",
432 32 : args->device ? args->device : "unspecified device",
433 32 : args->fstype ? args->fstype : "auto",
434 32 : args->mountpoint ? args->mountpoint : "unspecified mountpoint",
435 32 : args->options ? args->options : "none");
436 :
437 32 : ret = mnt_context_mount (cxt);
438 :
439 : /* we need to always do some libmount magic to check if the mount really
440 : succeeded -- `mnt_context_mount` can return zero when helper program
441 : (mount.type) fails
442 : */
443 : #ifdef LIBMOUNT_NEW_ERR_API
444 32 : success = get_mount_error_new (cxt, ret, args, error);
445 : #else
446 : success = get_mount_error_old (cxt, ret, args, error);
447 : #endif
448 :
449 32 : mnt_free_context (cxt);
450 32 : return success;
451 : }
452 :
453 0 : static gboolean set_ruid (uid_t uid, GError **error) {
454 0 : if (setresuid (uid, -1, -1) != 0) {
455 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
456 : "Error setting ruid: %m");
457 0 : return FALSE;
458 : }
459 :
460 0 : return TRUE;
461 : }
462 :
463 0 : static gboolean set_rgid (gid_t gid, GError **error) {
464 0 : if (setresgid (gid, -1, -1) != 0) {
465 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
466 : "Error setting rgid: %m");
467 0 : return FALSE;
468 : }
469 :
470 0 : return TRUE;
471 : }
472 :
473 :
474 : /**
475 : * run_as_user:
476 : *
477 : * Runs given @func in a child process with real user and group ID specified by
478 : * @run_as_uid and @run_as_gid. The child process is ended after @func is finished.
479 : * This is used to run mount and unmount functions in a similar way how the `mount`
480 : * command, which is a suid binary, works and is used to set the libmount context
481 : * to restricted.
482 : */
483 4 : static gboolean run_as_user (MountFunc func, MountArgs *args, uid_t run_as_uid, gid_t run_as_gid, GError ** error) {
484 4 : uid_t current_uid = -1;
485 4 : gid_t current_gid = -1;
486 4 : pid_t pid = -1;
487 4 : pid_t wpid = -1;
488 : int pipefd[2];
489 4 : int status = 0;
490 4 : GIOChannel *channel = NULL;
491 4 : GError *local_error = NULL;
492 4 : gchar *error_msg = NULL;
493 4 : gsize msglen = 0;
494 :
495 4 : current_uid = getuid ();
496 4 : current_gid = getgid ();
497 :
498 4 : if (geteuid () != 0) {
499 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
500 : "Not running as root, cannot change the UID/GID.");
501 0 : return FALSE;
502 : }
503 :
504 4 : if (pipe(pipefd) == -1) {
505 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
506 : "Error creating pipe.");
507 0 : return FALSE;
508 : }
509 :
510 4 : pid = fork ();
511 :
512 4 : if (pid == -1) {
513 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
514 : "Error forking.");
515 0 : return FALSE;
516 4 : } else if (pid == 0) {
517 0 : close (pipefd[0]);
518 :
519 0 : if (run_as_gid != current_gid) {
520 0 : if (!set_rgid (run_as_gid, error)) {
521 0 : if (write(pipefd[1], (*error)->message, strlen((*error)->message)) < 0)
522 0 : _exit (BD_FS_ERROR_PIPE);
523 : else
524 0 : _exit ((*error)->code);
525 : }
526 : }
527 :
528 0 : if (run_as_uid != current_uid) {
529 0 : if (!set_ruid (run_as_uid, error)) {
530 0 : if (write(pipefd[1], (*error)->message, strlen((*error)->message)) < 0)
531 0 : _exit (BD_FS_ERROR_PIPE);
532 : else
533 0 : _exit ((*error)->code);
534 : }
535 : }
536 :
537 0 : if (!func (args, error)) {
538 0 : if (write(pipefd[1], (*error)->message, strlen((*error)->message)) < 0)
539 0 : _exit (BD_FS_ERROR_PIPE);
540 : else
541 0 : _exit ((*error)->code);
542 : }
543 :
544 0 : _exit (EXIT_SUCCESS);
545 :
546 : } else {
547 4 : close (pipefd[1]);
548 :
549 : do {
550 4 : wpid = waitpid (pid, &status, WUNTRACED | WCONTINUED);
551 4 : if (wpid == -1) {
552 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
553 : "Error while waiting for process.");
554 0 : close (pipefd[0]);
555 0 : return FALSE;
556 : }
557 :
558 4 : if (WIFEXITED (status)) {
559 4 : if (WEXITSTATUS (status) != EXIT_SUCCESS) {
560 2 : if (WEXITSTATUS (status) == BD_FS_ERROR_PIPE) {
561 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
562 : "Error while reading error.");
563 0 : close (pipefd[0]);
564 0 : return FALSE;
565 : }
566 :
567 2 : channel = g_io_channel_unix_new (pipefd[0]);
568 2 : if (g_io_channel_read_to_end (channel, &error_msg, &msglen, &local_error) != G_IO_STATUS_NORMAL) {
569 0 : if (local_error) {
570 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
571 : "Error while reading error: %s (%d)",
572 0 : local_error->message, local_error->code);
573 0 : g_clear_error (&local_error);
574 : } else
575 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
576 : "Unknown error while reading error.");
577 0 : g_io_channel_unref (channel);
578 0 : close (pipefd[0]);
579 0 : g_free (error_msg);
580 0 : return FALSE;
581 : }
582 :
583 2 : if (g_io_channel_shutdown (channel, TRUE, &local_error) == G_IO_STATUS_ERROR) {
584 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
585 : "Error shutting down GIO channel: %s (%d)",
586 0 : local_error->message, local_error->code);
587 0 : g_clear_error (&local_error);
588 0 : g_io_channel_unref (channel);
589 0 : close (pipefd[0]);
590 0 : g_free (error_msg);
591 0 : return FALSE;
592 : }
593 :
594 2 : if (WEXITSTATUS (status) > BD_FS_ERROR_AUTH)
595 0 : g_set_error_literal (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
596 : error_msg);
597 : else
598 2 : g_set_error_literal (error, BD_FS_ERROR, WEXITSTATUS (status),
599 : error_msg);
600 :
601 2 : g_io_channel_unref (channel);
602 2 : close (pipefd[0]);
603 2 : g_free (error_msg);
604 2 : return FALSE;
605 : } else {
606 2 : close (pipefd[0]);
607 2 : return TRUE;
608 : }
609 0 : } else if (WIFSIGNALED (status)) {
610 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
611 : "Killed by signal %d.", WTERMSIG(status));
612 0 : close (pipefd[0]);
613 0 : return FALSE;
614 : }
615 :
616 0 : } while (!WIFEXITED (status) && !WIFSIGNALED (status));
617 0 : close (pipefd[0]);
618 : }
619 :
620 0 : return FALSE;
621 : }
622 :
623 : /**
624 : * bd_fs_unmount:
625 : * @spec: mount point or device to unmount
626 : * @lazy: enable/disable lazy unmount
627 : * @force: enable/disable force unmount
628 : * @extra: (nullable) (array zero-terminated=1): extra options for the unmount;
629 : * currently only 'run_as_uid'
630 : * and 'run_as_gid' are supported;
631 : * value must be a valid non zero
632 : * uid (gid), if you specify one of
633 : * these, the function will run in
634 : * a child process with real user
635 : * @error: (out) (optional): place to store error (if any)
636 : *
637 : * Returns: whether @spec was successfully unmounted or not
638 : *
639 : * Tech category: %BD_FS_TECH_GENERIC (no mode, ignored)
640 : */
641 29 : gboolean bd_fs_unmount (const gchar *spec, gboolean lazy, gboolean force, const BDExtraArg **extra, GError **error) {
642 29 : uid_t run_as_uid = -1;
643 29 : gid_t run_as_gid = -1;
644 29 : uid_t current_uid = -1;
645 29 : gid_t current_gid = -1;
646 29 : const BDExtraArg **extra_p = NULL;
647 29 : gchar *endptr = NULL;
648 29 : MountArgs args = ZERO_INIT;
649 29 : GError *l_error = NULL;
650 29 : gboolean ret = FALSE;
651 :
652 29 : args.spec = spec;
653 29 : args.lazy = lazy;
654 29 : args.force = force;
655 :
656 29 : current_uid = getuid ();
657 29 : run_as_uid = current_uid;
658 :
659 29 : current_gid = getgid ();
660 29 : run_as_gid = current_gid;
661 :
662 29 : if (extra) {
663 6 : for (extra_p=extra; *extra_p; extra_p++) {
664 4 : if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "run_as_uid") == 0)) {
665 2 : run_as_uid = g_ascii_strtoull ((*extra_p)->val, &endptr, 0);
666 :
667 : /* g_ascii_strtoull returns 0 in case of error */
668 2 : if (run_as_uid == 0 && endptr == (*extra_p)->val) {
669 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
670 0 : "Invalid specification of UID: '%s'", (*extra_p)->val);
671 0 : return FALSE;
672 : }
673 2 : } else if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "run_as_gid") == 0)) {
674 2 : run_as_gid = g_ascii_strtoull ((*extra_p)->val, &endptr, 0);
675 :
676 : /* g_ascii_strtoull returns 0 in case of error */
677 2 : if (run_as_gid == 0 && endptr == (*extra_p)->val) {
678 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
679 0 : "Invalid specification of GID: '%s'", (*extra_p)->val);
680 0 : return FALSE;
681 : }
682 : } else {
683 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
684 0 : "Unsupported argument for unmount: '%s'", (*extra_p)->opt);
685 0 : return FALSE;
686 : }
687 : }
688 : }
689 :
690 29 : if (run_as_uid != current_uid || run_as_gid != current_gid) {
691 2 : ret = run_as_user ((MountFunc) do_unmount, &args, run_as_uid, run_as_gid, &l_error);
692 2 : if (!ret)
693 1 : g_propagate_error (error, l_error);
694 2 : return ret;
695 : } else
696 27 : return do_unmount (&args, error);
697 :
698 : return TRUE;
699 : }
700 :
701 : /**
702 : * bd_fs_mount:
703 : * @device: (nullable): device to mount, if not specified @mountpoint entry
704 : * from fstab will be used
705 : * @mountpoint: (nullable): mountpoint for @device, if not specified @device
706 : * entry from fstab will be used
707 : * @fstype: (nullable): filesystem type
708 : * @options: (nullable): comma delimited options for mount
709 : * @extra: (nullable) (array zero-terminated=1): extra options for the mount;
710 : * currently only 'run_as_uid'
711 : * and 'run_as_gid' are supported;
712 : * value must be a valid non zero
713 : * uid (gid), if you specify one of
714 : * these, the function will run in
715 : * a child process with real user
716 : * and/or group ID set to these values.
717 : * @error: (out) (optional): place to store error (if any)
718 : *
719 : * Returns: whether @device (or @mountpoint) was successfully mounted or not
720 : *
721 : * Tech category: %BD_FS_TECH_MOUNT (no mode, ignored)
722 : */
723 35 : gboolean bd_fs_mount (const gchar *device, const gchar *mountpoint, const gchar *fstype, const gchar *options, const BDExtraArg **extra, GError **error) {
724 35 : uid_t run_as_uid = -1;
725 35 : gid_t run_as_gid = -1;
726 35 : uid_t current_uid = -1;
727 35 : gid_t current_gid = -1;
728 35 : const BDExtraArg **extra_p = NULL;
729 35 : gchar *endptr = NULL;
730 35 : MountArgs args = ZERO_INIT;
731 35 : GError *l_error = NULL;
732 35 : gboolean ret = FALSE;
733 :
734 35 : args.device = device;
735 35 : args.mountpoint = mountpoint;
736 35 : args.fstype = fstype;
737 35 : args.options = options;
738 :
739 35 : current_uid = getuid ();
740 35 : run_as_uid = current_uid;
741 :
742 35 : current_gid = getgid ();
743 35 : run_as_gid = current_gid;
744 :
745 35 : if (extra) {
746 10 : for (extra_p=extra; *extra_p; extra_p++) {
747 7 : if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "run_as_uid") == 0)) {
748 4 : run_as_uid = g_ascii_strtoull ((*extra_p)->val, &endptr, 0);
749 :
750 : /* g_ascii_strtoull returns 0 in case of error */
751 4 : if (run_as_uid == 0 && endptr == (*extra_p)->val) {
752 1 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
753 1 : "Invalid specification of UID: '%s'", (*extra_p)->val);
754 1 : return FALSE;
755 : }
756 3 : } else if ((*extra_p)->opt && (g_strcmp0 ((*extra_p)->opt, "run_as_gid") == 0)) {
757 3 : run_as_gid = g_ascii_strtoull ((*extra_p)->val, &endptr, 0);
758 :
759 : /* g_ascii_strtoull returns 0 in case of error */
760 3 : if (run_as_gid == 0 && endptr == (*extra_p)->val) {
761 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
762 0 : "Invalid specification of GID: '%s'", (*extra_p)->val);
763 0 : return FALSE;
764 : }
765 : } else {
766 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
767 0 : "Unsupported argument for unmount: '%s'", (*extra_p)->opt);
768 0 : return FALSE;
769 : }
770 : }
771 : }
772 :
773 34 : if (run_as_uid != current_uid || run_as_gid != current_gid) {
774 2 : ret = run_as_user ((MountFunc) do_mount, &args, run_as_uid, run_as_gid, &l_error);
775 2 : if (!ret)
776 1 : g_propagate_error (error, l_error);
777 2 : return ret;
778 : } else
779 32 : return do_mount (&args, error);
780 :
781 : return TRUE;
782 : }
783 :
784 : /**
785 : * bd_fs_get_mountpoint:
786 : * @device: device to find mountpoint for
787 : * @error: (out) (optional): place to store error (if any)
788 : *
789 : * Get mountpoint for @device. If @device is mounted multiple times only
790 : * one mountpoint will be returned.
791 : *
792 : * Returns: (transfer full): mountpoint for @device, %NULL in case device is
793 : * not mounted or in case of an error (@error is set
794 : * in this case)
795 : *
796 : * Tech category: %BD_FS_TECH_MOUNT (no mode, ignored)
797 : */
798 72 : gchar* bd_fs_get_mountpoint (const gchar *device, GError **error) {
799 72 : struct libmnt_table *table = NULL;
800 72 : struct libmnt_fs *fs = NULL;
801 72 : struct libmnt_cache *cache = NULL;
802 72 : gint ret = 0;
803 72 : gchar *mountpoint = NULL;
804 72 : const gchar *target = NULL;
805 :
806 72 : table = mnt_new_table ();
807 72 : cache = mnt_new_cache ();
808 :
809 72 : ret = mnt_table_set_cache (table, cache);
810 72 : if (ret != 0) {
811 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
812 : "Failed to set cache for mount info table.");
813 0 : mnt_free_table (table);
814 0 : return NULL;
815 : }
816 :
817 72 : ret = mnt_table_parse_mtab (table, NULL);
818 72 : if (ret != 0) {
819 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
820 : "Failed to parse mount info.");
821 0 : mnt_free_table (table);
822 0 : mnt_free_cache (cache);
823 0 : return NULL;
824 : }
825 :
826 72 : fs = mnt_table_find_source (table, device, MNT_ITER_FORWARD);
827 72 : if (!fs) {
828 38 : mnt_free_table (table);
829 38 : mnt_free_cache (cache);
830 38 : return NULL;
831 : }
832 :
833 34 : target = mnt_fs_get_target (fs);
834 34 : if (!target) {
835 0 : mnt_free_fs (fs);
836 0 : mnt_free_table (table);
837 0 : mnt_free_cache (cache);
838 0 : return NULL;
839 : }
840 :
841 34 : mountpoint = g_strdup (target);
842 34 : mnt_free_fs (fs);
843 34 : mnt_free_table (table);
844 34 : mnt_free_cache (cache);
845 34 : return mountpoint;
846 : }
847 :
848 : /**
849 : * bd_fs_is_mountpoint:
850 : * @path: path (folder) to check
851 : * @error: (out) (optional): place to store error (if any)
852 : *
853 : * Returns: whether @path is a mountpoint or not
854 : *
855 : * Tech category: %BD_FS_TECH_MOUNT (no mode, ignored)
856 : */
857 9 : gboolean bd_fs_is_mountpoint (const gchar *path, GError **error) {
858 9 : struct libmnt_table *table = NULL;
859 9 : struct libmnt_fs *fs = NULL;
860 9 : struct libmnt_cache *cache = NULL;
861 9 : const gchar *target = NULL;
862 9 : gint ret = 0;
863 :
864 9 : table = mnt_new_table ();
865 9 : cache = mnt_new_cache ();
866 :
867 9 : ret = mnt_table_set_cache (table, cache);
868 9 : if (ret != 0) {
869 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
870 : "Failed to set cache for mount info table.");
871 0 : mnt_free_table (table);
872 0 : return FALSE;
873 : }
874 :
875 9 : ret = mnt_table_parse_mtab (table, NULL);
876 9 : if (ret != 0) {
877 0 : g_set_error (error, BD_FS_ERROR, BD_FS_ERROR_FAIL,
878 : "Failed to parse mount info.");
879 0 : mnt_free_table (table);
880 0 : mnt_free_cache (cache);
881 0 : return FALSE;
882 : }
883 :
884 9 : fs = mnt_table_find_target (table, path, MNT_ITER_BACKWARD);
885 9 : if (!fs) {
886 4 : mnt_free_table (table);
887 4 : mnt_free_cache (cache);
888 4 : return FALSE;
889 : }
890 :
891 5 : target = mnt_fs_get_target (fs);
892 5 : if (!target) {
893 0 : mnt_free_fs (fs);
894 0 : mnt_free_table (table);
895 0 : mnt_free_cache (cache);
896 0 : return FALSE;
897 : }
898 :
899 5 : mnt_free_fs (fs);
900 5 : mnt_free_table (table);
901 5 : mnt_free_cache (cache);
902 5 : return TRUE;
903 : }
|