1 /***************************************************************************
2 * CVSID: $Id: hal-storage-mount.c,v 1.7 2006/06/21 00:44:03 david Exp $
4 * hal-storage-mount.c : Mount wrapper
6 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 **************************************************************************/
33 #include <glib/gstdio.h>
36 #include <sys/param.h>
37 #include <sys/ucred.h>
38 #include <sys/mount.h>
43 #include <sys/mnttab.h>
44 #include <sys/vfstab.h>
48 #include <bsm/adt_event.h>
52 #include <sys/types.h>
58 #include "hal-storage-shared.h"
63 struct statfs
*mounts
;
71 mtab_open (gpointer
*handle
)
74 struct mtab_handle
*mtab
;
76 mtab
= g_new0 (struct mtab_handle
, 1);
77 mtab
->n_mounts
= getmntinfo (&mtab
->mounts
, MNT_NOWAIT
);
78 if (mtab
->n_mounts
== 0) {
86 *handle
= fopen (MNTTAB
, "r");
87 return *handle
!= NULL
;
89 *handle
= fopen ("/proc/mounts", "r");
90 return *handle
!= NULL
;
95 mtab_next (gpointer handle
, char **mount_point
)
98 struct mtab_handle
*mtab
= handle
;
100 if (mtab
->iter
< mtab
->n_mounts
)
101 return mtab
->mounts
[mtab
->iter
++].f_mntfromname
;
104 #error TODO: set *mount_point to g_strdup()-ed value if mount_point!=NULL
106 static struct mnttab mnt
;
108 if (getmntent (handle
, &mnt
) == 0) {
109 if (mount_point
!= NULL
) {
110 *mount_point
= g_strdup (mnt
.mnt_mountp
);
112 return mnt
.mnt_special
;
119 mnt
= getmntent (handle
);
122 if (mount_point
!= NULL
) {
123 *mount_point
= g_strdup (mnt
->mnt_dir
);
125 return mnt
->mnt_fsname
;
133 mtab_close (gpointer handle
)
145 fstab_open (gpointer
*handle
)
148 return setfsent () == 1;
150 *handle
= fopen (VFSTAB
, "r");
151 return *handle
!= NULL
;
153 *handle
= fopen ("/etc/fstab", "r");
154 return *handle
!= NULL
;
159 fstab_next (gpointer handle
, char **mount_point
)
166 /* TODO: fill out mount_point */
167 if (mount_point
!= NULL
&& fstab
!= NULL
) {
168 *mount_point
= fstab
->fs_file
;
171 return fstab
? fstab
->fs_spec
: NULL
;
173 static struct vfstab v
;
175 return getvfsent (handle
, &v
) == 0 ? v
.vfs_special
: NULL
;
179 mnt
= getmntent (handle
);
181 if (mount_point
!= NULL
&& mnt
!= NULL
) {
182 *mount_point
= mnt
->mnt_dir
;
185 return mnt
? mnt
->mnt_fsname
: NULL
;
190 fstab_close (gpointer handle
)
200 #define UMOUNT "/sbin/umount"
202 #define UMOUNT "/sbin/umount"
204 #define UMOUNT "/bin/umount"
208 unknown_error (const char *detail
)
210 fprintf (stderr
, "org.freedesktop.Hal.Device.Volume.UnknownFailure\n");
211 fprintf (stderr
, "%s\n", detail
);
217 device_busy (const char *detail
)
219 fprintf (stderr
, "org.freedesktop.Hal.Device.Volume.Busy\n");
220 fprintf (stderr
, "%s\n", detail
);
226 not_mounted (const char *detail
)
228 fprintf (stderr
, "org.freedesktop.Hal.Device.Volume.NotMounted\n");
229 fprintf (stderr
, "%s\n", detail
);
235 not_mounted_by_hal (const char *detail
)
237 fprintf (stderr
, "org.freedesktop.Hal.Device.Volume.NotMountedByHal\n");
238 fprintf (stderr
, "%s\n", detail
);
243 permission_denied_privilege (const char *privilege
, const char *uid
)
245 fprintf (stderr
, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n");
246 fprintf (stderr
, "%s refused uid %s\n", privilege
, uid
);
251 permission_denied_volume_ignore (const char *device
)
253 fprintf (stderr
, "org.freedesktop.Hal.Device.Volume.PermissionDenied\n");
254 fprintf (stderr
, "Device has %s volume.ignore set to TRUE. Refusing to mount.\n", device
);
259 handle_unmount (LibHalContext
*hal_ctx
,
261 LibPolKitContext
*pol_ctx
,
264 LibHalVolume
*volume
, LibHalDrive
*drive
, const char *device
,
265 const char *invoked_by_uid
, const char *invoked_by_syscon_name
,
266 gboolean option_lazy
, gboolean option_force
,
267 DBusConnection
*system_bus
)
278 int hal_mtab_orig_len
;
282 char *mount_point_to_unmount
;
283 gboolean mounted_by_other_uid
;
286 adt_export_data_t
*adt_data
;
287 size_t adt_data_size
;
291 printf ("device = %s\n", device
);
292 printf ("invoked by uid = %s\n", invoked_by_uid
);
293 printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name
);
296 if (volume
!= NULL
) {
297 dbus_error_init (&error
);
298 if (libhal_device_get_property_bool (hal_ctx
, udi
, "volume.ignore", &error
) ||
299 dbus_error_is_set (&error
)) {
300 if (dbus_error_is_set (&error
)) {
301 LIBHAL_FREE_DBUS_ERROR (&error
);
304 * When device allocation is enabled (bsmconv or TX), we
305 * set volume.ignore on all volumes, but still want
306 * Mount() to succeed when called from the euid=0
307 * device allocation program.
309 if (atol (invoked_by_uid
) != 0) {
310 permission_denied_volume_ignore (device
);
314 if (!libhal_volume_is_mounted (volume
)) {
315 not_mounted ("According to HAL, the volume is not mounted");
320 /* check hal's mtab file to verify the device to unmount is actually mounted by hal */
321 hal_mtab_orig
= fopen ("/media/.hal-mtab", "r");
322 if (hal_mtab_orig
== NULL
) {
323 unknown_error ("Cannot open /media/.hal-mtab");
325 if (fseek (hal_mtab_orig
, 0L, SEEK_END
) != 0) {
326 unknown_error ("Cannot seek to end of /media/.hal-mtab");
328 hal_mtab_orig_len
= ftell (hal_mtab_orig
);
329 if (hal_mtab_orig_len
< 0) {
330 unknown_error ("Cannot determine size of /media/.hal-mtab");
332 rewind (hal_mtab_orig
);
333 hal_mtab_buf
= g_new0 (char, hal_mtab_orig_len
+ 1);
334 num_read
= fread (hal_mtab_buf
, 1, hal_mtab_orig_len
, hal_mtab_orig
);
335 if (num_read
!= hal_mtab_orig_len
) {
336 unknown_error ("Cannot read from /media/.hal-mtab");
338 fclose (hal_mtab_orig
);
341 printf ("hal_mtab = '%s'\n", hal_mtab_buf
);
344 lines
= g_strsplit (hal_mtab_buf
, "\n", 0);
345 g_free (hal_mtab_buf
);
347 mount_point_to_unmount
= NULL
;
348 mounted_by_other_uid
= TRUE
;
350 /* find the entry we're going to unmount */
351 for (i
= 0; lines
[i
] != NULL
; i
++) {
352 char **line_elements
;
353 char *special
, *dosp
;
357 printf (" line = '%s'\n", lines
[i
]);
360 if ((lines
[i
])[0] == '#')
363 line_elements
= g_strsplit (lines
[i
], "\t", 6);
364 if (g_strv_length (line_elements
) == 6) {
367 printf (" devfile = '%s'\n", line_elements
[0]);
368 printf (" uid = '%s'\n", line_elements
[1]);
369 printf (" session id = '%s'\n", line_elements
[2]);
370 printf (" fs = '%s'\n", line_elements
[3]);
371 printf (" options = '%s'\n", line_elements
[4]);
372 printf (" mount_point = '%s'\n", line_elements
[5]);
375 if (strcmp (line_elements
[0], device
) == 0) {
378 if (strcmp (line_elements
[1], invoked_by_uid
) == 0) {
379 mounted_by_other_uid
= FALSE
;
382 if (stat("/dev/vt/console_user", &st
) == 0 &&
383 st
.st_uid
== atoi (invoked_by_uid
)) {
385 * Owner is allowed to take over. Before we have real
386 * ownership in HAL, assume it's the console owner.
388 mounted_by_other_uid
= FALSE
;
391 mount_point_to_unmount
= g_strdup (line_elements
[5]);
393 line_to_free
= lines
[i
];
395 for (j
= i
; lines
[j
] != NULL
; j
++) {
396 lines
[j
] = lines
[j
+1];
400 g_free (line_to_free
);
402 g_strfreev (line_elements
);
409 g_strfreev (line_elements
);
413 if (mount_point_to_unmount
== NULL
) {
414 not_mounted_by_hal ("Device to unmount is not in /media/.hal-mtab so it is not mounted by HAL");
417 /* bail out, unless if we got the "hal-storage-can-unmount-volumes-mounted-by-others" privilege only
418 * if mounted_by_other_uid==TRUE
420 * We allow uid 0 to actually ensure that Unmount(options=["lazy"], "/dev/blah") works from addon-storage.
422 if ((strcmp (invoked_by_uid
, "0") != 0) && mounted_by_other_uid
) {
423 /* TODO: actually check for privilege "hal-storage-can-unmount-volumes-mounted-by-others" */
424 permission_denied_privilege ("hal-storage-can-unmount-volumes-mounted-by-others", invoked_by_uid
);
427 /* create new .hal-mtab~ file without the entry we're going to unmount */
428 hal_mtab_new
= fopen ("/media/.hal-mtab~", "w");
429 if (hal_mtab_new
== NULL
) {
430 unknown_error ("Cannot create /media/.hal-mtab~");
432 for (i
= 0; lines
[i
] != NULL
; i
++) {
434 char anewl
[2] = "\n\0";
435 if (fwrite (anewl
, 1, 1, hal_mtab_new
) != 1) {
436 unknown_error ("Cannot write to /media/.hal-mtab~");
440 if (fwrite (lines
[i
], 1, strlen (lines
[i
]), hal_mtab_new
) != strlen (lines
[i
])) {
441 unknown_error ("Cannot write to /media/.hal-mtab~");
445 fclose (hal_mtab_new
);
449 /* construct arguments to /bin/umount */
456 args
[na
++] = (char *) device
;
460 printf ("will umount %s (mounted at '%s'), mounted_by_other_uid=%d\n",
461 device
, mount_point_to_unmount
, mounted_by_other_uid
);
464 /* invoke /bin/umount */
465 if (!g_spawn_sync ("/",
475 printf ("Cannot execute %s\n", UMOUNT
);
476 unlink ("/media/.hal-mtab~");
477 unknown_error ("Cannot spawn " UMOUNT
);
480 /* check if unmount was succesful */
481 if (exit_status
!= 0) {
482 printf ("%s error %d, stdout='%s', stderr='%s'\n", UMOUNT
, exit_status
, sout
, serr
);
484 if (strstr (serr
, "device is busy") != NULL
) {
485 unlink ("/media/.hal-mtab~");
488 unlink ("/media/.hal-mtab~");
489 unknown_error (serr
);
494 if ((adt_data
= get_audit_export_data (system_bus
,
495 invoked_by_syscon_name
, &adt_data_size
)) != NULL
) {
496 audit_volume (adt_data
, ADT_detach
, WEXITSTATUS(exit_status
),
497 "solaris.device.mount.removable",
498 mount_point_to_unmount
, device
, NULL
);
503 /* unmount was succesful, remove directory we created in Mount() */
505 if (strncmp (mount_point_to_unmount
, "/media/", 7) == 0)
507 if (g_rmdir (mount_point_to_unmount
) != 0) {
508 unlink ("/media/.hal-mtab~");
509 unknown_error ("Cannot remove directory");
512 /* set new .hal-mtab file */
513 if (rename ("/media/.hal-mtab~", "/media/.hal-mtab") != 0) {
514 unlink ("/media/.hal-mtab~");
515 unknown_error ("Cannot rename /media/.hal-mtab~ to /media/.hal-mtab");
519 printf ("done unmounting\n");
521 openlog ("hald", 0, LOG_DAEMON
);
522 syslog (LOG_INFO
, "unmounted %s from '%s' on behalf of uid %s", device
, mount_point_to_unmount
, invoked_by_uid
);
527 g_free (mount_point_to_unmount
);
530 #define EJECT "/usr/bin/eject"
533 handle_eject (LibHalContext
*hal_ctx
,
535 LibPolKitContext
*pol_ctx
,
538 LibHalDrive
*drive
, const char *device
,
539 const char *invoked_by_uid
, const char *invoked_by_syscon_name
,
540 gboolean closetray
, DBusConnection
*system_bus
)
549 adt_export_data_t
*adt_data
;
550 size_t adt_data_size
;
552 /* TODO: should we require privileges here? */
555 printf ("device = %s\n", device
);
556 printf ("invoked by uid = %s\n", invoked_by_uid
);
557 printf ("invoked by system bus connection = %s\n", invoked_by_syscon_name
);
560 /* construct arguments to EJECT (e.g. /usr/bin/eject) */
566 args
[na
++] = (char *) device
;
570 putenv("EJECT_DIRECT=1");
574 printf ("will eject %s\n", device
);
577 /* invoke eject command */
578 if (!g_spawn_sync ("/",
588 printf ("Cannot execute %s\n", EJECT
);
589 unknown_error ("Cannot spawn " EJECT
);
594 * Solaris eject returns 4 for manually ejectable media like floppy.
595 * Consider it success.
597 if (WEXITSTATUS(exit_status
) == 4) {
601 if ((adt_data
= get_audit_export_data (system_bus
,
602 invoked_by_syscon_name
, &adt_data_size
)) != NULL
) {
603 audit_volume (adt_data
, ADT_remove
, WEXITSTATUS(exit_status
),
604 "solaris.device.mount.removable", NULL
, device
, NULL
);
609 /* check if eject was succesful */
610 if (exit_status
!= 0) {
611 printf ("%s error %d, stdout='%s', stderr='%s'\n", EJECT
, exit_status
, sout
, serr
);
613 unknown_error (serr
);
616 /* eject was succesful... */
619 printf ("done ejecting\n");
627 static int lock_mtab_fd
= -1;
632 if (lock_mtab_fd
>= 0)
635 printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ());
637 lock_mtab_fd
= open ("/media/.hal-mtab-lock", O_CREAT
| O_RDWR
);
639 if (lock_mtab_fd
< 0)
644 if (lockf (lock_mtab_fd
, F_LOCK
, 0) != 0) {
646 if (flock (lock_mtab_fd
, LOCK_EX
) != 0) {
653 printf ("%d: XYA got lock on /media/.hal-mtab-lock\n", getpid ());
660 unlock_hal_mtab (void)
663 lockf (lock_mtab_fd
, F_ULOCK
, 0);
665 flock (lock_mtab_fd
, LOCK_UN
);
667 close (lock_mtab_fd
);
669 printf ("%d: XYA released lock on /media/.hal-mtab-lock\n", getpid ());
674 /* map PolicyKit privilege to RBAC authorization */
676 auth_from_privilege(const char *privilege
)
681 if (strcmp (privilege
, "hal-storage-removable-mount") == 0) {
682 authname
= g_strdup ("solaris.device.mount.removable");
683 } else if (strcmp (privilege
, "hal-storage-removable-mount-all-options") == 0) {
684 authname
= g_strdup ("solaris.device.mount.alloptions.removable");
685 } else if (strcmp (privilege
, "hal-storage-fixed-mount") == 0) {
686 authname
= g_strdup ("solaris.device.mount.fixed");
687 } else if (strcmp (privilege
, "hal-storage-fixed-mount-all-options") == 0) {
688 authname
= g_strdup ("solaris.device.mount.alloptions.fixed");
690 /* replace '-' with '.' */
691 authname
= g_strdup (privilege
);
692 for (i
= 0; i
< strlen (authname
); i
++) {
693 if (authname
[i
] == '-') {
702 audit_volume(const adt_export_data_t
*imported_state
, au_event_t event_id
,
703 int result
, const char *auth_used
, const char *mount_point
,
704 const char *device
, const char *options
)
706 adt_session_data_t
*ah
;
707 adt_event_data_t
*event
;
709 if (adt_start_session(&ah
, imported_state
, 0) != 0) {
710 printf ("adt_start_session failed %d\n", errno
);
713 if ((event
= adt_alloc_event(ah
, event_id
)) == NULL
) {
714 printf ("adt_alloc_event(ADT_attach)\n", errno
);
720 event
->adt_attach
.auth_used
= (char *)auth_used
;
721 event
->adt_attach
.mount_point
= (char *)mount_point
;
722 event
->adt_attach
.device
= (char *)device
;
723 event
->adt_attach
.options
= (char *)options
;
726 event
->adt_detach
.auth_used
= (char *)auth_used
;
727 event
->adt_detach
.mount_point
= (char *)mount_point
;
728 event
->adt_detach
.device
= (char *)device
;
729 event
->adt_detach
.options
= (char *)options
;
732 event
->adt_remove
.auth_used
= (char *)auth_used
;
733 event
->adt_remove
.mount_point
= (char *)mount_point
;
734 event
->adt_remove
.device
= (char *)device
;
741 if (adt_put_event(event
, ADT_SUCCESS
, ADT_SUCCESS
) != 0) {
742 printf ("adt_put_event(%d, success)\n", event_id
);
745 if (adt_put_event(event
, ADT_FAILURE
, result
) != 0) {
746 printf ("adt_put_event(%d, failure)\n", event_id
);
750 adt_free_event(event
);
751 (void) adt_end_session(ah
);