1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20 
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23 
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <pwd.h>
27 #include <grp.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <mntent.h>
32 #include <sys/sysmacros.h>
33 #ifdef HAVE_ACL
34 #include <sys/acl.h>
35 #endif
36 #include <errno.h>
37 #include <blockdev/fs.h>
38 #include <blockdev/utils.h>
39 
40 #include <libmount/libmount.h>
41 
42 #include <glib/gstdio.h>
43 
44 #include "udiskslogging.h"
45 #include "udiskslinuxfilesystem.h"
46 #include "udiskslinuxfilesystemhelpers.h"
47 #include "udiskslinuxblockobject.h"
48 #include "udiskslinuxblock.h"
49 #include "udiskslinuxfsinfo.h"
50 #include "udisksdaemon.h"
51 #include "udisksstate.h"
52 #include "udisksdaemonutil.h"
53 #include "udisksmountmonitor.h"
54 #include "udisksmount.h"
55 #include "udiskslinuxdevice.h"
56 #include "udiskssimplejob.h"
57 #include "udiskslinuxdriveata.h"
58 #include "udiskslinuxmountoptions.h"
59 
60 /**
61  * SECTION:udiskslinuxfilesystem
62  * @title: UDisksLinuxFilesystem
63  * @short_description: Linux implementation of #UDisksFilesystem
64  *
65  * This type provides an implementation of the #UDisksFilesystem
66  * interface on Linux.
67  */
68 
69 typedef struct _UDisksLinuxFilesystemClass   UDisksLinuxFilesystemClass;
70 
71 /**
72  * UDisksLinuxFilesystem:
73  *
74  * The #UDisksLinuxFilesystem structure contains only private data and should
75  * only be accessed using the provided API.
76  */
77 struct _UDisksLinuxFilesystem
78 {
79   UDisksFilesystemSkeleton parent_instance;
80   GMutex lock;
81 };
82 
83 struct _UDisksLinuxFilesystemClass
84 {
85   UDisksFilesystemSkeletonClass parent_class;
86 };
87 
88 static void filesystem_iface_init (UDisksFilesystemIface *iface);
89 
90 G_DEFINE_TYPE_WITH_CODE (UDisksLinuxFilesystem, udisks_linux_filesystem, UDISKS_TYPE_FILESYSTEM_SKELETON,
91                          G_IMPLEMENT_INTERFACE (UDISKS_TYPE_FILESYSTEM, filesystem_iface_init));
92 
93 #ifdef HAVE_FHS_MEDIA
94 #define MOUNT_BASE "/media"
95 #define MOUNT_BASE_PERSISTENT TRUE
96 #else
97 #define MOUNT_BASE "/run/media"
98 #define MOUNT_BASE_PERSISTENT FALSE
99 #endif
100 
101 /* ---------------------------------------------------------------------------------------------------- */
102 
103 static void
udisks_linux_filesystem_finalize(GObject * object)104 udisks_linux_filesystem_finalize (GObject *object)
105 {
106   UDisksLinuxFilesystem *filesystem = UDISKS_LINUX_FILESYSTEM (object);
107 
108   g_mutex_clear (&(filesystem->lock));
109 
110   if (G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize != NULL)
111     G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize (object);
112 }
113 
114 static void
udisks_linux_filesystem_init(UDisksLinuxFilesystem * filesystem)115 udisks_linux_filesystem_init (UDisksLinuxFilesystem *filesystem)
116 {
117   g_mutex_init (&filesystem->lock);
118   g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (filesystem),
119                                        G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
120 }
121 
122 static void
udisks_linux_filesystem_class_init(UDisksLinuxFilesystemClass * klass)123 udisks_linux_filesystem_class_init (UDisksLinuxFilesystemClass *klass)
124 {
125   GObjectClass *gobject_class;
126 
127   gobject_class = G_OBJECT_CLASS (klass);
128   gobject_class->finalize     = udisks_linux_filesystem_finalize;
129 }
130 
131 /**
132  * udisks_linux_filesystem_new:
133  *
134  * Creates a new #UDisksLinuxFilesystem instance.
135  *
136  * Returns: A new #UDisksLinuxFilesystem. Free with g_object_unref().
137  */
138 UDisksFilesystem *
udisks_linux_filesystem_new(void)139 udisks_linux_filesystem_new (void)
140 {
141   return UDISKS_FILESYSTEM (g_object_new (UDISKS_TYPE_LINUX_FILESYSTEM,
142                                           NULL));
143 }
144 
145 /* ---------------------------------------------------------------------------------------------------- */
146 
147 static guint64
get_filesystem_size(UDisksLinuxBlockObject * object)148 get_filesystem_size (UDisksLinuxBlockObject *object)
149 {
150   guint64 size = 0;
151   UDisksLinuxDevice *device;
152   gchar *dev;
153   const gchar *type;
154   GError *error = NULL;
155 
156   device = udisks_linux_block_object_get_device (object);
157   dev = udisks_linux_block_object_get_device_file (object);
158   type = g_udev_device_get_property (device->udev_device, "ID_FS_TYPE");
159 
160   if (g_strcmp0 (type, "ext2") == 0)
161     {
162       BDFSExt2Info *info = bd_fs_ext2_get_info (dev, &error);
163       if (info)
164         {
165           size = info->block_size * info->block_count;
166           bd_fs_ext2_info_free (info);
167         }
168     }
169   else if (g_strcmp0 (type, "ext3") == 0)
170     {
171       BDFSExt3Info *info = bd_fs_ext3_get_info (dev, &error);
172       if (info)
173         {
174           size = info->block_size * info->block_count;
175           bd_fs_ext3_info_free (info);
176         }
177     }
178   else if (g_strcmp0 (type, "ext4") == 0)
179     {
180       BDFSExt4Info *info = bd_fs_ext4_get_info (dev, &error);
181       if (info)
182         {
183           size = info->block_size * info->block_count;
184           bd_fs_ext4_info_free (info);
185         }
186     }
187   else if (g_strcmp0 (type, "xfs") == 0)
188     {
189       BDFSXfsInfo *info = bd_fs_xfs_get_info (dev, &error);
190       if (info)
191         {
192           size = info->block_size * info->block_count;
193           bd_fs_xfs_info_free (info);
194         }
195     }
196 
197   g_free (dev);
198   g_object_unref (device);
199   g_clear_error (&error);
200 
201   return size;
202 }
203 
204 static UDisksDriveAta *
get_drive_ata(UDisksLinuxBlockObject * object)205 get_drive_ata (UDisksLinuxBlockObject *object)
206 {
207   UDisksObject *drive_object = NULL;
208   UDisksDriveAta *ata = NULL;
209   UDisksBlock *block;
210 
211   block = udisks_object_peek_block (UDISKS_OBJECT (object));
212   if (block == NULL)
213     return NULL;
214 
215   drive_object = udisks_daemon_find_object (udisks_linux_block_object_get_daemon (object), udisks_block_get_drive (block));
216   if (drive_object == NULL)
217     return NULL;
218 
219   ata = udisks_object_get_drive_ata (drive_object);
220 
221   g_object_unref (drive_object);
222 
223   return ata;
224 }
225 
226 /**
227  * udisks_linux_filesystem_update:
228  * @filesystem: A #UDisksLinuxFilesystem.
229  * @object: The enclosing #UDisksLinuxBlockObject instance.
230  *
231  * Updates the interface.
232  */
233 void
udisks_linux_filesystem_update(UDisksLinuxFilesystem * filesystem,UDisksLinuxBlockObject * object)234 udisks_linux_filesystem_update (UDisksLinuxFilesystem  *filesystem,
235                                 UDisksLinuxBlockObject *object)
236 {
237   UDisksMountMonitor *mount_monitor;
238   UDisksLinuxDevice *device;
239   UDisksDriveAta *ata = NULL;
240   GPtrArray *p;
241   GList *mounts;
242   GList *l;
243   gboolean skip_fs_size = FALSE;
244   guchar pm_state;
245 
246   mount_monitor = udisks_daemon_get_mount_monitor (udisks_linux_block_object_get_daemon (object));
247   device = udisks_linux_block_object_get_device (object);
248 
249   p = g_ptr_array_new ();
250   mounts = udisks_mount_monitor_get_mounts_for_dev (mount_monitor, g_udev_device_get_device_number (device->udev_device));
251   /* we are guaranteed that the list is sorted so if there are
252    * multiple mounts we'll always get the same order
253    */
254   for (l = mounts; l != NULL; l = l->next)
255     {
256       UDisksMount *mount = UDISKS_MOUNT (l->data);
257       if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM)
258         g_ptr_array_add (p, (gpointer) udisks_mount_get_mount_path (mount));
259     }
260   g_ptr_array_add (p, NULL);
261   udisks_filesystem_set_mount_points (UDISKS_FILESYSTEM (filesystem),
262                                       (const gchar *const *) p->pdata);
263   g_ptr_array_free (p, TRUE);
264   g_list_free_full (mounts, g_object_unref);
265 
266   /* if the drive is ATA and is sleeping, skip filesystem size check to prevent
267    * drive waking up - nothing has changed anyway since it's been sleeping...
268    */
269   ata = get_drive_ata (object);
270   if (ata != NULL)
271     {
272       if (udisks_linux_drive_ata_get_pm_state (UDISKS_LINUX_DRIVE_ATA (ata), NULL, &pm_state))
273         skip_fs_size = ! UDISKS_LINUX_DRIVE_ATA_IS_AWAKE (pm_state);
274     }
275   g_clear_object (&ata);
276 
277   if (! skip_fs_size)
278     udisks_filesystem_set_size (UDISKS_FILESYSTEM (filesystem), get_filesystem_size (object));
279 
280   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (filesystem));
281 
282   g_object_unref (device);
283 }
284 
285 /* ---------------------------------------------------------------------------------------------------- */
286 
287 static const gchar *well_known_filesystems[] =
288 {
289   "btrfs",
290   "ext2",
291   "ext3",
292   "ext4",
293   "udf",
294   "iso9660",
295   "xfs",
296   "jfs",
297   "nilfs",
298   "reiserfs",
299   "reiser4",
300   "msdos",
301   "umsdos",
302   "vfat",
303   "exfat",
304   "ntfs",
305   NULL,
306 };
307 
308 static gboolean
is_in_filesystem_file(const gchar * filesystems_file,const gchar * fstype)309 is_in_filesystem_file (const gchar *filesystems_file,
310                        const gchar *fstype)
311 {
312   gchar *filesystems = NULL;
313   GError *error = NULL;
314   gboolean ret = FALSE;
315   gchar **lines = NULL;
316   guint n;
317 
318   if (!g_file_get_contents (filesystems_file,
319                             &filesystems,
320                             NULL, /* gsize *out_length */
321                             &error))
322     {
323       udisks_warning ("Error reading %s: %s (%s %d)",
324                       filesystems_file,
325                       error->message,
326                       g_quark_to_string (error->domain),
327                       error->code);
328       g_clear_error (&error);
329       goto out;
330     }
331 
332   lines = g_strsplit (filesystems, "\n", -1);
333   for (n = 0; lines != NULL && lines[n] != NULL && !ret; n++)
334     {
335       gchar **tokens;
336       gint num_tokens;
337       g_strdelimit (lines[n], " \t", ' ');
338       g_strstrip (lines[n]);
339       tokens = g_strsplit (lines[n], " ", -1);
340       num_tokens = g_strv_length (tokens);
341       if (num_tokens == 1 && g_strcmp0 (tokens[0], fstype) == 0)
342         {
343           ret = TRUE;
344         }
345       g_strfreev (tokens);
346     }
347 
348  out:
349   g_strfreev (lines);
350   g_free (filesystems);
351   return ret;
352 }
353 
354 static gboolean
is_well_known_filesystem(const gchar * fstype)355 is_well_known_filesystem (const gchar *fstype)
356 {
357   gboolean ret = FALSE;
358   guint n;
359 
360   for (n = 0; well_known_filesystems[n] != NULL; n++)
361     {
362       if (g_strcmp0 (well_known_filesystems[n], fstype) == 0)
363         {
364           ret = TRUE;
365           goto out;
366         }
367     }
368  out:
369   return ret;
370 }
371 
372 /* this is not a very efficient implementation but it's very rarely
373  * called so no real point in optimizing it...
374  */
375 static gboolean
is_allowed_filesystem(const gchar * fstype)376 is_allowed_filesystem (const gchar *fstype)
377 {
378   return is_well_known_filesystem (fstype) ||
379     is_in_filesystem_file ("/proc/filesystems", fstype) ||
380     is_in_filesystem_file ("/etc/filesystems", fstype);
381 }
382 
383 /* ---------------------------------------------------------------------------------------------------- */
384 
385 /*
386  * calculate_fs_type: <internal>
387  * @block: A #UDisksBlock.
388  * @given_options: The a{sv} #GVariant.
389  * @error: Return location for error or %NULL.
390  *
391  * Calculates the file system type to use.
392  *
393  * Returns: A valid UTF-8 string with the filesystem type (may be "auto") or %NULL if @error is set. Free with g_free().
394  */
395 static gchar *
calculate_fs_type(UDisksBlock * block,GVariant * given_options,GError ** error)396 calculate_fs_type (UDisksBlock  *block,
397                    GVariant     *given_options,
398                    GError      **error)
399 {
400   gchar *fs_type_to_use = NULL;
401   const gchar *probed_fs_type = NULL;
402   const gchar *requested_fs_type;
403 
404   probed_fs_type = NULL;
405   if (block != NULL)
406     probed_fs_type = udisks_block_get_id_type (block);
407 
408   if (g_variant_lookup (given_options,
409                         "fstype",
410                         "&s", &requested_fs_type) &&
411       strlen (requested_fs_type) > 0)
412     {
413       /* If the user requests the filesystem type, error out unless the
414        * filesystem type is
415        *
416        * - well-known [1]; or
417        * - in the /proc/filesystems file; or
418        * - in the /etc/filesystems file
419        *
420        * in that order. We do this because mount(8) on Linux allows
421        * loading any arbitrary kernel module (when invoked as root) by
422        * passing something appropriate to the -t option. So we have to
423        * validate whatever we pass...
424        *
425        * See https://bugs.freedesktop.org/show_bug.cgi?id=32232 for more
426        * details.
427        *
428        * [1] : since /etc/filesystems may be horribly out of date and
429        *       not contain e.g. ext4
430        */
431       if (g_strcmp0 (requested_fs_type, "auto") != 0)
432         {
433           if (!is_allowed_filesystem (requested_fs_type))
434             {
435               g_set_error (error,
436                            UDISKS_ERROR,
437                            UDISKS_ERROR_OPTION_NOT_PERMITTED,
438                            "Requested filesystem type `%s' is neither well-known nor "
439                            "in /proc/filesystems nor in /etc/filesystems",
440                            requested_fs_type);
441               goto out;
442             }
443         }
444 
445       /* TODO: maybe check that it's compatible with probed_fs_type */
446       fs_type_to_use = g_strdup (requested_fs_type);
447     }
448   else
449     {
450       if (probed_fs_type != NULL && strlen (probed_fs_type) > 0)
451         fs_type_to_use = g_strdup (probed_fs_type);
452       else
453         fs_type_to_use = g_strdup ("auto");
454     }
455 
456  out:
457   g_assert (fs_type_to_use == NULL || g_utf8_validate (fs_type_to_use, -1, NULL));
458 
459   return fs_type_to_use;
460 }
461 
462 /* ---------------------------------------------------------------------------------------------------- */
463 
464 static gchar *
ensure_utf8(const gchar * s)465 ensure_utf8 (const gchar *s)
466 {
467   const gchar *end;
468   gchar *ret;
469 
470   if (!g_utf8_validate (s, -1, &end))
471     {
472       gchar *tmp;
473       gint pos;
474       /* TODO: could possibly return a nicer UTF-8 string  */
475       pos = (gint) (end - s);
476       tmp = g_strndup (s, end - s);
477       ret = g_strdup_printf ("%s (Invalid UTF-8 at byte %d)", tmp, pos);
478       g_free (tmp);
479     }
480   else
481     {
482       ret = g_strdup (s);
483     }
484 
485   return ret;
486 }
487 
488 /* ---------------------------------------------------------------------------------------------------- */
489 
490 #ifdef HAVE_ACL
491 static gboolean
add_acl(const gchar * path,uid_t uid,GError ** error)492 add_acl (const gchar  *path,
493          uid_t         uid,
494          GError      **error)
495 {
496   gboolean ret = FALSE;
497   acl_t acl = NULL;
498   acl_entry_t entry;
499   acl_permset_t permset;
500 
501   acl = acl_get_file(path, ACL_TYPE_ACCESS);
502   if (acl == NULL ||
503       acl_create_entry (&acl, &entry) == -1 ||
504       acl_set_tag_type (entry, ACL_USER) == -1 ||
505       acl_set_qualifier (entry, &uid) == -1 ||
506       acl_get_permset (entry, &permset) == -1 ||
507       acl_add_perm (permset, ACL_READ|ACL_EXECUTE) == -1 ||
508       acl_calc_mask (&acl) == -1 ||
509       acl_set_file (path, ACL_TYPE_ACCESS, acl) == -1)
510     {
511       udisks_warning(
512                    "Adding read ACL for uid %d to `%s' failed: %m",
513                    (gint) uid, path);
514       chown(path, uid, -1);
515     }
516 
517   ret = TRUE;
518 
519   if (acl != NULL)
520     acl_free (acl);
521   return ret;
522 }
523 #endif
524 
525 /*
526  * calculate_mount_point: <internal>
527  * @daemon: A #UDisksDaemon.
528  * @block: A #UDisksBlock.
529  * @uid: user id of the calling user
530  * @gid: group id of the calling user
531  * @user_name: user name of the calling user
532  * @fs_type: The file system type to mount with
533  * @persistent: if the mount point is persistent (survives reboot) or not
534  * @error: Return location for error or %NULL.
535  *
536  * Calculates the mount point to use.
537  *
538  * Returns: A UTF-8 string with the mount point to use or %NULL if @error is set. Free with g_free().
539  */
540 static gchar *
calculate_mount_point(UDisksDaemon * daemon,UDisksBlock * block,uid_t uid,gid_t gid,const gchar * user_name,const gchar * fs_type,gboolean * persistent,GError ** error)541 calculate_mount_point (UDisksDaemon  *daemon,
542                        UDisksBlock   *block,
543                        uid_t          uid,
544                        gid_t          gid,
545                        const gchar   *user_name,
546                        const gchar   *fs_type,
547                        gboolean      *persistent,
548                        GError       **error)
549 {
550   UDisksLinuxBlockObject *object = NULL;
551   gboolean fs_shared = FALSE;
552   const gchar *label = NULL;
553   const gchar *uuid = NULL;
554   gchar *escaped_user_name = NULL;
555   gchar *mount_dir = NULL;
556   gchar *mount_point = NULL;
557   gchar *orig_mount_point;
558   GString *str;
559   gchar *s;
560   guint n;
561 
562   label = NULL;
563   uuid = NULL;
564   if (block != NULL)
565     {
566       label = udisks_block_get_id_label (block);
567       uuid = udisks_block_get_id_uuid (block);
568     }
569 
570   object = udisks_daemon_util_dup_object (block, NULL);
571   if (object != NULL)
572     {
573       UDisksLinuxDevice *device = udisks_linux_block_object_get_device (object);
574       if (device != NULL)
575         {
576           if (device->udev_device != NULL)
577             {
578               /* TODO: maybe introduce Block:HintFilesystemShared instead of pulling it directly from the udev device */
579               fs_shared = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_FILESYSTEM_SHARED");
580             }
581           g_object_unref (device);
582         }
583     }
584 
585   /* If we know the user-name and it doesn't have any '/' character in
586    * it, mount in MOUNT_BASE/$USER
587    */
588   if (!fs_shared && (user_name != NULL && strstr (user_name, "/") == NULL))
589     {
590       mount_dir = g_strdup_printf (MOUNT_BASE "/%s", user_name);
591       *persistent = MOUNT_BASE_PERSISTENT;
592       if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS))
593         {
594           /* First ensure that MOUNT_BASE exists */
595           if (g_mkdir (MOUNT_BASE, 0755) != 0 && errno != EEXIST)
596             {
597               g_set_error (error,
598                            UDISKS_ERROR,
599                            UDISKS_ERROR_FAILED,
600                            "Error creating directory " MOUNT_BASE ": %m");
601               goto out;
602             }
603           /* Then create the per-user MOUNT_BASE/$USER */
604 #ifdef HAVE_ACL
605           if (g_mkdir (mount_dir, 0700) != 0 && errno != EEXIST)
606 #else
607           if (g_mkdir (mount_dir, 0750) != 0 && errno != EEXIST)
608 #endif
609             {
610               g_set_error (error,
611                            UDISKS_ERROR,
612                            UDISKS_ERROR_FAILED,
613                            "Error creating directory `%s': %m",
614                            mount_dir);
615               goto out;
616             }
617           /* Finally, add the read+execute ACL for $USER */
618 #ifdef HAVE_ACL
619           if (!add_acl (mount_dir, uid, error))
620             {
621 #else
622           if (chown (mount_dir, -1, gid) == -1)
623             {
624                g_set_error (error, G_IO_ERROR,
625                             g_io_error_from_errno (errno),
626                             "Failed to change gid to %d for %s: %m",
627                             (gint) gid, mount_dir);
628 #endif
629               if (rmdir (mount_dir) != 0)
630                 udisks_warning ("Error calling rmdir() on %s: %m", mount_dir);
631               goto out;
632             }
633         }
634     }
635   /* otherwise fall back to mounting in /media */
636   if (mount_dir == NULL)
637     {
638       mount_dir = g_strdup ("/media");
639       *persistent = TRUE;
640     }
641 
642   /* NOTE: UTF-8 has the nice property that valid UTF-8 strings only contains
643    *       the byte 0x2F if it's for the '/' character (U+002F SOLIDUS).
644    *
645    *       See http://en.wikipedia.org/wiki/UTF-8 for details.
646    */
647   if (label != NULL && strlen (label) > 0)
648     {
649       str = g_string_new (NULL);
650       g_string_append_printf (str, "%s/", mount_dir);
651       s = ensure_utf8 (label);
652       for (n = 0; s[n] != '\0'; n++)
653         {
654           gint c = s[n];
655           if (c == '/')
656             g_string_append_c (str, '_');
657           else
658             g_string_append_c (str, c);
659         }
660       mount_point = g_string_free (str, FALSE);
661       g_free (s);
662     }
663   else if (uuid != NULL && strlen (uuid) > 0)
664     {
665       str = g_string_new (NULL);
666       g_string_append_printf (str, "%s/", mount_dir);
667       s = ensure_utf8 (uuid);
668       for (n = 0; s[n] != '\0'; n++)
669         {
670           gint c = s[n];
671           if (c == '/')
672             g_string_append_c (str, '_');
673           else
674             g_string_append_c (str, c);
675         }
676       mount_point = g_string_free (str, FALSE);
677       g_free (s);
678     }
679   else
680     {
681       mount_point = g_strdup_printf ("%s/disk", mount_dir);
682     }
683 
684   /* ... then uniqify the mount point */
685   orig_mount_point = g_strdup (mount_point);
686   n = 1;
687   while (TRUE)
688     {
689       if (!g_file_test (mount_point, G_FILE_TEST_EXISTS))
690         {
691           break;
692         }
693       else
694         {
695           g_free (mount_point);
696           mount_point = g_strdup_printf ("%s%u", orig_mount_point, n++);
697         }
698     }
699   g_free (orig_mount_point);
700 
701  out:
702   g_free (mount_dir);
703   g_clear_object (&object);
704   g_free (escaped_user_name);
705   return mount_point;
706 }
707 
708 /* ---------------------------------------------------------------------------------------------------- */
709 
710 static gboolean
711 has_option (const gchar *options,
712             const gchar *option)
713 {
714   gboolean ret = FALSE;
715   gchar **tokens;
716   guint n;
717 
718   tokens = g_strsplit (options, ",", -1);
719   for (n = 0; tokens != NULL && tokens[n] != NULL; n++)
720     {
721       if (g_strcmp0 (tokens[n], option) == 0)
722         {
723           ret = TRUE;
724           goto out;
725         }
726     }
727 
728  out:
729   g_strfreev (tokens);
730   return ret;
731 }
732 
733 /* returns TRUE if, and only if, device is referenced in e.g. /etc/fstab
734  */
735 static gboolean
736 is_system_managed (UDisksDaemon *daemon,
737                    UDisksBlock  *block,
738                    gchar       **out_mount_point,
739                    gchar       **out_mount_options)
740 {
741   UDisksMountMonitor *mount_monitor = udisks_daemon_get_mount_monitor (daemon);
742   gboolean ret = FALSE;
743   struct libmnt_table *table;
744   struct libmnt_iter* iter;
745   struct libmnt_fs *fs = NULL;
746 
747   table = mnt_new_table ();
748   if (mnt_table_parse_fstab (table, NULL) < 0)
749     {
750       mnt_free_table (table);
751       return FALSE;
752     }
753 
754   iter = mnt_new_iter (MNT_ITER_FORWARD);
755   while (mnt_table_next_fs (table, iter, &fs) == 0)
756     {
757       if (udisks_linux_block_matches_id (UDISKS_LINUX_BLOCK (block), mnt_fs_get_source (fs)))
758         {
759           /* If this block device is found in fstab, but something else is already
760            * mounted on that mount point, ignore the fstab entry.
761            */
762           UDisksMount *mount = udisks_mount_monitor_get_mount_for_path (mount_monitor, mnt_fs_get_target (fs));
763           if (mount == NULL || udisks_block_get_device_number (block) == udisks_mount_get_dev (mount))
764             {
765               ret = TRUE;
766               if (out_mount_point != NULL)
767                 *out_mount_point = g_strdup (mnt_fs_get_target (fs));
768               if (out_mount_options != NULL)
769                 *out_mount_options = mnt_fs_strdup_options (fs);
770             }
771 
772           g_clear_object (&mount);
773           if (ret)
774             break;
775         }
776     }
777   mnt_free_iter (iter);
778   mnt_free_table (table);
779 
780   return ret;
781 }
782 
783 /* ---------------------------------------------------------------------------------------------------- */
784 
785 /* runs in thread dedicated to handling @invocation */
786 static gboolean
787 handle_mount (UDisksFilesystem      *filesystem,
788               GDBusMethodInvocation *invocation,
789               GVariant              *options)
790 {
791   UDisksObject *object = NULL;
792   UDisksBlock *block;
793   UDisksDaemon *daemon;
794   UDisksState *state = NULL;
795   uid_t caller_uid;
796   gid_t caller_gid;
797   const gchar * const *existing_mount_points;
798   const gchar *probed_fs_usage;
799   gchar *fs_type_to_use = NULL;
800   gchar *mount_options_to_use = NULL;
801   gchar *mount_point_to_use = NULL;
802   gboolean mpoint_persistent = TRUE;
803   gchar *fstab_mount_options = NULL;
804   gchar *caller_user_name = NULL;
805   GError *error = NULL;
806   const gchar *action_id = NULL;
807   const gchar *message = NULL;
808   gboolean system_managed = FALSE;
809   gboolean success = FALSE;
810   gchar *device = NULL;
811   UDisksBaseJob *job = NULL;
812 
813 
814   /* only allow a single call at a time */
815   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
816 
817   object = udisks_daemon_util_dup_object (filesystem, &error);
818   if (object == NULL)
819     {
820       g_dbus_method_invocation_take_error (invocation, error);
821       goto out;
822     }
823 
824   block = udisks_object_peek_block (object);
825   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
826   state = udisks_daemon_get_state (daemon);
827   device = udisks_block_dup_device (block);
828 
829   /* perform state cleanup to avoid duplicate entries for this block device */
830   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
831   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
832 
833   /* check if mount point is managed by e.g. /etc/fstab or similar */
834   if (is_system_managed (daemon, block, &mount_point_to_use, &fstab_mount_options))
835     {
836       system_managed = TRUE;
837     }
838 
839   /* First, fail if the device is already mounted */
840   existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
841   if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
842     {
843       GString *str;
844       guint n;
845       str = g_string_new (NULL);
846       for (n = 0; existing_mount_points[n] != NULL; n++)
847         {
848           if (n > 0)
849             g_string_append (str, ", ");
850           g_string_append_printf (str, "`%s'", existing_mount_points[n]);
851         }
852       g_dbus_method_invocation_return_error (invocation,
853                                              UDISKS_ERROR,
854                                              UDISKS_ERROR_ALREADY_MOUNTED,
855                                              "Device %s is already mounted at %s.\n",
856                                              device,
857                                              str->str);
858       g_string_free (str, TRUE);
859       goto out;
860     }
861 
862   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
863                                                invocation,
864                                                NULL /* GCancellable */,
865                                                &caller_uid,
866                                                &error))
867     {
868       g_dbus_method_invocation_return_gerror (invocation, error);
869       g_clear_error (&error);
870       goto out;
871     }
872 
873   if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, &caller_user_name, &error))
874     {
875       g_dbus_method_invocation_return_gerror (invocation, error);
876       g_clear_error (&error);
877       goto out;
878     }
879 
880   if (system_managed)
881     {
882       gboolean mount_fstab_as_root = FALSE;
883 
884       if (!has_option (fstab_mount_options, "x-udisks-auth") &&
885           !has_option (fstab_mount_options, "user") &&
886           !has_option (fstab_mount_options, "users"))
887         {
888           action_id = "org.freedesktop.udisks2.filesystem-mount";
889           /* Translators: Shown in authentication dialog when the user
890            * requests mounting a filesystem.
891            *
892            * Do not translate $(drive), it's a placeholder and
893            * will be replaced by the name of the drive/device in question
894            */
895           message = N_("Authentication is required to mount $(drive)");
896           if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
897             {
898               if (udisks_block_get_hint_system (block))
899                 {
900                   action_id = "org.freedesktop.udisks2.filesystem-mount-system";
901                 }
902               else if (!udisks_daemon_util_on_user_seat (daemon, object, caller_uid))
903                 {
904                   action_id = "org.freedesktop.udisks2.filesystem-mount-other-seat";
905                 }
906             }
907 
908           if (!udisks_daemon_util_check_authorization_sync (daemon,
909                                                             object,
910                                                             action_id,
911                                                             options,
912                                                             message,
913                                                             invocation))
914             goto out;
915           mount_fstab_as_root = TRUE;
916         }
917 
918       if (!g_file_test (mount_point_to_use, G_FILE_TEST_IS_DIR))
919         {
920           if (g_mkdir_with_parents (mount_point_to_use, 0755) != 0)
921             {
922               g_dbus_method_invocation_return_error (invocation,
923                                                      UDISKS_ERROR,
924                                                      UDISKS_ERROR_FAILED,
925                                                      "Error creating directory `%s' to be used for mounting %s: %m",
926                                                      mount_point_to_use,
927                                                      device);
928               goto out;
929             }
930         }
931 
932     mount_fstab_again:
933       job = udisks_daemon_launch_simple_job (daemon,
934                                              UDISKS_OBJECT (object),
935                                              "filesystem-mount",
936                                              mount_fstab_as_root ? 0 : caller_uid,
937                                              NULL /* cancellable */);
938 
939       /* XXX: using run_as_uid for root doesn't work even if the caller is already root */
940       if (!mount_fstab_as_root && caller_uid != 0)
941         {
942           BDExtraArg uid_arg = {g_strdup ("run_as_uid"), g_strdup_printf("%d", caller_uid)};
943           BDExtraArg gid_arg = {g_strdup ("run_as_gid"), g_strdup_printf("%d", caller_gid)};
944           const BDExtraArg *extra_args[3] = {&uid_arg, &gid_arg, NULL};
945 
946           success = bd_fs_mount (NULL, mount_point_to_use, NULL, NULL, extra_args, &error);
947 
948           g_free (uid_arg.opt);
949           g_free (uid_arg.val);
950           g_free (gid_arg.opt);
951           g_free (gid_arg.val);
952         }
953       else
954         {
955           success = bd_fs_mount (NULL, mount_point_to_use, NULL, NULL, NULL, &error);
956         }
957 
958       if (!success)
959           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
960       else
961           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
962 
963       if (!success)
964         {
965           if (!mount_fstab_as_root && g_error_matches (error, BD_FS_ERROR, BD_FS_ERROR_AUTH))
966             {
967               g_clear_error (&error);
968               if (!udisks_daemon_util_check_authorization_sync (daemon,
969                                                                 object,
970                                                                 "org.freedesktop.udisks2.filesystem-fstab",
971                                                                 options,
972                                                                 /* Translators: Shown in authentication dialog when the
973                                                                  * user requests mounting a filesystem that is in
974                                                                  * /etc/fstab file with the x-udisks-auth option.
975                                                                  *
976                                                                  * Do not translate $(drive), it's a
977                                                                  * placeholder and will be replaced by the name of
978                                                                  * the drive/device in question
979                                                                  *
980                                                                  * Do not translate /etc/fstab
981                                                                  */
982                                                                 N_("Authentication is required to mount $(drive) referenced in the /etc/fstab file"),
983                                                                 invocation))
984                 goto out;
985               mount_fstab_as_root = TRUE;
986               goto mount_fstab_again;
987             }
988 
989           g_dbus_method_invocation_return_error (invocation,
990                                                  UDISKS_ERROR,
991                                                  UDISKS_ERROR_FAILED,
992                                                  "Error mounting system-managed device %s: %s",
993                                                  device,
994                                                  error->message);
995           g_clear_error (&error);
996           goto out;
997         }
998       udisks_notice ("Mounted %s (system) at %s on behalf of uid %u",
999                      device,
1000                      mount_point_to_use,
1001                      caller_uid);
1002 
1003       /* update the mounted-fs file */
1004       udisks_state_add_mounted_fs (state,
1005                                    mount_point_to_use,
1006                                    udisks_block_get_device_number (block),
1007                                    caller_uid,
1008                                    TRUE,   /* fstab_mounted */
1009                                    FALSE); /* persistent */
1010 
1011       udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
1012                                                      UDISKS_DEFAULT_WAIT_TIMEOUT);
1013       udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use);
1014       goto out;
1015     }
1016 
1017   /* Then fail if the device is not mountable - we actually allow mounting
1018    * devices that are not probed since since it could be that we just
1019    * don't have the data in the udev database but the device has a
1020    * filesystem *anyway*...
1021    *
1022    * For example, this applies to PC floppy devices - automatically
1023    * probing for media them creates annoying noise. So they won't
1024    * appear in the udev database.
1025    */
1026   probed_fs_usage = NULL;
1027   if (block != NULL)
1028     probed_fs_usage = udisks_block_get_id_usage (block);
1029   if (probed_fs_usage != NULL && strlen (probed_fs_usage) > 0 &&
1030       g_strcmp0 (probed_fs_usage, "filesystem") != 0)
1031     {
1032       g_dbus_method_invocation_return_error (invocation,
1033                                              UDISKS_ERROR,
1034                                              UDISKS_ERROR_FAILED,
1035                                              "Cannot mount block device %s with probed usage `%s' - expected `filesystem'",
1036                                              device,
1037                                              probed_fs_usage);
1038       goto out;
1039     }
1040 
1041   /* calculate filesystem type (guaranteed to be valid UTF-8) */
1042   fs_type_to_use = calculate_fs_type (block,
1043                                       options,
1044                                       &error);
1045   if (fs_type_to_use == NULL)
1046     {
1047       g_dbus_method_invocation_return_gerror (invocation, error);
1048       g_clear_error (&error);
1049       goto out;
1050     }
1051 
1052   /* calculate mount options (guaranteed to be valid UTF-8) */
1053   mount_options_to_use = udisks_linux_calculate_mount_options (daemon,
1054                                                                block,
1055                                                                caller_uid,
1056                                                                fs_type_to_use,
1057                                                                options,
1058                                                                &error);
1059   if (mount_options_to_use == NULL)
1060     {
1061       g_dbus_method_invocation_return_gerror (invocation, error);
1062       g_clear_error (&error);
1063       goto out;
1064     }
1065 
1066   /* Now, check that the user is actually authorized to mount the
1067    * device. Need to do this before calculating a mount point since we
1068    * may be racing with other threads...
1069    */
1070   action_id = "org.freedesktop.udisks2.filesystem-mount";
1071   /* Translators: Shown in authentication dialog when the user
1072    * requests mounting a filesystem.
1073    *
1074    * Do not translate $(drive), it's a placeholder and
1075    * will be replaced by the name of the drive/device in question
1076    */
1077   message = N_("Authentication is required to mount $(drive)");
1078   if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
1079     {
1080       if (udisks_block_get_hint_system (block))
1081         {
1082           action_id = "org.freedesktop.udisks2.filesystem-mount-system";
1083         }
1084       else if (!udisks_daemon_util_on_user_seat (daemon, object, caller_uid))
1085         {
1086           action_id = "org.freedesktop.udisks2.filesystem-mount-other-seat";
1087         }
1088     }
1089 
1090   if (!udisks_daemon_util_check_authorization_sync (daemon,
1091                                                     object,
1092                                                     action_id,
1093                                                     options,
1094                                                     message,
1095                                                     invocation))
1096     goto out;
1097 
1098   /* calculate mount point (guaranteed to be valid UTF-8) */
1099   mount_point_to_use = calculate_mount_point (daemon,
1100                                               block,
1101                                               caller_uid,
1102                                               caller_gid,
1103                                               caller_user_name,
1104                                               fs_type_to_use,
1105                                               &mpoint_persistent,
1106                                               &error);
1107   if (mount_point_to_use == NULL)
1108     {
1109       g_dbus_method_invocation_return_gerror (invocation, error);
1110       g_clear_error (&error);
1111       goto out;
1112     }
1113 
1114   /* create the mount point */
1115   if (g_mkdir (mount_point_to_use, 0700) != 0)
1116     {
1117       g_dbus_method_invocation_return_error (invocation,
1118                                              UDISKS_ERROR,
1119                                              UDISKS_ERROR_FAILED,
1120                                              "Error creating mount point `%s': %m",
1121                                              mount_point_to_use);
1122       goto out;
1123     }
1124 
1125   job = udisks_daemon_launch_simple_job (daemon,
1126                                          UDISKS_OBJECT (object),
1127                                          "filesystem-mount",
1128                                          0,
1129                                          NULL /* cancellable */);
1130 
1131   if (!bd_fs_mount (device, mount_point_to_use, fs_type_to_use, mount_options_to_use, NULL, &error))
1132     {
1133       /* ugh, something went wrong.. we need to clean up the created mount point */
1134       if (g_rmdir (mount_point_to_use) != 0)
1135         udisks_warning ("Error removing directory %s: %m", mount_point_to_use);
1136 
1137       g_dbus_method_invocation_return_error (invocation,
1138                                              UDISKS_ERROR,
1139                                              UDISKS_ERROR_FAILED,
1140                                              "Error mounting %s at %s: %s",
1141                                              device,
1142                                              mount_point_to_use,
1143                                              error->message);
1144       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
1145       g_clear_error (&error);
1146       goto out;
1147     }
1148   else
1149     udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
1150 
1151   /* update the mounted-fs file */
1152   udisks_state_add_mounted_fs (state,
1153                                mount_point_to_use,
1154                                udisks_block_get_device_number (block),
1155                                caller_uid,
1156                                FALSE,  /* fstab_mounted */
1157                                mpoint_persistent);
1158 
1159   udisks_notice ("Mounted %s at %s on behalf of uid %u",
1160                  device,
1161                  mount_point_to_use,
1162                  caller_uid);
1163 
1164   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
1165                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
1166   udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use);
1167 
1168  out:
1169   if (object != NULL)
1170     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
1171   if (state != NULL)
1172     udisks_state_check (state);
1173   g_free (fs_type_to_use);
1174   g_free (mount_options_to_use);
1175   g_free (mount_point_to_use);
1176   g_free (fstab_mount_options);
1177   g_free (caller_user_name);
1178   g_free (device);
1179   g_clear_object (&object);
1180 
1181   /* only allow a single call at a time */
1182   g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1183 
1184   return TRUE; /* returning TRUE means that we handled the method invocation */
1185 }
1186 
1187 /* ---------------------------------------------------------------------------------------------------- */
1188 
1189 static guint
1190 get_error_code_for_umount (const gchar *error_message)
1191 {
1192   if (strstr (error_message, "busy") != NULL)
1193     return UDISKS_ERROR_DEVICE_BUSY;
1194   else
1195     return UDISKS_ERROR_FAILED;
1196 }
1197 
1198 typedef struct
1199 {
1200   const gchar *object_path;
1201   guint64      old_size;
1202   gchar       *mount_point;
1203 } WaitForFilesystemMountPointsData;
1204 
1205 static UDisksObject *
1206 wait_for_filesystem_mount_points (UDisksDaemon *daemon,
1207                                   gpointer      user_data)
1208 {
1209   WaitForFilesystemMountPointsData *data = user_data;
1210   UDisksObject *object = NULL;
1211   UDisksFilesystem *filesystem = NULL;
1212   const gchar * const *mount_points = NULL;
1213 
1214   object = udisks_daemon_find_object (daemon, data->object_path);
1215 
1216   if (object != NULL)
1217     {
1218       filesystem = udisks_object_peek_filesystem (object);
1219       if (filesystem != NULL)
1220         {
1221           mount_points = udisks_filesystem_get_mount_points (filesystem);
1222         }
1223       /* If we know which mount point should have gone, directly test for it, otherwise test if any has gone */
1224       if (mount_points != NULL && ((data->mount_point != NULL && g_strv_contains (mount_points, data->mount_point)) ||
1225           g_strv_length ((gchar **) mount_points) == data->old_size))
1226         {
1227           g_clear_object (&object);
1228         }
1229     }
1230 
1231   return object;
1232 }
1233 
1234 /* runs in thread dedicated to handling @invocation */
1235 static gboolean
1236 handle_unmount (UDisksFilesystem      *filesystem,
1237                 GDBusMethodInvocation *invocation,
1238                 GVariant              *options)
1239 {
1240   UDisksObject *object;
1241   UDisksBlock *block;
1242   UDisksDaemon *daemon;
1243   UDisksState *state = NULL;
1244   gchar *mount_point = NULL;
1245   gchar *fstab_mount_options = NULL;
1246   GError *error = NULL;
1247   uid_t mounted_by_uid;
1248   uid_t caller_uid;
1249   gid_t caller_gid;
1250   const gchar *const *mount_points;
1251   gboolean opt_force = FALSE;
1252   gboolean system_managed = FALSE;
1253   gboolean fstab_mounted;
1254   gboolean success;
1255   UDisksBaseJob *job = NULL;
1256   UDisksObject *filesystem_object = NULL;
1257   WaitForFilesystemMountPointsData wait_data = {NULL, 0, NULL};
1258 
1259   /* only allow a single call at a time */
1260   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1261 
1262   object = udisks_daemon_util_dup_object (filesystem, &error);
1263   if (object == NULL)
1264     {
1265       g_dbus_method_invocation_take_error (invocation, error);
1266       goto out;
1267     }
1268 
1269   block = udisks_object_peek_block (object);
1270   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
1271   state = udisks_daemon_get_state (daemon);
1272 
1273   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
1274   /* trigger state cleanup so that we match actual mountpoint */
1275   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
1276 
1277   if (options != NULL)
1278     {
1279       g_variant_lookup (options,
1280                         "force",
1281                         "b",
1282                         &opt_force);
1283     }
1284 
1285   mount_points = udisks_filesystem_get_mount_points (filesystem);
1286   if (mount_points == NULL || g_strv_length ((gchar **) mount_points) == 0)
1287     {
1288       g_dbus_method_invocation_return_error (invocation,
1289                                              UDISKS_ERROR,
1290                                              UDISKS_ERROR_NOT_MOUNTED,
1291                                              "Device `%s' is not mounted",
1292                                              udisks_block_get_device (block));
1293       goto out;
1294     }
1295 
1296   wait_data.object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
1297   wait_data.old_size = g_strv_length ((gchar **) mount_points);
1298 
1299   if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL, &caller_uid, &error))
1300     {
1301       g_dbus_method_invocation_return_gerror (invocation, error);
1302       g_clear_error (&error);
1303       goto out;
1304     }
1305   if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, NULL, &error))
1306     {
1307       g_dbus_method_invocation_take_error (invocation, error);
1308       goto out;
1309     }
1310 
1311   /* check if mount point is managed by e.g. /etc/fstab or similar */
1312   if (is_system_managed (daemon, block, &mount_point, &fstab_mount_options))
1313     {
1314       system_managed = TRUE;
1315     }
1316 
1317   /* if system-managed (e.g. referenced in /etc/fstab or similar) and
1318    * with the option x-udisks-auth or user(s), just run umount(8) as the
1319    * calling user
1320    */
1321   if (system_managed && (has_option (fstab_mount_options, "x-udisks-auth") ||
1322                          has_option (fstab_mount_options, "users") ||
1323                          has_option (fstab_mount_options, "user")))
1324     {
1325       gboolean unmount_fstab_as_root;
1326 
1327       unmount_fstab_as_root = FALSE;
1328     unmount_fstab_again:
1329 
1330       job = udisks_daemon_launch_simple_job (daemon,
1331                                              UDISKS_OBJECT (object),
1332                                              "filesystem-unmount",
1333                                              unmount_fstab_as_root ? 0 : caller_uid,
1334                                              NULL);
1335 
1336       if (!unmount_fstab_as_root && caller_uid != 0)
1337         {
1338           BDExtraArg uid_arg = {g_strdup ("run_as_uid"), g_strdup_printf("%d", caller_uid)};
1339           BDExtraArg gid_arg = {g_strdup ("run_as_gid"), g_strdup_printf("%d", caller_gid)};
1340           const BDExtraArg *extra_args[3] = {&uid_arg, &gid_arg, NULL};
1341 
1342           success = bd_fs_unmount (mount_point, opt_force, FALSE, extra_args, &error);
1343 
1344           g_free (uid_arg.opt);
1345           g_free (uid_arg.val);
1346           g_free (gid_arg.opt);
1347           g_free (gid_arg.val);
1348         }
1349       else
1350           success = bd_fs_unmount (mount_point, opt_force, FALSE, NULL, &error);
1351 
1352       if (!success)
1353           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
1354       else
1355           udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
1356 
1357       if (!success)
1358         {
1359           if (!unmount_fstab_as_root && error->code == BD_FS_ERROR_AUTH)
1360             {
1361               g_clear_error (&error);
1362               if (!udisks_daemon_util_check_authorization_sync (daemon,
1363                                                                 object,
1364                                                                 "org.freedesktop.udisks2.filesystem-fstab",
1365                                                                 options,
1366                                                                 /* Translators: Shown in authentication dialog when the
1367                                                                  * user requests unmounting a filesystem that is in
1368                                                                  * /etc/fstab file with the x-udisks-auth option.
1369                                                                  *
1370                                                                  * Do not translate $(drive), it's a
1371                                                                  * placeholder and will be replaced by the name of
1372                                                                  * the drive/device in question
1373                                                                  *
1374                                                                  * Do not translate /etc/fstab
1375                                                                  */
1376                                                                 N_("Authentication is required to unmount $(drive) referenced in the /etc/fstab file"),
1377                                                                 invocation))
1378                 goto out;
1379               unmount_fstab_as_root = TRUE;
1380               goto unmount_fstab_again;
1381             }
1382 
1383           g_dbus_method_invocation_return_error (invocation,
1384                                                  UDISKS_ERROR,
1385                                                  get_error_code_for_umount (error->message),
1386                                                  "Error unmounting system-managed device %s: %s",
1387                                                  udisks_block_get_device (block),
1388                                                  error->message);
1389           g_clear_error (&error);
1390 
1391           goto out;
1392         }
1393       udisks_notice ("Unmounted %s (system) from %s on behalf of uid %u",
1394                      udisks_block_get_device (block),
1395                      mount_point,
1396                      caller_uid);
1397       goto waiting;
1398     }
1399 
1400   g_clear_pointer (&mount_point, g_free);
1401   mount_point = udisks_state_find_mounted_fs (state,
1402                                               udisks_block_get_device_number (block),
1403                                               &mounted_by_uid,
1404                                               &fstab_mounted);
1405   if (mount_point == NULL)
1406     {
1407       /* allow unmounting stuff not mentioned in mounted-fs, but treat it like root mounted it */
1408       mounted_by_uid = 0;
1409     }
1410 
1411   if (caller_uid != 0 && (caller_uid != mounted_by_uid))
1412     {
1413       const gchar *action_id;
1414       const gchar *message;
1415 
1416       action_id = "org.freedesktop.udisks2.filesystem-unmount-others";
1417       /* Translators: Shown in authentication dialog when the user
1418        * requests unmounting a filesystem previously mounted by
1419        * another user.
1420        *
1421        * Do not translate $(drive), it's a placeholder and
1422        * will be replaced by the name of the drive/device in question
1423        */
1424       message = N_("Authentication is required to unmount $(drive) mounted by another user");
1425 
1426       if (!udisks_daemon_util_check_authorization_sync (daemon,
1427                                                         object,
1428                                                         action_id,
1429                                                         options,
1430                                                         message,
1431                                                         invocation))
1432         goto out;
1433     }
1434 
1435   job = udisks_daemon_launch_simple_job (daemon,
1436                                          UDISKS_OBJECT (object),
1437                                          "filesystem-unmount",
1438                                          0,
1439                                          NULL);
1440 
1441   if (!bd_fs_unmount (mount_point ? mount_point : udisks_block_get_device (block),
1442                       opt_force, FALSE, NULL, &error))
1443     {
1444       g_dbus_method_invocation_return_error (invocation,
1445                                              UDISKS_ERROR,
1446                                              get_error_code_for_umount (error->message),
1447                                              "Error unmounting %s: %s",
1448                                              udisks_block_get_device (block),
1449                                              error->message);
1450       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
1451       g_clear_error (&error);
1452       goto out;
1453     }
1454   else
1455     udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
1456 
1457   /* filesystem unmounted, run the state/cleanup routines now to remove the mountpoint (if applicable) */
1458   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
1459 
1460   udisks_notice ("Unmounted %s on behalf of uid %u",
1461                  udisks_block_get_device (block),
1462                  caller_uid);
1463 
1464   waiting:
1465   /* wait for mount-points update before returning from method */
1466   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
1467                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
1468   wait_data.mount_point = g_strdup (mount_point);
1469   filesystem_object = udisks_daemon_wait_for_object_sync (daemon,
1470                                                           wait_for_filesystem_mount_points,
1471                                                           &wait_data,
1472                                                           NULL,
1473                                                           UDISKS_DEFAULT_WAIT_TIMEOUT,
1474                                                           NULL);
1475 
1476   udisks_filesystem_complete_unmount (filesystem, invocation);
1477 
1478  out:
1479   if (object != NULL)
1480     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
1481   if (state != NULL)
1482     udisks_state_check (state);
1483   g_free (wait_data.mount_point);
1484   g_free (mount_point);
1485   g_free (fstab_mount_options);
1486   g_clear_object (&object);
1487   g_clear_object (&filesystem_object);
1488 
1489   g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1490 
1491   return TRUE;
1492 }
1493 
1494 /* ---------------------------------------------------------------------------------------------------- */
1495 
1496 /* runs in thread dedicated to handling method call */
1497 static gboolean
1498 handle_set_label (UDisksFilesystem      *filesystem,
1499                   GDBusMethodInvocation *invocation,
1500                   const gchar           *label,
1501                   GVariant              *options)
1502 {
1503   UDisksBlock *block;
1504   UDisksObject *object;
1505   UDisksDaemon *daemon;
1506   UDisksState *state = NULL;
1507   const gchar *probed_fs_usage;
1508   const gchar *probed_fs_type;
1509   const FSInfo *fs_info;
1510   const gchar *action_id;
1511   const gchar *message;
1512   gchar *real_label = NULL;
1513   uid_t caller_uid;
1514   gchar *command;
1515   gint status = 0;
1516   gchar *out_message = NULL;
1517   gboolean success = FALSE;
1518   gchar *tmp;
1519   GError *error = NULL;
1520 
1521   object = NULL;
1522   daemon = NULL;
1523   command = NULL;
1524 
1525   object = udisks_daemon_util_dup_object (filesystem, &error);
1526   if (object == NULL)
1527     {
1528       g_dbus_method_invocation_take_error (invocation, error);
1529       goto out;
1530     }
1531 
1532   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
1533   state = udisks_daemon_get_state (daemon);
1534   block = udisks_object_peek_block (object);
1535 
1536   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
1537   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
1538 
1539   if (!udisks_daemon_util_get_caller_uid_sync (daemon,
1540                                                invocation,
1541                                                NULL /* GCancellable */,
1542                                                &caller_uid,
1543                                                &error))
1544     {
1545       g_dbus_method_invocation_return_gerror (invocation, error);
1546       g_clear_error (&error);
1547       goto out;
1548     }
1549 
1550   probed_fs_usage = udisks_block_get_id_usage (block);
1551   probed_fs_type = udisks_block_get_id_type (block);
1552 
1553   if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
1554     {
1555       g_dbus_method_invocation_return_error (invocation,
1556                                              UDISKS_ERROR,
1557                                              UDISKS_ERROR_NOT_SUPPORTED,
1558                                              "Cannot change label on device of type %s",
1559                                              probed_fs_usage);
1560       goto out;
1561     }
1562 
1563   fs_info = get_fs_info (probed_fs_type);
1564 
1565   if (fs_info == NULL || fs_info->command_change_label == NULL)
1566     {
1567       g_dbus_method_invocation_return_error (invocation,
1568                                              UDISKS_ERROR,
1569                                              UDISKS_ERROR_NOT_SUPPORTED,
1570                                              "Don't know how to change label on device of type %s:%s",
1571                                              probed_fs_usage,
1572                                              probed_fs_type);
1573       goto out;
1574     }
1575 
1576   /* VFAT does not allow some characters; as dosfslabel does not enforce this,
1577    * check in advance; also, VFAT only knows upper-case characters, dosfslabel
1578    * enforces this */
1579   if (g_strcmp0 (probed_fs_type, "vfat") == 0)
1580     {
1581       const gchar *forbidden = "\"*/:<>?\\|";
1582       guint n;
1583       for (n = 0; forbidden[n] != 0; n++)
1584         {
1585           if (strchr (label, forbidden[n]) != NULL)
1586             {
1587               g_dbus_method_invocation_return_error (invocation,
1588                                                      UDISKS_ERROR,
1589                                                      UDISKS_ERROR_NOT_SUPPORTED,
1590                                                      "character '%c' not supported in VFAT labels",
1591                                                      forbidden[n]);
1592                goto out;
1593             }
1594         }
1595 
1596       /* we need to remember that we make a copy, so assign it to a new
1597        * variable, too */
1598       real_label = g_ascii_strup (label, -1);
1599       label = real_label;
1600     }
1601 
1602   /* Fail if the device is already mounted and the tools/drivers doesn't
1603    * support changing the label in that case
1604    */
1605   if (filesystem != NULL && !fs_info->supports_online_label_rename)
1606     {
1607       const gchar * const *existing_mount_points;
1608       existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
1609       if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
1610         {
1611           g_dbus_method_invocation_return_error (invocation,
1612                                                  UDISKS_ERROR,
1613                                                  UDISKS_ERROR_NOT_SUPPORTED,
1614                                                  "Cannot change label on mounted device of type %s:%s.\n",
1615                                                  probed_fs_usage,
1616                                                  probed_fs_type);
1617           goto out;
1618         }
1619     }
1620 
1621   action_id = "org.freedesktop.udisks2.modify-device";
1622   /* Translators: Shown in authentication dialog when the user
1623    * requests changing the filesystem label.
1624    *
1625    * Do not translate $(drive), it's a placeholder and
1626    * will be replaced by the name of the drive/device in question
1627    */
1628   message = N_("Authentication is required to change the filesystem label on $(drive)");
1629   if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
1630     {
1631       if (udisks_block_get_hint_system (block))
1632         {
1633           action_id = "org.freedesktop.udisks2.modify-device-system";
1634         }
1635       else if (!udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
1636         {
1637           action_id = "org.freedesktop.udisks2.modify-device-other-seat";
1638         }
1639     }
1640 
1641   /* Check that the user is actually authorized to change the
1642    * filesystem label.
1643    */
1644   if (!udisks_daemon_util_check_authorization_sync (daemon,
1645                                                     object,
1646                                                     action_id,
1647                                                     options,
1648                                                     message,
1649                                                     invocation))
1650     goto out;
1651 
1652   if (fs_info->command_clear_label != NULL && strlen (label) == 0)
1653     {
1654       command = udisks_daemon_util_subst_str_and_escape (fs_info->command_clear_label, "$DEVICE", udisks_block_get_device (block));
1655     }
1656   else
1657     {
1658       tmp = udisks_daemon_util_subst_str_and_escape (fs_info->command_change_label, "$DEVICE", udisks_block_get_device (block));
1659       command = udisks_daemon_util_subst_str_and_escape (tmp, "$LABEL", label);
1660       g_free (tmp);
1661     }
1662 
1663   success = udisks_daemon_launch_spawned_job_sync (daemon,
1664                                                    object,
1665                                                    "filesystem-modify", caller_uid,
1666                                                    NULL, /* cancellable */
1667                                                    0,    /* uid_t run_as_uid */
1668                                                    0,    /* uid_t run_as_euid */
1669                                                    &status,
1670                                                    &out_message,
1671                                                    NULL, /* input_string */
1672                                                    "%s", command);
1673 
1674   /* Label property is automatically updated after an udev change
1675    * event for this device, but udev sometimes returns the old label
1676    * so just trigger the uevent again now to be sure the property
1677    * has been updated.
1678    */
1679   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
1680                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
1681 
1682   if (success)
1683     udisks_filesystem_complete_set_label (filesystem, invocation);
1684   else
1685     g_dbus_method_invocation_return_error (invocation,
1686                                            UDISKS_ERROR,
1687                                            UDISKS_ERROR_FAILED,
1688                                            "Error setting label: %s",
1689                                            out_message);
1690 
1691  out:
1692   if (object != NULL)
1693     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
1694   if (state != NULL)
1695     udisks_state_check (state);
1696   /* for some FSes we need to copy and modify label; free our copy */
1697   g_free (real_label);
1698   g_free (command);
1699   g_clear_object (&object);
1700   g_free (out_message);
1701   return TRUE; /* returning TRUE means that we handled the method invocation */
1702 }
1703 
1704 /* ---------------------------------------------------------------------------------------------------- */
1705 
1706 /* runs in thread dedicated to handling method call */
1707 static gboolean
1708 handle_resize (UDisksFilesystem      *filesystem,
1709                GDBusMethodInvocation *invocation,
1710                guint64                size,
1711                GVariant              *options)
1712 {
1713   UDisksBlock *block = NULL;
1714   UDisksObject *object = NULL;
1715   UDisksDaemon *daemon = NULL;
1716   UDisksState *state = NULL;
1717   const gchar *probed_fs_usage = NULL;
1718   const gchar *probed_fs_type = NULL;
1719   BDFsResizeFlags mode = 0;
1720   const gchar *action_id = NULL;
1721   const gchar *message = NULL;
1722   uid_t caller_uid;
1723   GError *error = NULL;
1724   UDisksBaseJob *job = NULL;
1725   gchar *required_utility = NULL;
1726   const gchar * const *existing_mount_points = NULL;
1727 
1728   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1729 
1730   object = udisks_daemon_util_dup_object (filesystem, &error);
1731   if (object == NULL)
1732     {
1733       g_dbus_method_invocation_take_error (invocation, error);
1734       goto out;
1735     }
1736 
1737   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
1738   state = udisks_daemon_get_state (daemon);
1739   block = udisks_object_peek_block (object);
1740 
1741   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
1742   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
1743 
1744   if (! udisks_daemon_util_get_caller_uid_sync (daemon,
1745                                                 invocation,
1746                                                 NULL /* GCancellable */,
1747                                                 &caller_uid,
1748                                                 &error))
1749     {
1750       g_dbus_method_invocation_return_gerror (invocation, error);
1751       goto out;
1752     }
1753 
1754   probed_fs_usage = udisks_block_get_id_usage (block);
1755   if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
1756     {
1757       g_dbus_method_invocation_return_error (invocation,
1758                                              UDISKS_ERROR,
1759                                              UDISKS_ERROR_NOT_SUPPORTED,
1760                                              "Cannot resize %s filesystem on %s",
1761                                              probed_fs_usage,
1762                                              udisks_block_get_device (block));
1763       goto out;
1764     }
1765 
1766   probed_fs_type = udisks_block_get_id_type (block);
1767   if (! bd_fs_can_resize (probed_fs_type, &mode, &required_utility, &error))
1768     {
1769       if (error != NULL)
1770         g_dbus_method_invocation_return_error (invocation,
1771                                                UDISKS_ERROR,
1772                                                UDISKS_ERROR_FAILED,
1773                                                "Cannot resize %s filesystem on %s: %s",
1774                                                probed_fs_type,
1775                                                udisks_block_get_device (block),
1776                                                error->message);
1777       else
1778         g_dbus_method_invocation_return_error (invocation,
1779                                                UDISKS_ERROR,
1780                                                UDISKS_ERROR_FAILED,
1781                                                "Cannot resize %s filesystem on %s: executable %s not found",
1782                                                probed_fs_type,
1783                                                udisks_block_get_device (block),
1784                                                required_utility);
1785       goto out;
1786     }
1787 
1788   /* it can't be known if it's shrinking or growing but check at least the mount state */
1789   existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
1790   if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
1791     {
1792       if (! (mode & BD_FS_ONLINE_SHRINK) && ! (mode & BD_FS_ONLINE_GROW))
1793         g_dbus_method_invocation_return_error (invocation,
1794                                                UDISKS_ERROR,
1795                                                UDISKS_ERROR_NOT_SUPPORTED,
1796                                                "Cannot resize %s filesystem on %s if mounted",
1797                                                probed_fs_usage,
1798                                                udisks_block_get_device (block));
1799     }
1800   else if (! (mode & BD_FS_OFFLINE_SHRINK) && ! (mode & BD_FS_OFFLINE_GROW))
1801     {
1802       g_dbus_method_invocation_return_error (invocation,
1803                                              UDISKS_ERROR,
1804                                              UDISKS_ERROR_NOT_SUPPORTED,
1805                                              "Cannot resize %s filesystem on %s if unmounted",
1806                                              probed_fs_usage,
1807                                              udisks_block_get_device (block));
1808     }
1809 
1810   action_id = "org.freedesktop.udisks2.modify-device";
1811   /* Translators: Shown in authentication dialog when the user
1812    * requests resizing the filesystem.
1813    *
1814    * Do not translate $(drive), it's a placeholder and
1815    * will be replaced by the name of the drive/device in question
1816    */
1817   message = N_("Authentication is required to resize the filesystem on $(drive)");
1818   if (! udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
1819     {
1820       if (udisks_block_get_hint_system (block))
1821         {
1822           action_id = "org.freedesktop.udisks2.modify-device-system";
1823         }
1824       else if (! udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
1825         {
1826           action_id = "org.freedesktop.udisks2.modify-device-other-seat";
1827         }
1828     }
1829 
1830   /* Check that the user is actually authorized to resize the filesystem. */
1831   if (! udisks_daemon_util_check_authorization_sync (daemon,
1832                                                     object,
1833                                                     action_id,
1834                                                     options,
1835                                                     message,
1836                                                     invocation))
1837     goto out;
1838 
1839   job = udisks_daemon_launch_simple_job (daemon,
1840                                          UDISKS_OBJECT (object),
1841                                          "filesystem-resize",
1842                                          caller_uid,
1843                                          NULL);
1844   if (job == NULL)
1845     {
1846       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
1847                                              "Failed to create a job object");
1848       goto out;
1849     }
1850 
1851   udisks_bd_thread_set_progress_for_job (UDISKS_JOB (job));
1852   if (! bd_fs_resize (udisks_block_get_device (block), size, &error))
1853     {
1854       g_dbus_method_invocation_return_error (invocation,
1855                                              UDISKS_ERROR,
1856                                              UDISKS_ERROR_FAILED,
1857                                              "Error resizing filesystem on %s: %s",
1858                                              udisks_block_get_device (block),
1859                                              error->message);
1860       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
1861       goto out;
1862     }
1863 
1864   /* At least resize2fs might need another uevent after it is done.
1865    */
1866   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
1867                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
1868 
1869   udisks_filesystem_set_size (filesystem, get_filesystem_size (UDISKS_LINUX_BLOCK_OBJECT (object)));
1870   g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (filesystem));
1871   udisks_filesystem_complete_resize (filesystem, invocation);
1872   udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
1873 
1874  out:
1875   udisks_bd_thread_disable_progress ();
1876   if (object != NULL)
1877     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
1878   if (state != NULL)
1879     udisks_state_check (state);
1880   g_clear_object (&object);
1881   g_free (required_utility);
1882   g_clear_error (&error);
1883   g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1884   return TRUE; /* returning TRUE means that we handled the method invocation */
1885 }
1886 
1887 /* ---------------------------------------------------------------------------------------------------- */
1888 
1889 /* runs in thread dedicated to handling method call */
1890 static gboolean
1891 handle_repair (UDisksFilesystem      *filesystem,
1892                GDBusMethodInvocation *invocation,
1893                GVariant              *options)
1894 {
1895   UDisksBlock *block = NULL;
1896   UDisksObject *object = NULL;
1897   UDisksDaemon *daemon = NULL;
1898   UDisksState *state = NULL;
1899   const gchar *probed_fs_usage = NULL;
1900   const gchar *probed_fs_type = NULL;
1901   const gchar *action_id = NULL;
1902   const gchar *message = NULL;
1903   uid_t caller_uid;
1904   GError *error = NULL;
1905   gboolean ret = FALSE;
1906   UDisksBaseJob *job = NULL;
1907   gchar *required_utility = NULL;
1908   const gchar * const *existing_mount_points = NULL;
1909 
1910   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
1911 
1912   object = udisks_daemon_util_dup_object (filesystem, &error);
1913   if (object == NULL)
1914     {
1915       g_dbus_method_invocation_take_error (invocation, error);
1916       goto out;
1917     }
1918 
1919   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
1920   state = udisks_daemon_get_state (daemon);
1921   block = udisks_object_peek_block (object);
1922 
1923   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
1924   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
1925 
1926   if (! udisks_daemon_util_get_caller_uid_sync (daemon,
1927                                                 invocation,
1928                                                 NULL /* GCancellable */,
1929                                                 &caller_uid,
1930                                                 &error))
1931     {
1932       g_dbus_method_invocation_return_gerror (invocation, error);
1933       goto out;
1934     }
1935 
1936   probed_fs_usage = udisks_block_get_id_usage (block);
1937   if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
1938     {
1939       g_dbus_method_invocation_return_error (invocation,
1940                                              UDISKS_ERROR,
1941                                              UDISKS_ERROR_NOT_SUPPORTED,
1942                                              "Cannot repair %s filesystem on %s",
1943                                              probed_fs_usage,
1944                                              udisks_block_get_device (block));
1945       goto out;
1946     }
1947 
1948   probed_fs_type = udisks_block_get_id_type (block);
1949   if (! bd_fs_can_repair (probed_fs_type, &required_utility, &error))
1950     {
1951       if (error != NULL)
1952         g_dbus_method_invocation_return_error (invocation,
1953                                                UDISKS_ERROR,
1954                                                UDISKS_ERROR_FAILED,
1955                                                "Cannot repair %s filesystem on %s: %s",
1956                                                probed_fs_type,
1957                                                udisks_block_get_device (block),
1958                                                error->message);
1959       else
1960         g_dbus_method_invocation_return_error (invocation,
1961                                                UDISKS_ERROR,
1962                                                UDISKS_ERROR_FAILED,
1963                                                "Cannot repair %s filesystem on %s: executable %s not found",
1964                                                probed_fs_type,
1965                                                udisks_block_get_device (block),
1966                                                required_utility);
1967       goto out;
1968     }
1969 
1970   /* check the mount state */
1971   existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
1972   if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
1973     {
1974       g_dbus_method_invocation_return_error (invocation,
1975                                              UDISKS_ERROR,
1976                                              UDISKS_ERROR_NOT_SUPPORTED,
1977                                              "Cannot repair %s filesystem on %s if mounted",
1978                                              probed_fs_usage,
1979                                              udisks_block_get_device (block));
1980     }
1981 
1982   action_id = "org.freedesktop.udisks2.modify-device";
1983   /* Translators: Shown in authentication dialog when the user
1984    * requests resizing the filesystem.
1985    *
1986    * Do not translate $(drive), it's a placeholder and
1987    * will be replaced by the name of the drive/device in question
1988    */
1989   message = N_("Authentication is required to repair the filesystem on $(drive)");
1990   if (! udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
1991     {
1992       if (udisks_block_get_hint_system (block))
1993         {
1994           action_id = "org.freedesktop.udisks2.modify-device-system";
1995         }
1996       else if (! udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
1997         {
1998           action_id = "org.freedesktop.udisks2.modify-device-other-seat";
1999         }
2000     }
2001 
2002   /* Check that the user is actually authorized to repair the filesystem. */
2003   if (! udisks_daemon_util_check_authorization_sync (daemon,
2004                                                      object,
2005                                                      action_id,
2006                                                      options,
2007                                                      message,
2008                                                      invocation))
2009     goto out;
2010 
2011   job = udisks_daemon_launch_simple_job (daemon,
2012                                          UDISKS_OBJECT (object),
2013                                          "filesystem-repair",
2014                                          caller_uid,
2015                                          NULL);
2016   if (job == NULL)
2017     {
2018       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2019                                              "Failed to create a job object");
2020       goto out;
2021     }
2022 
2023   udisks_bd_thread_set_progress_for_job (UDISKS_JOB (job));
2024   ret = bd_fs_repair (udisks_block_get_device (block), &error);
2025   if (error)
2026     {
2027       g_dbus_method_invocation_return_error (invocation,
2028                                              UDISKS_ERROR,
2029                                              UDISKS_ERROR_FAILED,
2030                                              "Error reparing filesystem on %s: %s",
2031                                              udisks_block_get_device (block),
2032                                              error->message);
2033       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
2034       goto out;
2035     }
2036 
2037   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
2038                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
2039   udisks_filesystem_complete_repair (filesystem, invocation, ret);
2040   udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
2041 
2042  out:
2043   udisks_bd_thread_disable_progress ();
2044   if (object != NULL)
2045     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
2046   if (state != NULL)
2047     udisks_state_check (state);
2048   g_clear_object (&object);
2049   g_free (required_utility);
2050   g_clear_error (&error);
2051   g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
2052   return TRUE; /* returning TRUE means that we handled the method invocation */
2053 }
2054 
2055 /* ---------------------------------------------------------------------------------------------------- */
2056 
2057 /* runs in thread dedicated to handling method call */
2058 static gboolean
2059 handle_check (UDisksFilesystem      *filesystem,
2060               GDBusMethodInvocation *invocation,
2061               GVariant              *options)
2062 {
2063   UDisksBlock *block = NULL;
2064   UDisksObject *object = NULL;
2065   UDisksDaemon *daemon = NULL;
2066   UDisksState *state = NULL;
2067   const gchar *probed_fs_usage = NULL;
2068   const gchar *probed_fs_type = NULL;
2069   const gchar *action_id = NULL;
2070   const gchar *message = NULL;
2071   uid_t caller_uid;
2072   GError *error = NULL;
2073   gboolean ret = FALSE;
2074   UDisksBaseJob *job = NULL;
2075   gchar *required_utility = NULL;
2076   const gchar * const *existing_mount_points = NULL;
2077 
2078   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
2079 
2080   object = udisks_daemon_util_dup_object (filesystem, &error);
2081   if (object == NULL)
2082     {
2083       g_dbus_method_invocation_take_error (invocation, error);
2084       goto out;
2085     }
2086 
2087   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
2088   state = udisks_daemon_get_state (daemon);
2089   block = udisks_object_peek_block (object);
2090 
2091   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
2092   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
2093 
2094   if (! udisks_daemon_util_get_caller_uid_sync (daemon,
2095                                                 invocation,
2096                                                 NULL /* GCancellable */,
2097                                                 &caller_uid,
2098                                                 &error))
2099     {
2100       g_dbus_method_invocation_return_gerror (invocation, error);
2101       goto out;
2102     }
2103 
2104   probed_fs_usage = udisks_block_get_id_usage (block);
2105   if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
2106     {
2107       g_dbus_method_invocation_return_error (invocation,
2108                                              UDISKS_ERROR,
2109                                              UDISKS_ERROR_NOT_SUPPORTED,
2110                                              "Cannot check %s filesystem on %s",
2111                                              probed_fs_usage,
2112                                              udisks_block_get_device (block));
2113       goto out;
2114     }
2115 
2116   probed_fs_type = udisks_block_get_id_type (block);
2117   if (! bd_fs_can_check (probed_fs_type, &required_utility, &error))
2118     {
2119       if (error != NULL)
2120         g_dbus_method_invocation_return_error (invocation,
2121                                                UDISKS_ERROR,
2122                                                UDISKS_ERROR_FAILED,
2123                                                "Cannot check %s filesystem on %s: %s",
2124                                                probed_fs_type,
2125                                                udisks_block_get_device (block),
2126                                                error->message);
2127       else
2128         g_dbus_method_invocation_return_error (invocation,
2129                                                UDISKS_ERROR,
2130                                                UDISKS_ERROR_FAILED,
2131                                                "Cannot check %s filesystem on %s: executable %s not found",
2132                                                probed_fs_type,
2133                                                udisks_block_get_device (block),
2134                                                required_utility);
2135       goto out;
2136     }
2137 
2138   /* check the mount state */
2139   existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
2140   if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
2141     {
2142       g_dbus_method_invocation_return_error (invocation,
2143                                              UDISKS_ERROR,
2144                                              UDISKS_ERROR_NOT_SUPPORTED,
2145                                              "Cannot check %s filesystem on %s if mounted",
2146                                              probed_fs_usage,
2147                                              udisks_block_get_device (block));
2148     }
2149 
2150   action_id = "org.freedesktop.udisks2.modify-device";
2151   /* Translators: Shown in authentication dialog when the user
2152    * requests resizing the filesystem.
2153    *
2154    * Do not translate $(drive), it's a placeholder and
2155    * will be replaced by the name of the drive/device in question
2156    */
2157   message = N_("Authentication is required to check the filesystem on $(drive)");
2158   if (! udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
2159     {
2160       if (udisks_block_get_hint_system (block))
2161         {
2162           action_id = "org.freedesktop.udisks2.modify-device-system";
2163         }
2164       else if (! udisks_daemon_util_on_user_seat (daemon, UDISKS_OBJECT (object), caller_uid))
2165         {
2166           action_id = "org.freedesktop.udisks2.modify-device-other-seat";
2167         }
2168     }
2169 
2170   /* Check that the user is actually authorized to check the filesystem. */
2171   if (! udisks_daemon_util_check_authorization_sync (daemon,
2172                                                      object,
2173                                                      action_id,
2174                                                      options,
2175                                                      message,
2176                                                      invocation))
2177     goto out;
2178 
2179   job = udisks_daemon_launch_simple_job (daemon,
2180                                          UDISKS_OBJECT (object),
2181                                          "filesystem-check",
2182                                          caller_uid,
2183                                          NULL);
2184   if (job == NULL)
2185     {
2186       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2187                                              "Failed to create a job object");
2188       goto out;
2189     }
2190 
2191   udisks_bd_thread_set_progress_for_job (UDISKS_JOB (job));
2192   ret = bd_fs_check (udisks_block_get_device (block), &error);
2193   if (error)
2194     {
2195       g_dbus_method_invocation_return_error (invocation,
2196                                              UDISKS_ERROR,
2197                                              UDISKS_ERROR_FAILED,
2198                                              "Error checking filesystem on %s: %s",
2199                                              udisks_block_get_device (block),
2200                                              error->message);
2201       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
2202       goto out;
2203     }
2204 
2205   udisks_linux_block_object_trigger_uevent_sync (UDISKS_LINUX_BLOCK_OBJECT (object),
2206                                                  UDISKS_DEFAULT_WAIT_TIMEOUT);
2207   udisks_filesystem_complete_check (filesystem, invocation, ret);
2208   udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
2209 
2210  out:
2211   udisks_bd_thread_disable_progress ();
2212   if (object != NULL)
2213     udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
2214   if (state != NULL)
2215     udisks_state_check (state);
2216   g_clear_object (&object);
2217   g_free (required_utility);
2218   g_clear_error (&error);
2219   g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
2220   return TRUE; /* returning TRUE means that we handled the method invocation */
2221 }
2222 
2223 static gboolean
2224 handle_take_ownership (UDisksFilesystem      *filesystem,
2225                        GDBusMethodInvocation *invocation,
2226                        GVariant              *options)
2227 {
2228   UDisksBlock *block = NULL;
2229   UDisksObject *object = NULL;
2230   UDisksDaemon *daemon = NULL;
2231   UDisksState *state = NULL;
2232   const gchar *probed_fs_usage = NULL;
2233   const gchar *probed_fs_type = NULL;
2234   const gchar *action_id = NULL;
2235   const gchar *message = NULL;
2236   const FSInfo *fs_info = NULL;
2237   UDisksBaseJob *job = NULL;
2238   GError *error = NULL;
2239   gboolean recursive = FALSE;
2240   uid_t caller_uid;
2241   gid_t caller_gid;
2242 
2243   g_variant_lookup (options, "recursive", "b", &recursive);
2244 
2245   /* only allow a single call at a time */
2246   g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
2247 
2248   object = udisks_daemon_util_dup_object (filesystem, &error);
2249   if (object == NULL)
2250     {
2251       g_dbus_method_invocation_take_error (invocation, error);
2252       goto out;
2253     }
2254 
2255   block = udisks_object_peek_block (object);
2256   daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
2257   state = udisks_daemon_get_state (daemon);
2258 
2259   udisks_linux_block_object_lock_for_cleanup (UDISKS_LINUX_BLOCK_OBJECT (object));
2260   udisks_state_check_block (state, udisks_linux_block_object_get_device_number (UDISKS_LINUX_BLOCK_OBJECT (object)));
2261 
2262   if (! udisks_daemon_util_get_caller_uid_sync (daemon,
2263                                                 invocation,
2264                                                 NULL /* GCancellable */,
2265                                                 &caller_uid,
2266                                                 &error))
2267     {
2268       g_dbus_method_invocation_return_gerror (invocation, error);
2269       goto out;
2270     }
2271 
2272   if (!udisks_daemon_util_get_user_info (caller_uid, &caller_gid, NULL /* user name */, &error))
2273     {
2274       g_dbus_method_invocation_return_gerror (invocation, error);
2275       goto out;
2276     }
2277 
2278   probed_fs_usage = udisks_block_get_id_usage (block);
2279   if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
2280     {
2281       g_dbus_method_invocation_return_error (invocation,
2282                                              UDISKS_ERROR,
2283                                              UDISKS_ERROR_NOT_SUPPORTED,
2284                                              "Cannot take ownership of %s filesystem on %s",
2285                                              probed_fs_usage,
2286                                              udisks_block_get_device (block));
2287       goto out;
2288     }
2289 
2290   probed_fs_type = udisks_block_get_id_type (block);
2291   fs_info = get_fs_info (probed_fs_type);
2292   if (fs_info == NULL || !fs_info->supports_owners)
2293     {
2294       g_dbus_method_invocation_return_error (invocation,
2295                                              UDISKS_ERROR,
2296                                              UDISKS_ERROR_NOT_SUPPORTED,
2297                                              "Filesystem %s doesn't support ownership",
2298                                              probed_fs_usage);
2299       goto out;
2300     }
2301 
2302   action_id = "org.freedesktop.udisks2.filesystem-take-ownership";
2303   /* Translators: Shown in authentication dialog when the user
2304    * requests taking ownership of the filesystem.
2305    *
2306    * Do not translate $(drive), it's a placeholder and
2307    * will be replaced by the name of the drive/device in question
2308    */
2309   message = N_("Authentication is required to change ownership of the filesystem on $(drive)");
2310 
2311   /* Check that the user is actually authorized to check the filesystem. */
2312   if (! udisks_daemon_util_check_authorization_sync (daemon,
2313                                                      object,
2314                                                      action_id,
2315                                                      options,
2316                                                      message,
2317                                                      invocation))
2318     goto out;
2319 
2320   job = udisks_daemon_launch_simple_job (daemon,
2321                                          UDISKS_OBJECT (object),
2322                                          "filesystem-modify",
2323                                          caller_uid,
2324                                          NULL);
2325   if (job == NULL)
2326     {
2327       g_dbus_method_invocation_return_error (invocation, UDISKS_ERROR, UDISKS_ERROR_FAILED,
2328                                              "Failed to create a job object");
2329       goto out;
2330     }
2331 
2332   if (! take_filesystem_ownership (udisks_block_get_device (block),
2333                                    probed_fs_type,
2334                                    caller_uid, caller_gid,
2335                                    recursive,
2336                                    &error))
2337     {
2338       g_dbus_method_invocation_return_error (invocation,
2339                                              UDISKS_ERROR,
2340                                              UDISKS_ERROR_FAILED,
2341                                              "Error taking ownership of filesystem on %s: %s",
2342                                              udisks_block_get_device (block),
2343                                              error->message);
2344       udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), FALSE, error->message);
2345       goto out;
2346     }
2347 
2348 
2349   udisks_filesystem_complete_take_ownership (filesystem, invocation);
2350   udisks_simple_job_complete (UDISKS_SIMPLE_JOB (job), TRUE, NULL);
2351 
2352   out:
2353    if (object != NULL)
2354      udisks_linux_block_object_release_cleanup_lock (UDISKS_LINUX_BLOCK_OBJECT (object));
2355    if (state != NULL)
2356      udisks_state_check (state);
2357    g_clear_object (&object);
2358    g_clear_error (&error);
2359    g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
2360    return TRUE; /* returning TRUE means that we handled the method invocation */
2361 }
2362 
2363 /* ---------------------------------------------------------------------------------------------------- */
2364 
2365 static void
2366 filesystem_iface_init (UDisksFilesystemIface *iface)
2367 {
2368   iface->handle_mount     = handle_mount;
2369   iface->handle_unmount   = handle_unmount;
2370   iface->handle_set_label = handle_set_label;
2371   iface->handle_resize    = handle_resize;
2372   iface->handle_repair    = handle_repair;
2373   iface->handle_check     = handle_check;
2374   iface->handle_take_ownership = handle_take_ownership;
2375 }
2376