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