~behda/+junk/udisks2.original

« back to all changes in this revision

Viewing changes to src/udiskslinuxfilesystem.c

  • Committer: behda
  • Date: 2014-05-24 15:15:11 UTC
  • Revision ID: pauvitk@gmail.com-20140524151511-3vtr0uubjewx3z2j
Initial commit of source code and Debian packaging.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 <pwd.h>
 
26
#include <grp.h>
 
27
#include <string.h>
 
28
#include <stdlib.h>
 
29
#include <stdio.h>
 
30
#include <mntent.h>
 
31
#include <sys/types.h>
 
32
#include <sys/acl.h>
 
33
#include <errno.h>
 
34
 
 
35
#include <glib/gstdio.h>
 
36
 
 
37
#include "udiskslogging.h"
 
38
#include "udiskslinuxfilesystem.h"
 
39
#include "udiskslinuxblockobject.h"
 
40
#include "udiskslinuxfsinfo.h"
 
41
#include "udisksdaemon.h"
 
42
#include "udisksstate.h"
 
43
#include "udisksdaemonutil.h"
 
44
#include "udisksmountmonitor.h"
 
45
#include "udisksmount.h"
 
46
#include "udiskslinuxdevice.h"
 
47
 
 
48
/**
 
49
 * SECTION:udiskslinuxfilesystem
 
50
 * @title: UDisksLinuxFilesystem
 
51
 * @short_description: Linux implementation of #UDisksFilesystem
 
52
 *
 
53
 * This type provides an implementation of the #UDisksFilesystem
 
54
 * interface on Linux.
 
55
 */
 
56
 
 
57
typedef struct _UDisksLinuxFilesystemClass   UDisksLinuxFilesystemClass;
 
58
 
 
59
/**
 
60
 * UDisksLinuxFilesystem:
 
61
 *
 
62
 * The #UDisksLinuxFilesystem structure contains only private data and should
 
63
 * only be accessed using the provided API.
 
64
 */
 
65
struct _UDisksLinuxFilesystem
 
66
{
 
67
  UDisksFilesystemSkeleton parent_instance;
 
68
  GMutex lock;
 
69
};
 
70
 
 
71
struct _UDisksLinuxFilesystemClass
 
72
{
 
73
  UDisksFilesystemSkeletonClass parent_class;
 
74
};
 
75
 
 
76
static void filesystem_iface_init (UDisksFilesystemIface *iface);
 
77
 
 
78
G_DEFINE_TYPE_WITH_CODE (UDisksLinuxFilesystem, udisks_linux_filesystem, UDISKS_TYPE_FILESYSTEM_SKELETON,
 
79
                         G_IMPLEMENT_INTERFACE (UDISKS_TYPE_FILESYSTEM, filesystem_iface_init));
 
80
 
 
81
/* ---------------------------------------------------------------------------------------------------- */
 
82
 
 
83
static void
 
84
udisks_linux_filesystem_finalize (GObject *object)
 
85
{
 
86
  UDisksLinuxFilesystem *filesystem = UDISKS_LINUX_FILESYSTEM (object);
 
87
 
 
88
  g_mutex_clear (&(filesystem->lock));
 
89
 
 
90
  if (G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize != NULL)
 
91
    G_OBJECT_CLASS (udisks_linux_filesystem_parent_class)->finalize (object);
 
92
}
 
93
 
 
94
static void
 
95
udisks_linux_filesystem_init (UDisksLinuxFilesystem *filesystem)
 
96
{
 
97
  g_mutex_init (&filesystem->lock);
 
98
  g_dbus_interface_skeleton_set_flags (G_DBUS_INTERFACE_SKELETON (filesystem),
 
99
                                       G_DBUS_INTERFACE_SKELETON_FLAGS_HANDLE_METHOD_INVOCATIONS_IN_THREAD);
 
100
}
 
101
 
 
102
static void
 
103
udisks_linux_filesystem_class_init (UDisksLinuxFilesystemClass *klass)
 
104
{
 
105
  GObjectClass *gobject_class;
 
106
 
 
107
  gobject_class = G_OBJECT_CLASS (klass);
 
108
  gobject_class->finalize     = udisks_linux_filesystem_finalize;
 
109
}
 
110
 
 
111
/**
 
112
 * udisks_linux_filesystem_new:
 
113
 *
 
114
 * Creates a new #UDisksLinuxFilesystem instance.
 
115
 *
 
116
 * Returns: A new #UDisksLinuxFilesystem. Free with g_object_unref().
 
117
 */
 
118
UDisksFilesystem *
 
119
udisks_linux_filesystem_new (void)
 
120
{
 
121
  return UDISKS_FILESYSTEM (g_object_new (UDISKS_TYPE_LINUX_FILESYSTEM,
 
122
                                          NULL));
 
123
}
 
124
 
 
125
/* ---------------------------------------------------------------------------------------------------- */
 
126
 
 
127
/**
 
128
 * udisks_linux_filesystem_update:
 
129
 * @filesystem: A #UDisksLinuxFilesystem.
 
130
 * @object: The enclosing #UDisksLinuxBlockObject instance.
 
131
 *
 
132
 * Updates the interface.
 
133
 */
 
134
void
 
135
udisks_linux_filesystem_update (UDisksLinuxFilesystem  *filesystem,
 
136
                                UDisksLinuxBlockObject *object)
 
137
{
 
138
  UDisksMountMonitor *mount_monitor;
 
139
  UDisksLinuxDevice *device;
 
140
  GPtrArray *p;
 
141
  GList *mounts;
 
142
  GList *l;
 
143
 
 
144
  mount_monitor = udisks_daemon_get_mount_monitor (udisks_linux_block_object_get_daemon (object));
 
145
  device = udisks_linux_block_object_get_device (object);
 
146
 
 
147
  p = g_ptr_array_new ();
 
148
  mounts = udisks_mount_monitor_get_mounts_for_dev (mount_monitor, g_udev_device_get_device_number (device->udev_device));
 
149
  /* we are guaranteed that the list is sorted so if there are
 
150
   * multiple mounts we'll always get the same order
 
151
   */
 
152
  for (l = mounts; l != NULL; l = l->next)
 
153
    {
 
154
      UDisksMount *mount = UDISKS_MOUNT (l->data);
 
155
      if (udisks_mount_get_mount_type (mount) == UDISKS_MOUNT_TYPE_FILESYSTEM)
 
156
        g_ptr_array_add (p, (gpointer) udisks_mount_get_mount_path (mount));
 
157
    }
 
158
  g_ptr_array_add (p, NULL);
 
159
  udisks_filesystem_set_mount_points (UDISKS_FILESYSTEM (filesystem),
 
160
                                      (const gchar *const *) p->pdata);
 
161
  g_ptr_array_free (p, TRUE);
 
162
  g_list_foreach (mounts, (GFunc) g_object_unref, NULL);
 
163
  g_list_free (mounts);
 
164
  g_object_unref (device);
 
165
}
 
166
 
 
167
/* ---------------------------------------------------------------------------------------------------- */
 
168
 
 
169
static const gchar *well_known_filesystems[] =
 
170
{
 
171
  "btrfs",
 
172
  "ext2",
 
173
  "ext3",
 
174
  "ext4",
 
175
  "udf",
 
176
  "iso9660",
 
177
  "xfs",
 
178
  "jfs",
 
179
  "nilfs",
 
180
  "reiserfs",
 
181
  "reiser4",
 
182
  "msdos",
 
183
  "umsdos",
 
184
  "vfat",
 
185
  "exfat",
 
186
  "ntfs",
 
187
  NULL,
 
188
};
 
189
 
 
190
static gboolean
 
191
is_in_filesystem_file (const gchar *filesystems_file,
 
192
                       const gchar *fstype)
 
193
{
 
194
  gchar *filesystems = NULL;
 
195
  GError *error = NULL;
 
196
  gboolean ret = FALSE;
 
197
  gchar **lines = NULL;
 
198
  guint n;
 
199
 
 
200
  if (!g_file_get_contents (filesystems_file,
 
201
                            &filesystems,
 
202
                            NULL, /* gsize *out_length */
 
203
                            &error))
 
204
    {
 
205
      udisks_warning ("Error reading %s: %s (%s %d)",
 
206
                      filesystems_file,
 
207
                      error->message,
 
208
                      g_quark_to_string (error->domain),
 
209
                      error->code);
 
210
      g_error_free (error);
 
211
      goto out;
 
212
    }
 
213
 
 
214
  lines = g_strsplit (filesystems, "\n", -1);
 
215
  for (n = 0; lines != NULL && lines[n] != NULL && !ret; n++)
 
216
    {
 
217
      gchar **tokens;
 
218
      gint num_tokens;
 
219
      g_strdelimit (lines[n], " \t", ' ');
 
220
      g_strstrip (lines[n]);
 
221
      tokens = g_strsplit (lines[n], " ", -1);
 
222
      num_tokens = g_strv_length (tokens);
 
223
      if (num_tokens == 1 && g_strcmp0 (tokens[0], fstype) == 0)
 
224
        {
 
225
          ret = TRUE;
 
226
        }
 
227
      g_strfreev (tokens);
 
228
    }
 
229
 
 
230
 out:
 
231
  g_strfreev (lines);
 
232
  g_free (filesystems);
 
233
  return ret;
 
234
}
 
235
 
 
236
static gboolean
 
237
is_well_known_filesystem (const gchar *fstype)
 
238
{
 
239
  gboolean ret = FALSE;
 
240
  guint n;
 
241
 
 
242
  for (n = 0; well_known_filesystems[n] != NULL; n++)
 
243
    {
 
244
      if (g_strcmp0 (well_known_filesystems[n], fstype) == 0)
 
245
        {
 
246
          ret = TRUE;
 
247
          goto out;
 
248
        }
 
249
    }
 
250
 out:
 
251
  return ret;
 
252
}
 
253
 
 
254
/* this is not a very efficient implementation but it's very rarely
 
255
 * called so no real point in optimizing it...
 
256
 */
 
257
static gboolean
 
258
is_allowed_filesystem (const gchar *fstype)
 
259
{
 
260
  return is_well_known_filesystem (fstype) ||
 
261
    is_in_filesystem_file ("/proc/filesystems", fstype) ||
 
262
    is_in_filesystem_file ("/etc/filesystems", fstype);
 
263
}
 
264
 
 
265
/* ---------------------------------------------------------------------------------------------------- */
 
266
 
 
267
typedef struct
 
268
{
 
269
  const gchar *fstype;
 
270
  const gchar * const *defaults;
 
271
  const gchar * const *allow;
 
272
  const gchar * const *allow_uid_self;
 
273
  const gchar * const *allow_gid_self;
 
274
} FSMountOptions;
 
275
 
 
276
/* ---------------------- vfat -------------------- */
 
277
 
 
278
static const gchar *vfat_defaults[] = { "uid=", "gid=", "shortname=mixed", "dmask=0077", "utf8=1", "showexec", "flush", NULL };
 
279
static const gchar *vfat_allow[] = { "flush", "utf8=", "shortname=", "umask=", "dmask=", "fmask=", "codepage=", "iocharset=", "usefree", "showexec", NULL };
 
280
static const gchar *vfat_allow_uid_self[] = { "uid=", NULL };
 
281
static const gchar *vfat_allow_gid_self[] = { "gid=", NULL };
 
282
 
 
283
/* ---------------------- ntfs -------------------- */
 
284
/* this is assuming that ntfs-3g is used */
 
285
 
 
286
static const gchar *ntfs_defaults[] = { "uid=", "gid=", "dmask=0077", "fmask=0177", NULL };
 
287
static const gchar *ntfs_allow[] = { "umask=", "dmask=", "fmask=", "locale=", "norecover", "ignore_case", "windows_names", "compression", "nocompression", NULL };
 
288
static const gchar *ntfs_allow_uid_self[] = { "uid=", NULL };
 
289
static const gchar *ntfs_allow_gid_self[] = { "gid=", NULL };
 
290
 
 
291
/* ---------------------- iso9660 -------------------- */
 
292
 
 
293
static const gchar *iso9660_defaults[] = { "uid=", "gid=", "iocharset=utf8", "mode=0400", "dmode=0500", NULL };
 
294
static const gchar *iso9660_allow[] = { "norock", "nojoliet", "iocharset=", "mode=", "dmode=", NULL };
 
295
static const gchar *iso9660_allow_uid_self[] = { "uid=", NULL };
 
296
static const gchar *iso9660_allow_gid_self[] = { "gid=", NULL };
 
297
 
 
298
/* ---------------------- udf -------------------- */
 
299
 
 
300
static const gchar *udf_defaults[] = { "uid=", "gid=", "iocharset=utf8", "umask=0077", NULL };
 
301
static const gchar *udf_allow[] = { "iocharset=", "umask=", NULL };
 
302
static const gchar *udf_allow_uid_self[] = { "uid=", NULL };
 
303
static const gchar *udf_allow_gid_self[] = { "gid=", NULL };
 
304
 
 
305
/* ---------------------- exfat -------------------- */
 
306
 
 
307
static const gchar *exfat_defaults[] = { "uid=", "gid=", "iocharset=utf8", "namecase=0", "errors=remount-ro", "umask=0077", NULL };
 
308
static const gchar *exfat_allow[] = { "dmask=", "errors=", "fmask=", "iocharset=", "namecase=", "umask=", NULL };
 
309
static const gchar *exfat_allow_uid_self[] = { "uid=", NULL };
 
310
static const gchar *exfat_allow_gid_self[] = { "gid=", NULL };
 
311
 
 
312
/* ------------------------------------------------ */
 
313
/* TODO: support context= */
 
314
 
 
315
static const gchar *any_allow[] = { "exec", "noexec", "nodev", "nosuid", "atime", "noatime", "nodiratime", "ro", "rw", "sync", "dirsync", NULL };
 
316
 
 
317
static const FSMountOptions fs_mount_options[] =
 
318
  {
 
319
    { "vfat", vfat_defaults, vfat_allow, vfat_allow_uid_self, vfat_allow_gid_self },
 
320
    { "ntfs", ntfs_defaults, ntfs_allow, ntfs_allow_uid_self, ntfs_allow_gid_self },
 
321
    { "iso9660", iso9660_defaults, iso9660_allow, iso9660_allow_uid_self, iso9660_allow_gid_self },
 
322
    { "udf", udf_defaults, udf_allow, udf_allow_uid_self, udf_allow_gid_self },
 
323
    { "exfat", exfat_defaults, exfat_allow, exfat_allow_uid_self, exfat_allow_gid_self },
 
324
  };
 
325
 
 
326
/* ------------------------------------------------ */
 
327
 
 
328
static int num_fs_mount_options = sizeof(fs_mount_options) / sizeof(FSMountOptions);
 
329
 
 
330
static const FSMountOptions *
 
331
find_mount_options_for_fs (const gchar *fstype)
 
332
{
 
333
  int n;
 
334
  const FSMountOptions *fsmo;
 
335
 
 
336
  for (n = 0; n < num_fs_mount_options; n++)
 
337
    {
 
338
      fsmo = fs_mount_options + n;
 
339
      if (g_strcmp0 (fsmo->fstype, fstype) == 0)
 
340
        goto out;
 
341
    }
 
342
 
 
343
  fsmo = NULL;
 
344
 out:
 
345
  return fsmo;
 
346
}
 
347
 
 
348
static gid_t
 
349
find_primary_gid (uid_t uid)
 
350
{
 
351
  struct passwd *pw = NULL;
 
352
  struct passwd pwstruct;
 
353
  gchar pwbuf[8192];
 
354
  int rc;
 
355
  gid_t gid;
 
356
 
 
357
  gid = (gid_t) - 1;
 
358
 
 
359
  rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
 
360
  if (rc != 0 || pw == NULL)
 
361
    {
 
362
      udisks_warning ("Error looking up uid %d: %m", uid);
 
363
      goto out;
 
364
    }
 
365
  gid = pw->pw_gid;
 
366
 
 
367
 out:
 
368
  return gid;
 
369
}
 
370
 
 
371
static gboolean
 
372
is_uid_in_gid (uid_t uid,
 
373
               gid_t gid)
 
374
{
 
375
  gboolean ret;
 
376
  struct passwd *pw = NULL;
 
377
  struct passwd pwstruct;
 
378
  gchar pwbuf[8192];
 
379
  int rc;
 
380
  static gid_t supplementary_groups[128];
 
381
  int num_supplementary_groups = 128;
 
382
  int n;
 
383
 
 
384
  /* TODO: use some #define instead of harcoding some random number like 128 */
 
385
 
 
386
  ret = FALSE;
 
387
 
 
388
  rc = getpwuid_r (uid, &pwstruct, pwbuf, sizeof pwbuf, &pw);
 
389
  if (rc != 0 || pw == NULL)
 
390
    {
 
391
      udisks_warning ("Error looking up uid %d: %m", uid);
 
392
      goto out;
 
393
    }
 
394
  if (pw->pw_gid == gid)
 
395
    {
 
396
      ret = TRUE;
 
397
      goto out;
 
398
    }
 
399
 
 
400
  if (getgrouplist (pw->pw_name, pw->pw_gid, supplementary_groups, &num_supplementary_groups) < 0)
 
401
    {
 
402
      udisks_warning ("Error getting supplementary groups for uid %d: %m", uid);
 
403
      goto out;
 
404
    }
 
405
 
 
406
  for (n = 0; n < num_supplementary_groups; n++)
 
407
    {
 
408
      if (supplementary_groups[n] == gid)
 
409
        {
 
410
          ret = TRUE;
 
411
          goto out;
 
412
        }
 
413
    }
 
414
 
 
415
 out:
 
416
  return ret;
 
417
}
 
418
 
 
419
static gboolean
 
420
is_mount_option_allowed (const FSMountOptions *fsmo,
 
421
                         const gchar          *option,
 
422
                         uid_t                 caller_uid)
 
423
{
 
424
  int n;
 
425
  gchar *endp;
 
426
  uid_t uid;
 
427
  gid_t gid;
 
428
  gboolean allowed;
 
429
  const gchar *ep;
 
430
  gsize ep_len;
 
431
 
 
432
  allowed = FALSE;
 
433
 
 
434
  /* first run through the allowed mount options */
 
435
  if (fsmo != NULL)
 
436
    {
 
437
      for (n = 0; fsmo->allow != NULL && fsmo->allow[n] != NULL; n++)
 
438
        {
 
439
          ep = strstr (fsmo->allow[n], "=");
 
440
          if (ep != NULL && ep[1] == '\0')
 
441
            {
 
442
              ep_len = ep - fsmo->allow[n] + 1;
 
443
              if (strncmp (fsmo->allow[n], option, ep_len) == 0)
 
444
                {
 
445
                  allowed = TRUE;
 
446
                  goto out;
 
447
                }
 
448
            }
 
449
          else
 
450
            {
 
451
              if (strcmp (fsmo->allow[n], option) == 0)
 
452
                {
 
453
                  allowed = TRUE;
 
454
                  goto out;
 
455
                }
 
456
            }
 
457
        }
 
458
    }
 
459
  for (n = 0; any_allow[n] != NULL; n++)
 
460
    {
 
461
      ep = strstr (any_allow[n], "=");
 
462
      if (ep != NULL && ep[1] == '\0')
 
463
        {
 
464
          ep_len = ep - any_allow[n] + 1;
 
465
          if (strncmp (any_allow[n], option, ep_len) == 0)
 
466
            {
 
467
              allowed = TRUE;
 
468
              goto out;
 
469
            }
 
470
        }
 
471
      else
 
472
        {
 
473
          if (strcmp (any_allow[n], option) == 0)
 
474
            {
 
475
              allowed = TRUE;
 
476
              goto out;
 
477
            }
 
478
        }
 
479
    }
 
480
 
 
481
  /* .. then check for mount options where the caller is allowed to pass
 
482
   * in his own uid
 
483
   */
 
484
  if (fsmo != NULL)
 
485
    {
 
486
      for (n = 0; fsmo->allow_uid_self != NULL && fsmo->allow_uid_self[n] != NULL; n++)
 
487
        {
 
488
          const gchar *r_mount_option = fsmo->allow_uid_self[n];
 
489
          if (g_str_has_prefix (option, r_mount_option))
 
490
            {
 
491
              uid = strtol (option + strlen (r_mount_option), &endp, 10);
 
492
              if (*endp != '\0')
 
493
                continue;
 
494
              if (uid == caller_uid)
 
495
                {
 
496
                  allowed = TRUE;
 
497
                  goto out;
 
498
                }
 
499
            }
 
500
        }
 
501
    }
 
502
 
 
503
  /* .. ditto for gid
 
504
   */
 
505
  if (fsmo != NULL)
 
506
    {
 
507
      for (n = 0; fsmo->allow_gid_self != NULL && fsmo->allow_gid_self[n] != NULL; n++)
 
508
        {
 
509
          const gchar *r_mount_option = fsmo->allow_gid_self[n];
 
510
          if (g_str_has_prefix (option, r_mount_option))
 
511
            {
 
512
              gid = strtol (option + strlen (r_mount_option), &endp, 10);
 
513
              if (*endp != '\0')
 
514
                continue;
 
515
              if (is_uid_in_gid (caller_uid, gid))
 
516
                {
 
517
                  allowed = TRUE;
 
518
                  goto out;
 
519
                }
 
520
            }
 
521
        }
 
522
    }
 
523
 
 
524
 out:
 
525
  return allowed;
 
526
}
 
527
 
 
528
static gchar **
 
529
prepend_default_mount_options (const FSMountOptions *fsmo,
 
530
                               uid_t                 caller_uid,
 
531
                               GVariant             *given_options)
 
532
{
 
533
  GPtrArray *options;
 
534
  gint n;
 
535
  gchar *s;
 
536
  gid_t gid;
 
537
  const gchar *option_string;
 
538
 
 
539
  options = g_ptr_array_new ();
 
540
  if (fsmo != NULL)
 
541
    {
 
542
      const gchar *const *defaults = fsmo->defaults;
 
543
 
 
544
      for (n = 0; defaults != NULL && defaults[n] != NULL; n++)
 
545
        {
 
546
          const gchar *option = defaults[n];
 
547
 
 
548
          if (strcmp (option, "uid=") == 0)
 
549
            {
 
550
              s = g_strdup_printf ("uid=%d", caller_uid);
 
551
              g_ptr_array_add (options, s);
 
552
            }
 
553
          else if (strcmp (option, "gid=") == 0)
 
554
            {
 
555
              gid = find_primary_gid (caller_uid);
 
556
              if (gid != (gid_t) - 1)
 
557
                {
 
558
                  s = g_strdup_printf ("gid=%d", gid);
 
559
                  g_ptr_array_add (options, s);
 
560
                }
 
561
            }
 
562
          else
 
563
            {
 
564
              g_ptr_array_add (options, g_strdup (option));
 
565
            }
 
566
        }
 
567
    }
 
568
 
 
569
  if (g_variant_lookup (given_options,
 
570
                        "options",
 
571
                        "&s", &option_string))
 
572
    {
 
573
      gchar **split_option_string;
 
574
      split_option_string = g_strsplit (option_string, ",", -1);
 
575
      for (n = 0; split_option_string[n] != NULL; n++)
 
576
        g_ptr_array_add (options, split_option_string[n]); /* steals string */
 
577
      g_free (split_option_string);
 
578
    }
 
579
  g_ptr_array_add (options, NULL);
 
580
 
 
581
  return (char **) g_ptr_array_free (options, FALSE);
 
582
}
 
583
 
 
584
static gchar *
 
585
subst_str (const gchar *str,
 
586
           const gchar *from,
 
587
           const gchar *to)
 
588
{
 
589
    gchar **parts;
 
590
    gchar *result;
 
591
 
 
592
    parts = g_strsplit (str, from, 0);
 
593
    result = g_strjoinv (to, parts);
 
594
    g_strfreev (parts);
 
595
    return result;
 
596
}
 
597
 
 
598
static gchar *
 
599
subst_str_and_escape (const gchar *str,
 
600
                      const gchar *from,
 
601
                      const gchar *to)
 
602
{
 
603
  gchar *quoted_and_escaped;
 
604
  gchar *ret;
 
605
  quoted_and_escaped = udisks_daemon_util_escape_and_quote (to);
 
606
  ret = subst_str (str, from, quoted_and_escaped);
 
607
  g_free (quoted_and_escaped);
 
608
  return ret;
 
609
}
 
610
 
 
611
/* ---------------------------------------------------------------------------------------------------- */
 
612
 
 
613
/*
 
614
 * calculate_fs_type: <internal>
 
615
 * @block: A #UDisksBlock.
 
616
 * @given_options: The a{sv} #GVariant.
 
617
 * @error: Return location for error or %NULL.
 
618
 *
 
619
 * Calculates the file system type to use.
 
620
 *
 
621
 * Returns: A valid UTF-8 string with the filesystem type (may be "auto") or %NULL if @error is set. Free with g_free().
 
622
 */
 
623
static gchar *
 
624
calculate_fs_type (UDisksBlock               *block,
 
625
                   GVariant                  *given_options,
 
626
                   GError                   **error)
 
627
{
 
628
  gchar *fs_type_to_use = NULL;
 
629
  const gchar *probed_fs_type = NULL;
 
630
  const gchar *requested_fs_type;
 
631
 
 
632
  probed_fs_type = NULL;
 
633
  if (block != NULL)
 
634
    probed_fs_type = udisks_block_get_id_type (block);
 
635
 
 
636
  if (g_variant_lookup (given_options,
 
637
                        "fstype",
 
638
                        "&s", &requested_fs_type) &&
 
639
      strlen (requested_fs_type) > 0)
 
640
    {
 
641
      /* If the user requests the filesystem type, error out unless the
 
642
       * filesystem type is
 
643
       *
 
644
       * - well-known [1]; or
 
645
       * - in the /proc/filesystems file; or
 
646
       * - in the /etc/filesystems file
 
647
       *
 
648
       * in that order. We do this because mount(8) on Linux allows
 
649
       * loading any arbitrary kernel module (when invoked as root) by
 
650
       * passing something appropriate to the -t option. So we have to
 
651
       * validate whatever we pass...
 
652
       *
 
653
       * See https://bugs.freedesktop.org/show_bug.cgi?id=32232 for more
 
654
       * details.
 
655
       *
 
656
       * [1] : since /etc/filesystems may be horribly out of date and
 
657
       *       not contain e.g. ext4
 
658
       */
 
659
      if (g_strcmp0 (requested_fs_type, "auto") != 0)
 
660
        {
 
661
          if (!is_allowed_filesystem (requested_fs_type))
 
662
            {
 
663
              g_set_error (error,
 
664
                           UDISKS_ERROR,
 
665
                           UDISKS_ERROR_OPTION_NOT_PERMITTED,
 
666
                           "Requested filesystem type `%s' is neither well-known nor "
 
667
                           "in /proc/filesystems nor in /etc/filesystems",
 
668
                           requested_fs_type);
 
669
              goto out;
 
670
            }
 
671
        }
 
672
 
 
673
      /* TODO: maybe check that it's compatible with probed_fs_type */
 
674
      fs_type_to_use = g_strdup (requested_fs_type);
 
675
    }
 
676
  else
 
677
    {
 
678
      if (probed_fs_type != NULL && strlen (probed_fs_type) > 0)
 
679
        fs_type_to_use = g_strdup (probed_fs_type);
 
680
      else
 
681
        fs_type_to_use = g_strdup ("auto");
 
682
    }
 
683
 
 
684
 out:
 
685
  g_assert (fs_type_to_use == NULL || g_utf8_validate (fs_type_to_use, -1, NULL));
 
686
 
 
687
  return fs_type_to_use;
 
688
}
 
689
 
 
690
/*
 
691
 * calculate_mount_options: <internal>
 
692
 * @daemon: A #UDisksDaemon.
 
693
 * @block: A #UDisksBlock.
 
694
 * @caller_uid: The uid of the caller making the request.
 
695
 * @fs_type: The filesystem type to use or %NULL.
 
696
 * @options: Options requested by the caller.
 
697
 * @error: Return location for error or %NULL.
 
698
 *
 
699
 * Calculates the mount option string to use. Ensures (by returning an
 
700
 * error) that only safe options are used.
 
701
 *
 
702
 * Returns: A string with mount options or %NULL if @error is set. Free with g_free().
 
703
 */
 
704
static gchar *
 
705
calculate_mount_options (UDisksDaemon              *daemon,
 
706
                         UDisksBlock               *block,
 
707
                         uid_t                      caller_uid,
 
708
                         const gchar               *fs_type,
 
709
                         GVariant                  *options,
 
710
                         GError                   **error)
 
711
{
 
712
  const FSMountOptions *fsmo;
 
713
  gchar **options_to_use;
 
714
  gchar *options_to_use_str;
 
715
  GString *str;
 
716
  guint n;
 
717
 
 
718
  options_to_use = NULL;
 
719
  options_to_use_str = NULL;
 
720
 
 
721
  fsmo = find_mount_options_for_fs (fs_type);
 
722
 
 
723
  /* always prepend some reasonable default mount options; these are
 
724
   * chosen here; the user can override them if he wants to
 
725
   */
 
726
  options_to_use = prepend_default_mount_options (fsmo, caller_uid, options);
 
727
 
 
728
  /* validate mount options */
 
729
  str = g_string_new ("uhelper=udisks2,nodev,nosuid");
 
730
  for (n = 0; options_to_use[n] != NULL; n++)
 
731
    {
 
732
      const gchar *option = options_to_use[n];
 
733
 
 
734
      /* avoid attacks like passing "shortname=lower,uid=0" as a single mount option */
 
735
      if (strstr (option, ",") != NULL)
 
736
        {
 
737
          g_set_error (error,
 
738
                       UDISKS_ERROR,
 
739
                       UDISKS_ERROR_OPTION_NOT_PERMITTED,
 
740
                       "Malformed mount option `%s'",
 
741
                       option);
 
742
          g_string_free (str, TRUE);
 
743
          goto out;
 
744
        }
 
745
 
 
746
      /* first check if the mount option is allowed */
 
747
      if (!is_mount_option_allowed (fsmo, option, caller_uid))
 
748
        {
 
749
          g_set_error (error,
 
750
                       UDISKS_ERROR,
 
751
                       UDISKS_ERROR_OPTION_NOT_PERMITTED,
 
752
                       "Mount option `%s' is not allowed",
 
753
                       option);
 
754
          g_string_free (str, TRUE);
 
755
          goto out;
 
756
        }
 
757
 
 
758
      g_string_append_c (str, ',');
 
759
      g_string_append (str, option);
 
760
    }
 
761
  options_to_use_str = g_string_free (str, FALSE);
 
762
 
 
763
 out:
 
764
  g_strfreev (options_to_use);
 
765
 
 
766
  g_assert (options_to_use_str == NULL || g_utf8_validate (options_to_use_str, -1, NULL));
 
767
 
 
768
  return options_to_use_str;
 
769
}
 
770
 
 
771
/* ---------------------------------------------------------------------------------------------------- */
 
772
 
 
773
static gchar *
 
774
ensure_utf8 (const gchar *s)
 
775
{
 
776
  const gchar *end;
 
777
  gchar *ret;
 
778
 
 
779
  if (!g_utf8_validate (s, -1, &end))
 
780
    {
 
781
      gchar *tmp;
 
782
      gint pos;
 
783
      /* TODO: could possibly return a nicer UTF-8 string  */
 
784
      pos = (gint) (end - s);
 
785
      tmp = g_strndup (s, end - s);
 
786
      ret = g_strdup_printf ("%s (Invalid UTF-8 at byte %d)", tmp, pos);
 
787
      g_free (tmp);
 
788
    }
 
789
  else
 
790
    {
 
791
      ret = g_strdup (s);
 
792
    }
 
793
 
 
794
  return ret;
 
795
}
 
796
 
 
797
/* ---------------------------------------------------------------------------------------------------- */
 
798
 
 
799
static gboolean
 
800
add_acl (const gchar  *path,
 
801
         uid_t         uid,
 
802
         GError      **error)
 
803
{
 
804
  gboolean ret = FALSE;
 
805
  acl_t acl = NULL;
 
806
  acl_entry_t entry;
 
807
  acl_permset_t permset;
 
808
 
 
809
  acl = acl_get_file(path, ACL_TYPE_ACCESS);
 
810
  if (acl == NULL ||
 
811
      acl_create_entry (&acl, &entry) == -1 ||
 
812
      acl_set_tag_type (entry, ACL_USER) == -1 ||
 
813
      acl_set_qualifier (entry, &uid) == -1 ||
 
814
      acl_get_permset (entry, &permset) == -1 ||
 
815
      acl_add_perm (permset, ACL_READ|ACL_EXECUTE) == -1 ||
 
816
      acl_calc_mask (&acl) == -1 ||
 
817
      acl_set_file (path, ACL_TYPE_ACCESS, acl) == -1)
 
818
    {
 
819
      g_set_error (error,
 
820
                   G_IO_ERROR,
 
821
                   g_io_error_from_errno (errno),
 
822
                   "Adding read ACL for uid %d to `%s' failed: %m",
 
823
                   (gint) uid, path);
 
824
      goto out;
 
825
    }
 
826
 
 
827
  ret = TRUE;
 
828
 
 
829
 out:
 
830
  if (acl != NULL)
 
831
    acl_free (acl);
 
832
  return ret;
 
833
}
 
834
 
 
835
/*
 
836
 * calculate_mount_point: <internal>
 
837
 * @dameon: A #UDisksDaemon.
 
838
 * @block: A #UDisksBlock.
 
839
 * @uid: user id of the calling user
 
840
 * @gid: group id of the calling user
 
841
 * @user_name: user name of the calling user
 
842
 * @fs_type: The file system type to mount with
 
843
 * @error: Return location for error or %NULL.
 
844
 *
 
845
 * Calculates the mount point to use.
 
846
 *
 
847
 * Returns: A UTF-8 string with the mount point to use or %NULL if @error is set. Free with g_free().
 
848
 */
 
849
static gchar *
 
850
calculate_mount_point (UDisksDaemon              *daemon,
 
851
                       UDisksBlock               *block,
 
852
                       uid_t                      uid,
 
853
                       gid_t                      gid,
 
854
                       const gchar               *user_name,
 
855
                       const gchar               *fs_type,
 
856
                       GError                   **error)
 
857
{
 
858
  UDisksLinuxBlockObject *object = NULL;
 
859
  gboolean fs_shared = FALSE;
 
860
  const gchar *label = NULL;
 
861
  const gchar *uuid = NULL;
 
862
  gchar *escaped_user_name = NULL;
 
863
  gchar *mount_dir = NULL;
 
864
  gchar *mount_point = NULL;
 
865
  gchar *orig_mount_point;
 
866
  GString *str;
 
867
  gchar *s;
 
868
  guint n;
 
869
 
 
870
  label = NULL;
 
871
  uuid = NULL;
 
872
  if (block != NULL)
 
873
    {
 
874
      label = udisks_block_get_id_label (block);
 
875
      uuid = udisks_block_get_id_uuid (block);
 
876
    }
 
877
 
 
878
  object = udisks_daemon_util_dup_object (block, NULL);
 
879
  if (object != NULL)
 
880
    {
 
881
      UDisksLinuxDevice *device = udisks_linux_block_object_get_device (object);
 
882
      if (device != NULL)
 
883
        {
 
884
          if (device->udev_device != NULL)
 
885
            {
 
886
              /* TODO: maybe introduce Block:HintFilesystemShared instead of pulling it directly from the udev device */
 
887
              fs_shared = g_udev_device_get_property_as_boolean (device->udev_device, "UDISKS_FILESYSTEM_SHARED");
 
888
            }
 
889
          g_object_unref (device);
 
890
        }
 
891
    }
 
892
 
 
893
  /* If we know the user-name and it doesn't have any '/' character in
 
894
   * it, mount in /run/media/$USER
 
895
   */
 
896
  if (!fs_shared && (user_name != NULL && strstr (user_name, "/") == NULL))
 
897
    {
 
898
      mount_dir = g_strdup_printf ("/run/media/%s", user_name);
 
899
      if (!g_file_test (mount_dir, G_FILE_TEST_EXISTS))
 
900
        {
 
901
          /* First ensure that /run/media exists */
 
902
          if (!g_file_test ("/run/media", G_FILE_TEST_EXISTS))
 
903
            {
 
904
              if (g_mkdir ("/run/media", 0755) != 0)
 
905
                {
 
906
                  g_set_error (error,
 
907
                               UDISKS_ERROR,
 
908
                               UDISKS_ERROR_FAILED,
 
909
                               "Error creating directory /run/media: %m");
 
910
                  goto out;
 
911
                }
 
912
            }
 
913
          /* Then create the per-user /run/media/$USER */
 
914
          if (g_mkdir (mount_dir, 0700) != 0)
 
915
            {
 
916
              g_set_error (error,
 
917
                           UDISKS_ERROR,
 
918
                           UDISKS_ERROR_FAILED,
 
919
                           "Error creating directory `%s': %m",
 
920
                           mount_dir);
 
921
              goto out;
 
922
            }
 
923
          /* Finally, add the read+execute ACL for $USER */
 
924
          if (!add_acl (mount_dir, uid, error))
 
925
            {
 
926
              if (rmdir (mount_dir) != 0)
 
927
                udisks_warning ("Error calling rmdir() on %s: %m", mount_dir);
 
928
              goto out;
 
929
            }
 
930
        }
 
931
    }
 
932
  /* otherwise fall back to mounting in /media */
 
933
  if (mount_dir == NULL)
 
934
    mount_dir = g_strdup ("/media");
 
935
 
 
936
  /* NOTE: UTF-8 has the nice property that valid UTF-8 strings only contains
 
937
   *       the byte 0x2F if it's for the '/' character (U+002F SOLIDUS).
 
938
   *
 
939
   *       See http://en.wikipedia.org/wiki/UTF-8 for details.
 
940
   */
 
941
  if (label != NULL && strlen (label) > 0)
 
942
    {
 
943
      str = g_string_new (NULL);
 
944
      g_string_append_printf (str, "%s/", mount_dir);
 
945
      s = ensure_utf8 (label);
 
946
      for (n = 0; s[n] != '\0'; n++)
 
947
        {
 
948
          gint c = s[n];
 
949
          if (c == '/')
 
950
            g_string_append_c (str, '_');
 
951
          else
 
952
            g_string_append_c (str, c);
 
953
        }
 
954
      mount_point = g_string_free (str, FALSE);
 
955
      g_free (s);
 
956
    }
 
957
  else if (uuid != NULL && strlen (uuid) > 0)
 
958
    {
 
959
      str = g_string_new (NULL);
 
960
      g_string_append_printf (str, "%s/", mount_dir);
 
961
      s = ensure_utf8 (uuid);
 
962
      for (n = 0; s[n] != '\0'; n++)
 
963
        {
 
964
          gint c = s[n];
 
965
          if (c == '/')
 
966
            g_string_append_c (str, '_');
 
967
          else
 
968
            g_string_append_c (str, c);
 
969
        }
 
970
      mount_point = g_string_free (str, FALSE);
 
971
      g_free (s);
 
972
    }
 
973
  else
 
974
    {
 
975
      mount_point = g_strdup_printf ("%s/disk", mount_dir);
 
976
    }
 
977
 
 
978
  /* ... then uniqify the mount point */
 
979
  orig_mount_point = g_strdup (mount_point);
 
980
  n = 1;
 
981
  while (TRUE)
 
982
    {
 
983
      if (!g_file_test (mount_point, G_FILE_TEST_EXISTS))
 
984
        {
 
985
          break;
 
986
        }
 
987
      else
 
988
        {
 
989
          g_free (mount_point);
 
990
          mount_point = g_strdup_printf ("%s%d", orig_mount_point, n++);
 
991
        }
 
992
    }
 
993
  g_free (orig_mount_point);
 
994
  g_free (mount_dir);
 
995
 
 
996
 out:
 
997
  g_clear_object (&object);
 
998
  g_free (escaped_user_name);
 
999
  return mount_point;
 
1000
}
 
1001
 
 
1002
/* ---------------------------------------------------------------------------------------------------- */
 
1003
 
 
1004
static gboolean
 
1005
has_option (const gchar *options,
 
1006
            const gchar *option)
 
1007
{
 
1008
  gboolean ret = FALSE;
 
1009
  gchar **tokens;
 
1010
  guint n;
 
1011
 
 
1012
  tokens = g_strsplit (options, ",", -1);
 
1013
  for (n = 0; tokens != NULL && tokens[n] != NULL; n++)
 
1014
    {
 
1015
      if (g_strcmp0 (tokens[n], option) == 0)
 
1016
        {
 
1017
          ret = TRUE;
 
1018
          goto out;
 
1019
        }
 
1020
    }
 
1021
  g_strfreev (tokens);
 
1022
 
 
1023
 out:
 
1024
  return ret;
 
1025
}
 
1026
 
 
1027
static gboolean
 
1028
is_in_fstab (UDisksBlock        *block,
 
1029
             const gchar        *fstab_path,
 
1030
             gchar             **out_mount_point,
 
1031
             gchar             **out_mount_options)
 
1032
{
 
1033
  gboolean ret;
 
1034
  FILE *f;
 
1035
  char buf[8192];
 
1036
  struct mntent mbuf;
 
1037
  struct mntent *m;
 
1038
 
 
1039
  ret = FALSE;
 
1040
  f = fopen (fstab_path, "r");
 
1041
  if (f == NULL)
 
1042
    {
 
1043
      udisks_warning ("Error opening fstab file %s: %m", fstab_path);
 
1044
      goto out;
 
1045
    }
 
1046
 
 
1047
  while ((m = getmntent_r (f, &mbuf, buf, sizeof (buf))) != NULL && !ret)
 
1048
    {
 
1049
      gchar *device;
 
1050
      struct stat sb;
 
1051
 
 
1052
      device = NULL;
 
1053
      if (g_str_has_prefix (m->mnt_fsname, "UUID="))
 
1054
        {
 
1055
          device = g_strdup_printf ("/dev/disk/by-uuid/%s", m->mnt_fsname + 5);
 
1056
        }
 
1057
      else if (g_str_has_prefix (m->mnt_fsname, "LABEL="))
 
1058
        {
 
1059
          device = g_strdup_printf ("/dev/disk/by-label/%s", m->mnt_fsname + 6);
 
1060
        }
 
1061
      else if (g_str_has_prefix (m->mnt_fsname, "/dev"))
 
1062
        {
 
1063
          device = g_strdup (m->mnt_fsname);
 
1064
        }
 
1065
      else
 
1066
        {
 
1067
          /* ignore non-device entries */
 
1068
          goto continue_loop;
 
1069
        }
 
1070
 
 
1071
      if (stat (device, &sb) != 0)
 
1072
        {
 
1073
          udisks_debug ("Error statting %s (for entry %s): %m", device, m->mnt_fsname);
 
1074
          goto continue_loop;
 
1075
        }
 
1076
      if (!S_ISBLK (sb.st_mode))
 
1077
        {
 
1078
          udisks_debug ("Device %s (for entry %s) is not a block device", device, m->mnt_fsname);
 
1079
          goto continue_loop;
 
1080
        }
 
1081
 
 
1082
      /* udisks_debug ("device %d:%d for entry %s", major (sb.st_rdev), minor (sb.st_rdev), m->mnt_fsname); */
 
1083
 
 
1084
      if (udisks_block_get_device_number (block) == sb.st_rdev)
 
1085
        {
 
1086
          ret = TRUE;
 
1087
          if (out_mount_point != NULL)
 
1088
            *out_mount_point = g_strdup (m->mnt_dir);
 
1089
          if (out_mount_options != NULL)
 
1090
            *out_mount_options = g_strdup (m->mnt_opts);
 
1091
        }
 
1092
 
 
1093
    continue_loop:
 
1094
      g_free (device);
 
1095
    }
 
1096
 
 
1097
 out:
 
1098
  if (f != NULL)
 
1099
    fclose (f);
 
1100
  return ret;
 
1101
}
 
1102
 
 
1103
/* returns TRUE if, and only if, device is referenced in e.g. /etc/fstab
 
1104
 *
 
1105
 * TODO: check all files in /etc/fstab.d (it's a non-standard Linux extension)
 
1106
 * TODO: check if systemd has a specific "unit" for the device
 
1107
 */
 
1108
static gboolean
 
1109
is_system_managed (UDisksBlock        *block,
 
1110
                   gchar             **out_mount_point,
 
1111
                   gchar             **out_mount_options)
 
1112
{
 
1113
  gboolean ret;
 
1114
 
 
1115
  ret = TRUE;
 
1116
 
 
1117
  /* First, check /etc/fstab */
 
1118
  if (is_in_fstab (block, "/etc/fstab", out_mount_point, out_mount_options))
 
1119
    goto out;
 
1120
 
 
1121
  ret = FALSE;
 
1122
 
 
1123
 out:
 
1124
  return ret;
 
1125
}
 
1126
 
 
1127
/* ---------------------------------------------------------------------------------------------------- */
 
1128
 
 
1129
/* runs in thread dedicated to handling @invocation */
 
1130
static gboolean
 
1131
handle_mount (UDisksFilesystem       *filesystem,
 
1132
              GDBusMethodInvocation  *invocation,
 
1133
              GVariant               *options)
 
1134
{
 
1135
  UDisksObject *object;
 
1136
  UDisksBlock *block;
 
1137
  UDisksDaemon *daemon;
 
1138
  UDisksState *state;
 
1139
  uid_t caller_uid;
 
1140
  gid_t caller_gid;
 
1141
  pid_t caller_pid;
 
1142
  const gchar * const *existing_mount_points;
 
1143
  const gchar *probed_fs_usage;
 
1144
  gchar *fs_type_to_use;
 
1145
  gchar *mount_options_to_use;
 
1146
  gchar *mount_point_to_use;
 
1147
  gchar *fstab_mount_options;
 
1148
  gchar *escaped_fs_type_to_use;
 
1149
  gchar *escaped_mount_options_to_use;
 
1150
  gchar *escaped_mount_point_to_use;
 
1151
  gchar *error_message;
 
1152
  gchar *caller_user_name;
 
1153
  GError *error;
 
1154
  const gchar *action_id;
 
1155
  const gchar *message;
 
1156
  gboolean system_managed;
 
1157
  gchar *escaped_device = NULL;
 
1158
 
 
1159
  object = NULL;
 
1160
  error_message = NULL;
 
1161
  fs_type_to_use = NULL;
 
1162
  mount_options_to_use = NULL;
 
1163
  mount_point_to_use = NULL;
 
1164
  fstab_mount_options = NULL;
 
1165
  escaped_fs_type_to_use = NULL;
 
1166
  escaped_mount_options_to_use = NULL;
 
1167
  escaped_mount_point_to_use = NULL;
 
1168
  caller_user_name = NULL;
 
1169
  system_managed = FALSE;
 
1170
 
 
1171
  /* only allow a single call at a time */
 
1172
  g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
 
1173
 
 
1174
  error = NULL;
 
1175
  object = udisks_daemon_util_dup_object (filesystem, &error);
 
1176
  if (object == NULL)
 
1177
    {
 
1178
      g_dbus_method_invocation_take_error (invocation, error);
 
1179
      goto out;
 
1180
    }
 
1181
 
 
1182
  block = udisks_object_peek_block (object);
 
1183
  daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
 
1184
  state = udisks_daemon_get_state (daemon);
 
1185
 
 
1186
  /* check if mount point is managed by e.g. /etc/fstab or similar */
 
1187
  if (is_system_managed (block, &mount_point_to_use, &fstab_mount_options))
 
1188
    {
 
1189
      system_managed = TRUE;
 
1190
    }
 
1191
 
 
1192
  /* First, fail if the device is already mounted */
 
1193
  existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
 
1194
  if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
 
1195
    {
 
1196
      GString *str;
 
1197
      guint n;
 
1198
      str = g_string_new (NULL);
 
1199
      for (n = 0; existing_mount_points[n] != NULL; n++)
 
1200
        {
 
1201
          if (n > 0)
 
1202
            g_string_append (str, ", ");
 
1203
          g_string_append_printf (str, "`%s'", existing_mount_points[n]);
 
1204
        }
 
1205
      g_dbus_method_invocation_return_error (invocation,
 
1206
                                             UDISKS_ERROR,
 
1207
                                             UDISKS_ERROR_ALREADY_MOUNTED,
 
1208
                                             "Device %s is already mounted at %s.\n",
 
1209
                                             udisks_block_get_device (block),
 
1210
                                             str->str);
 
1211
      g_string_free (str, TRUE);
 
1212
      goto out;
 
1213
    }
 
1214
 
 
1215
  error = NULL;
 
1216
  if (!udisks_daemon_util_get_caller_uid_sync (daemon,
 
1217
                                               invocation,
 
1218
                                               NULL /* GCancellable */,
 
1219
                                               &caller_uid,
 
1220
                                               &caller_gid,
 
1221
                                               &caller_user_name,
 
1222
                                               &error))
 
1223
    {
 
1224
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1225
      g_error_free (error);
 
1226
      goto out;
 
1227
    }
 
1228
 
 
1229
  error = NULL;
 
1230
  if (!udisks_daemon_util_get_caller_pid_sync (daemon,
 
1231
                                               invocation,
 
1232
                                               NULL /* GCancellable */,
 
1233
                                               &caller_pid,
 
1234
                                               &error))
 
1235
    {
 
1236
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1237
      g_error_free (error);
 
1238
      goto out;
 
1239
    }
 
1240
 
 
1241
  if (system_managed)
 
1242
    {
 
1243
      gint status;
 
1244
      gboolean mount_fstab_as_root = FALSE;
 
1245
 
 
1246
      if (!has_option (fstab_mount_options, "x-udisks-auth"))
 
1247
        {
 
1248
          action_id = "org.freedesktop.udisks2.filesystem-mount";
 
1249
          /* Translators: Shown in authentication dialog when the user
 
1250
           * requests mounting a filesystem.
 
1251
           *
 
1252
           * Do not translate $(drive), it's a placeholder and
 
1253
           * will be replaced by the name of the drive/device in question
 
1254
           */
 
1255
          message = N_("Authentication is required to mount $(drive)");
 
1256
          if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
 
1257
            {
 
1258
              if (udisks_block_get_hint_system (block))
 
1259
                {
 
1260
                  action_id = "org.freedesktop.udisks2.filesystem-mount-system";
 
1261
                }
 
1262
              else if (!udisks_daemon_util_on_same_seat (daemon, object, caller_pid))
 
1263
                {
 
1264
                  action_id = "org.freedesktop.udisks2.filesystem-mount-other-seat";
 
1265
                }
 
1266
            }
 
1267
 
 
1268
          if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1269
                                                            object,
 
1270
                                                            action_id,
 
1271
                                                            options,
 
1272
                                                            message,
 
1273
                                                            invocation))
 
1274
            goto out;
 
1275
          mount_fstab_as_root = TRUE;
 
1276
        }
 
1277
 
 
1278
      if (!g_file_test (mount_point_to_use, G_FILE_TEST_IS_DIR))
 
1279
        {
 
1280
          if (g_mkdir_with_parents (mount_point_to_use, 0755) != 0)
 
1281
            {
 
1282
              g_dbus_method_invocation_return_error (invocation,
 
1283
                                                     UDISKS_ERROR,
 
1284
                                                     UDISKS_ERROR_FAILED,
 
1285
                                                     "Error creating directory `%s' to be used for mounting %s: %m",
 
1286
                                                     mount_point_to_use,
 
1287
                                                     udisks_block_get_device (block));
 
1288
              goto out;
 
1289
            }
 
1290
        }
 
1291
 
 
1292
      escaped_mount_point_to_use   = udisks_daemon_util_escape_and_quote (mount_point_to_use);
 
1293
    mount_fstab_again:
 
1294
      if (!udisks_daemon_launch_spawned_job_sync (daemon,
 
1295
                                                  object,
 
1296
                                                  "filesystem-mount", caller_uid,
 
1297
                                                  NULL,  /* GCancellable */
 
1298
                                                  mount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_uid */
 
1299
                                                  mount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_euid */
 
1300
                                                  &status,
 
1301
                                                  &error_message,
 
1302
                                                  NULL,  /* input_string */
 
1303
                                                  "mount %s",
 
1304
                                                  escaped_mount_point_to_use))
 
1305
        {
 
1306
          /* mount(8) exits with status 1 on "incorrect invocation or permissions" - if this is
 
1307
           * is so, try as as root */
 
1308
          if (!mount_fstab_as_root && WIFEXITED (status) && WEXITSTATUS (status) == 1)
 
1309
            {
 
1310
              if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1311
                                                                object,
 
1312
                                                                "org.freedesktop.udisks2.filesystem-fstab",
 
1313
                                                                options,
 
1314
                                                                /* Translators: Shown in authentication dialog when the
 
1315
                                                                 * user requests mounting a filesystem that is in
 
1316
                                                                 * /etc/fstab file with the x-udisks-auth option.
 
1317
                                                                 *
 
1318
                                                                 * Do not translate $(drive), it's a
 
1319
                                                                 * placeholder and will be replaced by the name of
 
1320
                                                                 * the drive/device in question
 
1321
                                                                 *
 
1322
                                                                 * Do not translate /etc/fstab
 
1323
                                                                 */
 
1324
                                                                N_("Authentication is required to mount $(drive) referenced in the /etc/fstab file"),
 
1325
                                                                invocation))
 
1326
                goto out;
 
1327
              mount_fstab_as_root = TRUE;
 
1328
              goto mount_fstab_again;
 
1329
            }
 
1330
 
 
1331
          g_dbus_method_invocation_return_error (invocation,
 
1332
                                                 UDISKS_ERROR,
 
1333
                                                 UDISKS_ERROR_FAILED,
 
1334
                                                 "Error mounting system-managed device %s: %s",
 
1335
                                                 udisks_block_get_device (block),
 
1336
                                                 error_message);
 
1337
          goto out;
 
1338
        }
 
1339
      udisks_notice ("Mounted %s (system) at %s on behalf of uid %d",
 
1340
                     udisks_block_get_device (block),
 
1341
                     mount_point_to_use,
 
1342
                     caller_uid);
 
1343
 
 
1344
      /* update the mounted-fs file */
 
1345
      udisks_state_add_mounted_fs (state,
 
1346
                                   mount_point_to_use,
 
1347
                                   udisks_block_get_device_number (block),
 
1348
                                   caller_uid,
 
1349
                                   TRUE); /* fstab_mounted */
 
1350
 
 
1351
      udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use);
 
1352
      goto out;
 
1353
    }
 
1354
 
 
1355
  /* Then fail if the device is not mountable - we actually allow mounting
 
1356
   * devices that are not probed since since it could be that we just
 
1357
   * don't have the data in the udev database but the device has a
 
1358
   * filesystem *anyway*...
 
1359
   *
 
1360
   * For example, this applies to PC floppy devices - automatically
 
1361
   * probing for media them creates annoying noise. So they won't
 
1362
   * appear in the udev database.
 
1363
   */
 
1364
  probed_fs_usage = NULL;
 
1365
  if (block != NULL)
 
1366
    probed_fs_usage = udisks_block_get_id_usage (block);
 
1367
  if (probed_fs_usage != NULL && strlen (probed_fs_usage) > 0 &&
 
1368
      g_strcmp0 (probed_fs_usage, "filesystem") != 0)
 
1369
    {
 
1370
      g_dbus_method_invocation_return_error (invocation,
 
1371
                                             UDISKS_ERROR,
 
1372
                                             UDISKS_ERROR_FAILED,
 
1373
                                             "Cannot mount block device %s with probed usage `%s' - expected `filesystem'",
 
1374
                                             udisks_block_get_device (block),
 
1375
                                             probed_fs_usage);
 
1376
      goto out;
 
1377
    }
 
1378
 
 
1379
  /* calculate filesystem type (guaranteed to be valid UTF-8) */
 
1380
  error = NULL;
 
1381
  fs_type_to_use = calculate_fs_type (block,
 
1382
                                      options,
 
1383
                                      &error);
 
1384
  if (fs_type_to_use == NULL)
 
1385
    {
 
1386
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1387
      g_error_free (error);
 
1388
      goto out;
 
1389
    }
 
1390
 
 
1391
  /* calculate mount options (guaranteed to be valid UTF-8) */
 
1392
  error = NULL;
 
1393
  mount_options_to_use = calculate_mount_options (daemon,
 
1394
                                                  block,
 
1395
                                                  caller_uid,
 
1396
                                                  fs_type_to_use,
 
1397
                                                  options,
 
1398
                                                  &error);
 
1399
  if (mount_options_to_use == NULL)
 
1400
    {
 
1401
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1402
      g_error_free (error);
 
1403
      goto out;
 
1404
    }
 
1405
 
 
1406
  /* Now, check that the user is actually authorized to mount the
 
1407
   * device. Need to do this before calculating a mount point since we
 
1408
   * may be racing with other threads...
 
1409
   */
 
1410
  action_id = "org.freedesktop.udisks2.filesystem-mount";
 
1411
  /* Translators: Shown in authentication dialog when the user
 
1412
   * requests mounting a filesystem.
 
1413
   *
 
1414
   * Do not translate $(drive), it's a placeholder and
 
1415
   * will be replaced by the name of the drive/device in question
 
1416
   */
 
1417
  message = N_("Authentication is required to mount $(drive)");
 
1418
  if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
 
1419
    {
 
1420
      if (udisks_block_get_hint_system (block))
 
1421
        {
 
1422
          action_id = "org.freedesktop.udisks2.filesystem-mount-system";
 
1423
        }
 
1424
      else if (!udisks_daemon_util_on_same_seat (daemon, object, caller_pid))
 
1425
        {
 
1426
          action_id = "org.freedesktop.udisks2.filesystem-mount-other-seat";
 
1427
        }
 
1428
    }
 
1429
 
 
1430
  if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1431
                                                    object,
 
1432
                                                    action_id,
 
1433
                                                    options,
 
1434
                                                    message,
 
1435
                                                    invocation))
 
1436
    goto out;
 
1437
 
 
1438
  /* calculate mount point (guaranteed to be valid UTF-8) */
 
1439
  error = NULL;
 
1440
  mount_point_to_use = calculate_mount_point (daemon,
 
1441
                                              block,
 
1442
                                              caller_uid,
 
1443
                                              caller_gid,
 
1444
                                              caller_user_name,
 
1445
                                              fs_type_to_use,
 
1446
                                              &error);
 
1447
  if (mount_point_to_use == NULL)
 
1448
    {
 
1449
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1450
      g_error_free (error);
 
1451
      goto out;
 
1452
    }
 
1453
 
 
1454
  /* create the mount point */
 
1455
  if (g_mkdir (mount_point_to_use, 0700) != 0)
 
1456
    {
 
1457
      g_dbus_method_invocation_return_error (invocation,
 
1458
                                             UDISKS_ERROR,
 
1459
                                             UDISKS_ERROR_FAILED,
 
1460
                                             "Error creating mount point `%s': %m",
 
1461
                                             mount_point_to_use);
 
1462
      goto out;
 
1463
    }
 
1464
 
 
1465
  escaped_fs_type_to_use       = udisks_daemon_util_escape_and_quote (fs_type_to_use);
 
1466
  escaped_mount_options_to_use = udisks_daemon_util_escape_and_quote (mount_options_to_use);
 
1467
  escaped_mount_point_to_use   = udisks_daemon_util_escape_and_quote (mount_point_to_use);
 
1468
  escaped_device               = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block));
 
1469
 
 
1470
  /* run mount(8) */
 
1471
  if (!udisks_daemon_launch_spawned_job_sync (daemon,
 
1472
                                              object,
 
1473
                                              "filesystem-mount", caller_uid,
 
1474
                                              NULL, /* GCancellable */
 
1475
                                              0,    /* uid_t run_as_uid */
 
1476
                                              0,    /* uid_t run_as_euid */
 
1477
                                              NULL, /* gint *out_status */
 
1478
                                              &error_message,
 
1479
                                              NULL,  /* input_string */
 
1480
                                              "mount -t %s -o %s %s %s",
 
1481
                                              escaped_fs_type_to_use,
 
1482
                                              escaped_mount_options_to_use,
 
1483
                                              escaped_device,
 
1484
                                              escaped_mount_point_to_use))
 
1485
    {
 
1486
      /* ugh, something went wrong.. we need to clean up the created mount point */
 
1487
      if (g_rmdir (mount_point_to_use) != 0)
 
1488
        udisks_warning ("Error removing directory %s: %m", mount_point_to_use);
 
1489
      g_dbus_method_invocation_return_error (invocation,
 
1490
                                             UDISKS_ERROR,
 
1491
                                             UDISKS_ERROR_FAILED,
 
1492
                                             "Error mounting %s at %s: %s",
 
1493
                                             udisks_block_get_device (block),
 
1494
                                             mount_point_to_use,
 
1495
                                             error_message);
 
1496
      goto out;
 
1497
    }
 
1498
 
 
1499
  /* update the mounted-fs file */
 
1500
  udisks_state_add_mounted_fs (state,
 
1501
                               mount_point_to_use,
 
1502
                               udisks_block_get_device_number (block),
 
1503
                               caller_uid,
 
1504
                               FALSE); /* fstab_mounted */
 
1505
 
 
1506
  udisks_notice ("Mounted %s at %s on behalf of uid %d",
 
1507
                 udisks_block_get_device (block),
 
1508
                 mount_point_to_use,
 
1509
                 caller_uid);
 
1510
 
 
1511
  udisks_filesystem_complete_mount (filesystem, invocation, mount_point_to_use);
 
1512
 
 
1513
 out:
 
1514
  g_free (escaped_device);
 
1515
  g_free (error_message);
 
1516
  g_free (escaped_fs_type_to_use);
 
1517
  g_free (escaped_mount_options_to_use);
 
1518
  g_free (escaped_mount_point_to_use);
 
1519
  g_free (fs_type_to_use);
 
1520
  g_free (mount_options_to_use);
 
1521
  g_free (mount_point_to_use);
 
1522
  g_free (fstab_mount_options);
 
1523
  g_free (caller_user_name);
 
1524
  g_clear_object (&object);
 
1525
 
 
1526
  /* only allow a single call at a time */
 
1527
  g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
 
1528
 
 
1529
  return TRUE; /* returning TRUE means that we handled the method invocation */
 
1530
}
 
1531
 
 
1532
/* ---------------------------------------------------------------------------------------------------- */
 
1533
 
 
1534
static guint
 
1535
get_error_code_for_umount (gint         exit_status,
 
1536
                           const gchar *error_message)
 
1537
{
 
1538
  if (strstr (error_message, "device is busy") != NULL ||
 
1539
      strstr (error_message, "target is busy") != NULL)
 
1540
    return UDISKS_ERROR_DEVICE_BUSY;
 
1541
  else
 
1542
    return UDISKS_ERROR_FAILED;
 
1543
}
 
1544
 
 
1545
/* runs in thread dedicated to handling @invocation */
 
1546
static gboolean
 
1547
handle_unmount (UDisksFilesystem       *filesystem,
 
1548
                GDBusMethodInvocation  *invocation,
 
1549
                GVariant               *options)
 
1550
{
 
1551
  UDisksObject *object;
 
1552
  UDisksBlock *block;
 
1553
  UDisksDaemon *daemon;
 
1554
  UDisksState *state;
 
1555
  gchar *mount_point;
 
1556
  gchar *fstab_mount_options;
 
1557
  gchar *escaped_mount_point;
 
1558
  GError *error;
 
1559
  uid_t mounted_by_uid;
 
1560
  uid_t caller_uid;
 
1561
  gint status;
 
1562
  gchar *error_message;
 
1563
  const gchar *const *mount_points;
 
1564
  gboolean opt_force;
 
1565
  gboolean rc;
 
1566
  gboolean system_managed;
 
1567
  gboolean fstab_mounted;
 
1568
  gchar *escaped_device = NULL;
 
1569
 
 
1570
  mount_point = NULL;
 
1571
  fstab_mount_options = NULL;
 
1572
  escaped_mount_point = NULL;
 
1573
  error_message = NULL;
 
1574
  opt_force = FALSE;
 
1575
 
 
1576
  /* only allow a single call at a time */
 
1577
  g_mutex_lock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
 
1578
 
 
1579
  error = NULL;
 
1580
  object = udisks_daemon_util_dup_object (filesystem, &error);
 
1581
  if (object == NULL)
 
1582
    {
 
1583
      g_dbus_method_invocation_take_error (invocation, error);
 
1584
      goto out;
 
1585
    }
 
1586
 
 
1587
  block = udisks_object_peek_block (object);
 
1588
  daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
 
1589
  state = udisks_daemon_get_state (daemon);
 
1590
  system_managed = FALSE;
 
1591
 
 
1592
  if (options != NULL)
 
1593
    {
 
1594
      g_variant_lookup (options,
 
1595
                        "force",
 
1596
                        "b",
 
1597
                        &opt_force);
 
1598
    }
 
1599
 
 
1600
  mount_points = udisks_filesystem_get_mount_points (filesystem);
 
1601
  if (mount_points == NULL || g_strv_length ((gchar **) mount_points) == 0)
 
1602
    {
 
1603
      g_dbus_method_invocation_return_error (invocation,
 
1604
                                             UDISKS_ERROR,
 
1605
                                             UDISKS_ERROR_NOT_MOUNTED,
 
1606
                                             "Device `%s' is not mounted",
 
1607
                                             udisks_block_get_device (block));
 
1608
      goto out;
 
1609
    }
 
1610
 
 
1611
  error = NULL;
 
1612
  if (!udisks_daemon_util_get_caller_uid_sync (daemon, invocation, NULL, &caller_uid, NULL, NULL, &error))
 
1613
    {
 
1614
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1615
      g_error_free (error);
 
1616
      goto out;
 
1617
    }
 
1618
 
 
1619
  /* check if mount point is managed by e.g. /etc/fstab or similar */
 
1620
  if (is_system_managed (block, &mount_point, &fstab_mount_options))
 
1621
    {
 
1622
      system_managed = TRUE;
 
1623
    }
 
1624
 
 
1625
  /* if system-managed (e.g. referenced in /etc/fstab or similar) and
 
1626
   * with the option x-udisks-auth, just run umount(8) as the
 
1627
   * calling user
 
1628
   */
 
1629
  if (system_managed && has_option (fstab_mount_options, "x-udisks-auth"))
 
1630
    {
 
1631
      gboolean unmount_fstab_as_root;
 
1632
 
 
1633
      unmount_fstab_as_root = FALSE;
 
1634
    unmount_fstab_again:
 
1635
      escaped_mount_point = udisks_daemon_util_escape_and_quote (mount_point);
 
1636
      /* right now -l is the only way to "force unmount" file systems... */
 
1637
      if (!udisks_daemon_launch_spawned_job_sync (daemon,
 
1638
                                                  object,
 
1639
                                                  "filesystem-unmount", caller_uid,
 
1640
                                                  NULL, /* GCancellable */
 
1641
                                                  unmount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_uid */
 
1642
                                                  unmount_fstab_as_root ? 0 : caller_uid, /* uid_t run_as_euid */
 
1643
                                                  &status,
 
1644
                                                  &error_message,
 
1645
                                                  NULL,  /* input_string */
 
1646
                                                  "umount %s %s",
 
1647
                                                  opt_force ? "-l" : "",
 
1648
                                                  escaped_mount_point))
 
1649
        {
 
1650
          /* umount(8) does not (yet) have a specific exits status for
 
1651
           * "insufficient permissions" so just try again as root
 
1652
           *
 
1653
           * TODO: file bug asking for such an exit status
 
1654
           */
 
1655
          if (!unmount_fstab_as_root && WIFEXITED (status) && WEXITSTATUS (status) != 0)
 
1656
            {
 
1657
              if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1658
                                                                object,
 
1659
                                                                "org.freedesktop.udisks2.filesystem-fstab",
 
1660
                                                                options,
 
1661
                                                                /* Translators: Shown in authentication dialog when the
 
1662
                                                                 * user requests unmounting a filesystem that is in
 
1663
                                                                 * /etc/fstab file with the x-udisks-auth option.
 
1664
                                                                 *
 
1665
                                                                 * Do not translate $(drive), it's a
 
1666
                                                                 * placeholder and will be replaced by the name of
 
1667
                                                                 * the drive/device in question
 
1668
                                                                 *
 
1669
                                                                 * Do not translate /etc/fstab
 
1670
                                                                 */
 
1671
                                                                N_("Authentication is required to unmount $(drive) referenced in the /etc/fstab file"),
 
1672
                                                                invocation))
 
1673
                goto out;
 
1674
              unmount_fstab_as_root = TRUE;
 
1675
              goto unmount_fstab_again;
 
1676
            }
 
1677
 
 
1678
          g_dbus_method_invocation_return_error (invocation,
 
1679
                                                 UDISKS_ERROR,
 
1680
                                                 get_error_code_for_umount (status, error_message),
 
1681
                                                 "Error unmounting system-managed device %s: %s",
 
1682
                                                 udisks_block_get_device (block),
 
1683
                                                 error_message);
 
1684
          goto out;
 
1685
        }
 
1686
      udisks_notice ("Unmounted %s (system) from %s on behalf of uid %d",
 
1687
                     udisks_block_get_device (block),
 
1688
                     mount_point,
 
1689
                     caller_uid);
 
1690
      udisks_filesystem_complete_unmount (filesystem, invocation);
 
1691
      goto out;
 
1692
    }
 
1693
 
 
1694
  error = NULL;
 
1695
  mount_point = udisks_state_find_mounted_fs (state,
 
1696
                                              udisks_block_get_device_number (block),
 
1697
                                              &mounted_by_uid,
 
1698
                                              &fstab_mounted);
 
1699
  if (mount_point == NULL)
 
1700
    {
 
1701
      /* allow unmounting stuff not mentioned in mounted-fs, but treat it like root mounted it */
 
1702
      mounted_by_uid = 0;
 
1703
    }
 
1704
 
 
1705
  if (caller_uid != 0 && (caller_uid != mounted_by_uid))
 
1706
    {
 
1707
      const gchar *action_id;
 
1708
      const gchar *message;
 
1709
 
 
1710
      action_id = "org.freedesktop.udisks2.filesystem-unmount-others";
 
1711
      /* Translators: Shown in authentication dialog when the user
 
1712
       * requests unmounting a filesystem previously mounted by
 
1713
       * another user.
 
1714
       *
 
1715
       * Do not translate $(drive), it's a placeholder and
 
1716
       * will be replaced by the name of the drive/device in question
 
1717
       */
 
1718
      message = N_("Authentication is required to unmount $(drive) mounted by another user");
 
1719
 
 
1720
      if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1721
                                                        object,
 
1722
                                                        action_id,
 
1723
                                                        options,
 
1724
                                                        message,
 
1725
                                                        invocation))
 
1726
        goto out;
 
1727
    }
 
1728
 
 
1729
  escaped_device = udisks_daemon_util_escape_and_quote (udisks_block_get_device (block));
 
1730
 
 
1731
  /* otherwise go ahead and unmount the filesystem */
 
1732
  if (mount_point != NULL)
 
1733
    {
 
1734
      escaped_mount_point = udisks_daemon_util_escape_and_quote (mount_point);
 
1735
      rc = udisks_daemon_launch_spawned_job_sync (daemon,
 
1736
                                                  object,
 
1737
                                                  "filesystem-unmount", caller_uid,
 
1738
                                                  NULL, /* GCancellable */
 
1739
                                                  0,    /* uid_t run_as_uid */
 
1740
                                                  0,    /* uid_t run_as_euid */
 
1741
                                                  NULL, /* gint *out_status */
 
1742
                                                  &error_message,
 
1743
                                                  NULL,  /* input_string */
 
1744
                                                  "umount %s %s",
 
1745
                                                  opt_force ? "-l" : "",
 
1746
                                                  escaped_mount_point);
 
1747
    }
 
1748
  else
 
1749
    {
 
1750
      /* mount_point == NULL */
 
1751
      rc = udisks_daemon_launch_spawned_job_sync (daemon,
 
1752
                                                  object,
 
1753
                                                  "filesystem-unmount", caller_uid,
 
1754
                                                  NULL, /* GCancellable */
 
1755
                                                  0,    /* uid_t run_as_uid */
 
1756
                                                  0,    /* uid_t run_as_euid */
 
1757
                                                  &status,
 
1758
                                                  &error_message,
 
1759
                                                  NULL,  /* input_string */
 
1760
                                                  "umount %s %s",
 
1761
                                                  opt_force ? "-l" : "",
 
1762
                                                  escaped_device);
 
1763
    }
 
1764
 
 
1765
  if (!rc)
 
1766
    {
 
1767
      g_dbus_method_invocation_return_error (invocation,
 
1768
                                             UDISKS_ERROR,
 
1769
                                             get_error_code_for_umount (status, error_message),
 
1770
                                             "Error unmounting %s: %s",
 
1771
                                             udisks_block_get_device (block),
 
1772
                                             error_message);
 
1773
      goto out;
 
1774
    }
 
1775
 
 
1776
  /* OK, filesystem unmounted.. the state/cleanup routines will remove the mountpoint for us */
 
1777
 
 
1778
  udisks_notice ("Unmounted %s on behalf of uid %d",
 
1779
                 udisks_block_get_device (block),
 
1780
                 caller_uid);
 
1781
 
 
1782
  udisks_filesystem_complete_unmount (filesystem, invocation);
 
1783
 
 
1784
 out:
 
1785
  g_free (escaped_device);
 
1786
  g_free (error_message);
 
1787
  g_free (escaped_mount_point);
 
1788
  g_free (mount_point);
 
1789
  g_free (fstab_mount_options);
 
1790
  g_clear_object (&object);
 
1791
 
 
1792
  g_mutex_unlock (&UDISKS_LINUX_FILESYSTEM (filesystem)->lock);
 
1793
 
 
1794
  return TRUE;
 
1795
}
 
1796
 
 
1797
/* ---------------------------------------------------------------------------------------------------- */
 
1798
 
 
1799
static void
 
1800
on_set_label_job_completed (UDisksJob   *job,
 
1801
                            gboolean     success,
 
1802
                            const gchar *message,
 
1803
                            gpointer     user_data)
 
1804
{
 
1805
  GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
 
1806
  UDisksFilesystem *filesystem;
 
1807
 
 
1808
  filesystem = UDISKS_FILESYSTEM (g_dbus_method_invocation_get_user_data (invocation));
 
1809
 
 
1810
  if (success)
 
1811
    udisks_filesystem_complete_set_label (filesystem, invocation);
 
1812
  else
 
1813
    g_dbus_method_invocation_return_error (invocation,
 
1814
                                           UDISKS_ERROR,
 
1815
                                           UDISKS_ERROR_FAILED,
 
1816
                                           "Error setting label: %s",
 
1817
                                           message);
 
1818
}
 
1819
 
 
1820
/* runs in thread dedicated to handling method call */
 
1821
static gboolean
 
1822
handle_set_label (UDisksFilesystem       *filesystem,
 
1823
                  GDBusMethodInvocation  *invocation,
 
1824
                  const gchar            *label,
 
1825
                  GVariant               *options)
 
1826
{
 
1827
  UDisksBlock *block;
 
1828
  UDisksObject *object;
 
1829
  UDisksDaemon *daemon;
 
1830
  const gchar *probed_fs_usage;
 
1831
  const gchar *probed_fs_type;
 
1832
  const FSInfo *fs_info;
 
1833
  UDisksBaseJob *job;
 
1834
  const gchar *action_id;
 
1835
  const gchar *message;
 
1836
  gchar *real_label = NULL;
 
1837
  uid_t caller_uid;
 
1838
  gid_t caller_gid;
 
1839
  pid_t caller_pid;
 
1840
  gchar *command;
 
1841
  gchar *tmp;
 
1842
  GError *error;
 
1843
 
 
1844
  object = NULL;
 
1845
  daemon = NULL;
 
1846
  command = NULL;
 
1847
 
 
1848
  error = NULL;
 
1849
  object = udisks_daemon_util_dup_object (filesystem, &error);
 
1850
  if (object == NULL)
 
1851
    {
 
1852
      g_dbus_method_invocation_take_error (invocation, error);
 
1853
      goto out;
 
1854
    }
 
1855
 
 
1856
  daemon = udisks_linux_block_object_get_daemon (UDISKS_LINUX_BLOCK_OBJECT (object));
 
1857
  block = udisks_object_peek_block (object);
 
1858
 
 
1859
  error = NULL;
 
1860
  if (!udisks_daemon_util_get_caller_pid_sync (daemon,
 
1861
                                               invocation,
 
1862
                                               NULL /* GCancellable */,
 
1863
                                               &caller_pid,
 
1864
                                               &error))
 
1865
    {
 
1866
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1867
      g_error_free (error);
 
1868
      goto out;
 
1869
    }
 
1870
 
 
1871
  error = NULL;
 
1872
  if (!udisks_daemon_util_get_caller_uid_sync (daemon,
 
1873
                                               invocation,
 
1874
                                               NULL /* GCancellable */,
 
1875
                                               &caller_uid,
 
1876
                                               &caller_gid,
 
1877
                                               NULL,
 
1878
                                               &error))
 
1879
    {
 
1880
      g_dbus_method_invocation_return_gerror (invocation, error);
 
1881
      g_error_free (error);
 
1882
      goto out;
 
1883
    }
 
1884
 
 
1885
  probed_fs_usage = udisks_block_get_id_usage (block);
 
1886
  probed_fs_type = udisks_block_get_id_type (block);
 
1887
 
 
1888
  if (g_strcmp0 (probed_fs_usage, "filesystem") != 0)
 
1889
    {
 
1890
      g_dbus_method_invocation_return_error (invocation,
 
1891
                                             UDISKS_ERROR,
 
1892
                                             UDISKS_ERROR_NOT_SUPPORTED,
 
1893
                                             "Cannot change label on device of type %s",
 
1894
                                             probed_fs_usage);
 
1895
      goto out;
 
1896
    }
 
1897
 
 
1898
  fs_info = get_fs_info (probed_fs_type);
 
1899
 
 
1900
  if (fs_info == NULL || fs_info->command_change_label == NULL)
 
1901
    {
 
1902
      g_dbus_method_invocation_return_error (invocation,
 
1903
                                             UDISKS_ERROR,
 
1904
                                             UDISKS_ERROR_NOT_SUPPORTED,
 
1905
                                             "Don't know how to change label on device of type %s:%s",
 
1906
                                             probed_fs_usage,
 
1907
                                             probed_fs_type);
 
1908
      goto out;
 
1909
    }
 
1910
 
 
1911
  /* VFAT does not allow some characters; as dosfslabel does not enforce this,
 
1912
   * check in advance; also, VFAT only knows upper-case characters, dosfslabel
 
1913
   * enforces this */
 
1914
  if (g_strcmp0 (probed_fs_type, "vfat") == 0)
 
1915
    {
 
1916
      const gchar *forbidden = "\"*/:<>?\\|";
 
1917
      guint n;
 
1918
      for (n = 0; forbidden[n] != 0; n++)
 
1919
        {
 
1920
          if (strchr (label, forbidden[n]) != NULL)
 
1921
            {
 
1922
              g_dbus_method_invocation_return_error (invocation,
 
1923
                                                     UDISKS_ERROR,
 
1924
                                                     UDISKS_ERROR_NOT_SUPPORTED,
 
1925
                                                     "character '%c' not supported in VFAT labels",
 
1926
                                                     forbidden[n]);
 
1927
               goto out;
 
1928
            }
 
1929
        }
 
1930
 
 
1931
      /* we need to remember that we make a copy, so assign it to a new
 
1932
       * variable, too */
 
1933
      real_label = g_ascii_strup (label, -1);
 
1934
      label = real_label;
 
1935
    }
 
1936
 
 
1937
  /* Fail if the device is already mounted and the tools/drivers doesn't
 
1938
   * support changing the label in that case
 
1939
   */
 
1940
  if (filesystem != NULL && !fs_info->supports_online_label_rename)
 
1941
    {
 
1942
      const gchar * const *existing_mount_points;
 
1943
      existing_mount_points = udisks_filesystem_get_mount_points (filesystem);
 
1944
      if (existing_mount_points != NULL && g_strv_length ((gchar **) existing_mount_points) > 0)
 
1945
        {
 
1946
          g_dbus_method_invocation_return_error (invocation,
 
1947
                                                 UDISKS_ERROR,
 
1948
                                                 UDISKS_ERROR_NOT_SUPPORTED,
 
1949
                                                 "Cannot change label on mounted device of type %s:%s.\n",
 
1950
                                                 probed_fs_usage,
 
1951
                                                 probed_fs_type);
 
1952
          goto out;
 
1953
        }
 
1954
    }
 
1955
 
 
1956
  action_id = "org.freedesktop.udisks2.modify-device";
 
1957
  /* Translators: Shown in authentication dialog when the user
 
1958
   * requests changing the filesystem label.
 
1959
   *
 
1960
   * Do not translate $(drive), it's a placeholder and
 
1961
   * will be replaced by the name of the drive/device in question
 
1962
   */
 
1963
  message = N_("Authentication is required to change the filesystem label on $(drive)");
 
1964
  if (!udisks_daemon_util_setup_by_user (daemon, object, caller_uid))
 
1965
    {
 
1966
      if (udisks_block_get_hint_system (block))
 
1967
        {
 
1968
          action_id = "org.freedesktop.udisks2.modify-device-system";
 
1969
        }
 
1970
      else if (!udisks_daemon_util_on_same_seat (daemon, UDISKS_OBJECT (object), caller_pid))
 
1971
        {
 
1972
          action_id = "org.freedesktop.udisks2.modify-device-other-seat";
 
1973
        }
 
1974
    }
 
1975
 
 
1976
  /* Check that the user is actually authorized to change the
 
1977
   * filesystem label.
 
1978
   */
 
1979
  if (!udisks_daemon_util_check_authorization_sync (daemon,
 
1980
                                                    object,
 
1981
                                                    action_id,
 
1982
                                                    options,
 
1983
                                                    message,
 
1984
                                                    invocation))
 
1985
    goto out;
 
1986
 
 
1987
  if (fs_info->command_clear_label != NULL && strlen (label) == 0)
 
1988
    {
 
1989
      command = subst_str_and_escape (fs_info->command_clear_label, "$DEVICE", udisks_block_get_device (block));
 
1990
    }
 
1991
  else
 
1992
    {
 
1993
      tmp = subst_str_and_escape (fs_info->command_change_label, "$DEVICE", udisks_block_get_device (block));
 
1994
      command = subst_str_and_escape (tmp, "$LABEL", label);
 
1995
      g_free (tmp);
 
1996
    }
 
1997
 
 
1998
  job = udisks_daemon_launch_spawned_job (daemon,
 
1999
                                          object,
 
2000
                                          "filesystem-modify", caller_uid,
 
2001
                                          NULL, /* cancellable */
 
2002
                                          0,    /* uid_t run_as_uid */
 
2003
                                          0,    /* uid_t run_as_euid */
 
2004
                                          NULL, /* input_string */
 
2005
                                          "%s", command);
 
2006
  g_signal_connect (job,
 
2007
                    "completed",
 
2008
                    G_CALLBACK (on_set_label_job_completed),
 
2009
                    invocation);
 
2010
 
 
2011
 out:
 
2012
  /* for some FSes we need to copy and modify label; free our copy */
 
2013
  g_free (real_label);
 
2014
  g_free (command);
 
2015
  g_clear_object (&object);
 
2016
  return TRUE; /* returning TRUE means that we handled the method invocation */
 
2017
}
 
2018
 
 
2019
/* ---------------------------------------------------------------------------------------------------- */
 
2020
 
 
2021
static void
 
2022
filesystem_iface_init (UDisksFilesystemIface *iface)
 
2023
{
 
2024
  iface->handle_mount     = handle_mount;
 
2025
  iface->handle_unmount   = handle_unmount;
 
2026
  iface->handle_set_label = handle_set_label;
 
2027
}