~psusi/ubuntu/utopic/udisks2/fix-standby

« back to all changes in this revision

Viewing changes to .pc/dosfslabel.patch/src/udiskslinuxfilesystem.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt, Martin Pitt, Colin Watson, Andreas Henriksson
  • Date: 2014-01-14 10:04:52 UTC
  • mfrom: (14.2.3 sid)
  • Revision ID: package-import@ubuntu.com-20140114100452-hpj6k2wt5gtml2xb
Tags: 2.1.2-1
[ Martin Pitt ]
* New upstream release:
  - Add exfat support (Closes: #720695)
  - Fix crash when waiting for loop device (LP: #1128275)
  - Use dosfstools instead of mtools
  - Add polkit authorization variables for removable media
  - Hide more rescue partitions
* Drop dosfslabel.patch, fixed upstream.
* Add Recommends: gdisk, as we need it for manipulating GPT partition tables
  through sgdisk. (LP: #1080745)
* Bump Standards-Version to 3.9.5. No changes necessary.

[ Colin Watson ]
* Use dh-autoreconf to update libtool macros for new ports.
  (Closes: #732287)

[ Andreas Henriksson ]
* Add parted dependency. (Closes: #720491)

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