LCOV - code coverage report
Current view: top level - plugins/fs - mount.c (source / functions) Coverage Total Hit
Test: libblockdev Coverage Report Lines: 60.3 % 350 211
Test Date: 2026-01-23 09:12:16 Functions: 81.8 % 11 9
Legend: Lines: hit not hit

            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              : }
        

Generated by: LCOV version 2.0-1