1 /*************************************************************************** 2 * CVSID: $Id: hal-storage-mount.c,v 1.7 2006/06/21 00:44:03 david Exp $ 3 * 4 * hal-storage-mount.c : Mount wrapper 5 * 6 * Copyright (C) 2006 David Zeuthen, <david@fubar.dk> 7 * 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. 12 * 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. 17 * 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 21 * 22 **************************************************************************/ 23 24 25 #ifdef HAVE_CONFIG_H 26 # include <config.h> 27 #endif 28 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <glib.h> 33 #include <glib/gstdio.h> 34 #ifdef __FreeBSD__ 35 #include <fstab.h> 36 #include <sys/param.h> 37 #include <sys/ucred.h> 38 #include <sys/mount.h> 39 #include <limits.h> 40 #include <pwd.h> 41 #elif sun 42 #include <fcntl.h> 43 #include <sys/mnttab.h> 44 #include <sys/vfstab.h> 45 #include <sys/stat.h> 46 #include <sys/wait.h> 47 #include <bsm/adt.h> 48 #include <bsm/adt_event.h> 49 #else 50 #include <mntent.h> 51 #endif 52 #include <sys/types.h> 53 #include <unistd.h> 54 #include <sys/file.h> 55 #include <errno.h> 56 #include <syslog.h> 57 58 #include "hal-storage-shared.h" 59 60 #ifdef __FreeBSD__ 61 struct mtab_handle 62 { 63 struct statfs *mounts; 64 int n_mounts; 65 int iter; 66 }; 67 #endif 68 69 70 gboolean 71 mtab_open (gpointer *handle) 72 { 73 #ifdef __FreeBSD__ 74 struct mtab_handle *mtab; 75 76 mtab = g_new0 (struct mtab_handle, 1); 77 mtab->n_mounts = getmntinfo (&mtab->mounts, MNT_NOWAIT); 78 if (mtab->n_mounts == 0) { 79 g_free (mtab); 80 return FALSE; 81 } 82 83 *handle = mtab; 84 return TRUE; 85 #elif sun 86 *handle = fopen (MNTTAB, "r"); 87 return *handle != NULL; 88 #else 89 *handle = fopen ("/proc/mounts", "r"); 90 return *handle != NULL; 91 #endif 92 } 93 94 char * 95 mtab_next (gpointer handle, char **mount_point) 96 { 97 #ifdef __FreeBSD__ 98 struct mtab_handle *mtab = handle; 99 100 if (mtab->iter < mtab->n_mounts) 101 return mtab->mounts[mtab->iter++].f_mntfromname; 102 else 103 return NULL; 104 #error TODO: set *mount_point to g_strdup()-ed value if mount_point!=NULL 105 #elif sun 106 static struct mnttab mnt; 107 108 if (getmntent (handle, &mnt) == 0) { 109 if (mount_point != NULL) { 110 *mount_point = g_strdup (mnt.mnt_mountp); 111 } 112 return mnt.mnt_special; 113 } else { 114 return NULL; 115 } 116 #else 117 struct mntent *mnt; 118 119 mnt = getmntent (handle); 120 121 if (mnt != NULL) { 122 if (mount_point != NULL) { 123 *mount_point = g_strdup (mnt->mnt_dir); 124 } 125 return mnt->mnt_fsname; 126 } else { 127 return NULL; 128 } 129 #endif 130 } 131 132 void 133 mtab_close (gpointer handle) 134 { 135 #ifdef __FreeBSD__ 136 g_free (handle); 137 #else 138 fclose (handle); 139 #endif 140 } 141 142 143 144 gboolean 145 fstab_open (gpointer *handle) 146 { 147 #ifdef __FreeBSD__ 148 return setfsent () == 1; 149 #elif sun 150 *handle = fopen (VFSTAB, "r"); 151 return *handle != NULL; 152 #else 153 *handle = fopen ("/etc/fstab", "r"); 154 return *handle != NULL; 155 #endif 156 } 157 158 char * 159 fstab_next (gpointer handle, char **mount_point) 160 { 161 #ifdef __FreeBSD__ 162 struct fstab *fstab; 163 164 fstab = getfsent (); 165 166 /* TODO: fill out mount_point */ 167 if (mount_point != NULL && fstab != NULL) { 168 *mount_point = fstab->fs_file; 169 } 170 171 return fstab ? fstab->fs_spec : NULL; 172 #elif sun 173 static struct vfstab v; 174 175 return getvfsent (handle, &v) == 0 ? v.vfs_special : NULL; 176 #else 177 struct mntent *mnt; 178 179 mnt = getmntent (handle); 180 181 if (mount_point != NULL && mnt != NULL) { 182 *mount_point = mnt->mnt_dir; 183 } 184 185 return mnt ? mnt->mnt_fsname : NULL; 186 #endif 187 } 188 189 void 190 fstab_close (gpointer handle) 191 { 192 #ifdef __FreeBSD__ 193 endfsent (); 194 #else 195 fclose (handle); 196 #endif 197 } 198 199 #ifdef __FreeBSD__ 200 #define UMOUNT "/sbin/umount" 201 #elif sun 202 #define UMOUNT "/sbin/umount" 203 #else 204 #define UMOUNT "/bin/umount" 205 #endif 206 207 void 208 unknown_error (const char *detail) 209 { 210 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.UnknownFailure\n"); 211 fprintf (stderr, "%s\n", detail); 212 exit (1); 213 } 214 215 216 static void 217 device_busy (const char *detail) 218 { 219 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.Busy\n"); 220 fprintf (stderr, "%s\n", detail); 221 exit (1); 222 } 223 224 225 static void 226 not_mounted (const char *detail) 227 { 228 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.NotMounted\n"); 229 fprintf (stderr, "%s\n", detail); 230 exit (1); 231 } 232 233 234 static void 235 not_mounted_by_hal (const char *detail) 236 { 237 fprintf (stderr, "org.freedesktop.Hal.Device.Volume.NotMountedByHal\n"); 238 fprintf (stderr, "%s\n", detail); 239 exit (1); 240 } 241 242 static void 243 permission_denied_privilege (const char *privilege, const char *uid) 244 { 245 fprintf (stderr, "org.freedesktop.Hal.Device.PermissionDeniedByPolicy\n"); 246 fprintf (stderr, "%s refused uid %s\n", privilege, uid); 247 exit (1); 248 } 249 250 static void 251 permission_denied_volume_ignore (const char *device) 252 { 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); 255 exit (1); 256 } 257 258 void 259 handle_unmount (LibHalContext *hal_ctx, 260 #ifdef HAVE_POLKIT 261 LibPolKitContext *pol_ctx, 262 #endif 263 const char *udi, 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) 268 { 269 int i, j; 270 DBusError error; 271 GError *err = NULL; 272 char *sout = NULL; 273 char *serr = NULL; 274 int exit_status; 275 char *args[10]; 276 int na; 277 FILE *hal_mtab_orig; 278 int hal_mtab_orig_len; 279 int num_read; 280 char *hal_mtab_buf; 281 char **lines; 282 char *mount_point_to_unmount; 283 gboolean mounted_by_other_uid; 284 FILE *hal_mtab_new; 285 #ifdef sun 286 adt_export_data_t *adt_data; 287 size_t adt_data_size; 288 #endif 289 290 #ifdef DEBUG 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); 294 #endif 295 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); 302 } 303 /* 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. 308 */ 309 if (atol (invoked_by_uid) != 0) { 310 permission_denied_volume_ignore (device); 311 } 312 } 313 314 if (!libhal_volume_is_mounted (volume)) { 315 not_mounted ("According to HAL, the volume is not mounted"); 316 } 317 } 318 319 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"); 324 } 325 if (fseek (hal_mtab_orig, 0L, SEEK_END) != 0) { 326 unknown_error ("Cannot seek to end of /media/.hal-mtab"); 327 } 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"); 331 } 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"); 337 } 338 fclose (hal_mtab_orig); 339 340 #ifdef DEBUG 341 printf ("hal_mtab = '%s'\n", hal_mtab_buf); 342 #endif 343 344 lines = g_strsplit (hal_mtab_buf, "\n", 0); 345 g_free (hal_mtab_buf); 346 347 mount_point_to_unmount = NULL; 348 mounted_by_other_uid = TRUE; 349 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; 354 struct stat st; 355 356 #ifdef DEBUG 357 printf (" line = '%s'\n", lines[i]); 358 #endif 359 360 if ((lines[i])[0] == '#') 361 continue; 362 363 line_elements = g_strsplit (lines[i], "\t", 6); 364 if (g_strv_length (line_elements) == 6) { 365 366 #ifdef DEBUG 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]); 373 #endif 374 375 if (strcmp (line_elements[0], device) == 0) { 376 char *line_to_free; 377 378 if (strcmp (line_elements[1], invoked_by_uid) == 0) { 379 mounted_by_other_uid = FALSE; 380 } 381 #ifdef sun 382 if (stat("/dev/console", &st) == 0 && 383 st.st_uid == atoi (invoked_by_uid)) { 384 /* 385 * Owner is allowed to take over. Before we have real 386 * ownership in HAL, assume it's the console owner. 387 */ 388 mounted_by_other_uid = FALSE; 389 } 390 #endif /* sun */ 391 mount_point_to_unmount = g_strdup (line_elements[5]); 392 393 line_to_free = lines[i]; 394 395 for (j = i; lines[j] != NULL; j++) { 396 lines[j] = lines[j+1]; 397 } 398 lines[j] = NULL; 399 400 g_free (line_to_free); 401 402 g_strfreev (line_elements); 403 goto line_found; 404 405 } 406 407 } 408 409 g_strfreev (line_elements); 410 } 411 line_found: 412 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"); 415 } 416 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 419 * 420 * We allow uid 0 to actually ensure that Unmount(options=["lazy"], "/dev/blah") works from addon-storage. 421 */ 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); 425 } 426 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~"); 431 } 432 for (i = 0; lines[i] != NULL; i++) { 433 if (i > 0) { 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~"); 437 } 438 } 439 440 if (fwrite (lines[i], 1, strlen (lines[i]), hal_mtab_new) != strlen (lines[i])) { 441 unknown_error ("Cannot write to /media/.hal-mtab~"); 442 } 443 444 } 445 fclose (hal_mtab_new); 446 447 g_strfreev (lines); 448 449 /* construct arguments to /bin/umount */ 450 na = 0; 451 args[na++] = UMOUNT; 452 if (option_lazy) 453 args[na++] = "-l"; 454 if (option_force) 455 args[na++] = "-f"; 456 args[na++] = (char *) device; 457 args[na++] = NULL; 458 459 #ifdef DEBUG 460 printf ("will umount %s (mounted at '%s'), mounted_by_other_uid=%d\n", 461 device, mount_point_to_unmount, mounted_by_other_uid); 462 #endif 463 464 /* invoke /bin/umount */ 465 if (!g_spawn_sync ("/", 466 args, 467 NULL, 468 0, 469 NULL, 470 NULL, 471 &sout, 472 &serr, 473 &exit_status, 474 &err)) { 475 printf ("Cannot execute %s\n", UMOUNT); 476 unlink ("/media/.hal-mtab~"); 477 unknown_error ("Cannot spawn " UMOUNT); 478 } 479 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); 483 484 if (strstr (serr, "device is busy") != NULL) { 485 unlink ("/media/.hal-mtab~"); 486 device_busy (serr); 487 } else { 488 unlink ("/media/.hal-mtab~"); 489 unknown_error (serr); 490 } 491 } 492 493 #ifdef sun 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); 499 free (adt_data); 500 } 501 #endif 502 503 /* unmount was succesful, remove directory we created in Mount() */ 504 #ifdef sun 505 if (strncmp (mount_point_to_unmount, "/media/", 7) == 0) 506 #endif 507 if (g_rmdir (mount_point_to_unmount) != 0) { 508 unlink ("/media/.hal-mtab~"); 509 unknown_error ("Cannot remove directory"); 510 } 511 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"); 516 } 517 518 #ifdef DEBUG 519 printf ("done unmounting\n"); 520 #endif 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); 523 closelog (); 524 525 g_free (sout); 526 g_free (serr); 527 g_free (mount_point_to_unmount); 528 } 529 530 #define EJECT "/usr/bin/eject" 531 532 void 533 handle_eject (LibHalContext *hal_ctx, 534 #ifdef HAVE_POLKIT 535 LibPolKitContext *pol_ctx, 536 #endif 537 const char *udi, 538 LibHalDrive *drive, const char *device, 539 const char *invoked_by_uid, const char *invoked_by_syscon_name, 540 gboolean closetray, DBusConnection *system_bus) 541 { 542 GError *err = NULL; 543 char *sout = NULL; 544 char *serr = NULL; 545 int exit_status; 546 char *args[10]; 547 int na; 548 #ifdef sun 549 adt_export_data_t *adt_data; 550 size_t adt_data_size; 551 #endif 552 /* TODO: should we require privileges here? */ 553 554 #ifdef DEBUG 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); 558 #endif 559 560 /* construct arguments to EJECT (e.g. /usr/bin/eject) */ 561 na = 0; 562 args[na++] = EJECT; 563 if (closetray) { 564 args[na++] = "-t"; 565 } 566 args[na++] = (char *) device; 567 args[na++] = NULL; 568 569 #ifdef sun 570 putenv("EJECT_DIRECT=1"); 571 #endif 572 573 #ifdef DEBUG 574 printf ("will eject %s\n", device); 575 #endif 576 577 /* invoke eject command */ 578 if (!g_spawn_sync ("/", 579 args, 580 NULL, 581 0, 582 NULL, 583 NULL, 584 &sout, 585 &serr, 586 &exit_status, 587 &err)) { 588 printf ("Cannot execute %s\n", EJECT); 589 unknown_error ("Cannot spawn " EJECT); 590 } 591 592 #ifdef sun 593 /* 594 * Solaris eject returns 4 for manually ejectable media like floppy. 595 * Consider it success. 596 */ 597 if (WEXITSTATUS(exit_status) == 4) { 598 exit_status = 0; 599 } 600 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); 605 free (adt_data); 606 } 607 #endif /* sun */ 608 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); 612 613 unknown_error (serr); 614 } 615 616 /* eject was succesful... */ 617 618 #ifdef DEBUG 619 printf ("done ejecting\n"); 620 #endif 621 622 g_free (sout); 623 g_free (serr); 624 } 625 626 627 static int lock_mtab_fd = -1; 628 629 gboolean 630 lock_hal_mtab (void) 631 { 632 if (lock_mtab_fd >= 0) 633 return TRUE; 634 635 printf ("%d: XYA attempting to get lock on /media/.hal-mtab-lock\n", getpid ()); 636 637 lock_mtab_fd = open ("/media/.hal-mtab-lock", O_CREAT | O_RDWR); 638 639 if (lock_mtab_fd < 0) 640 return FALSE; 641 642 tryagain: 643 #if sun 644 if (lockf (lock_mtab_fd, F_LOCK, 0) != 0) { 645 #else 646 if (flock (lock_mtab_fd, LOCK_EX) != 0) { 647 #endif 648 if (errno == EINTR) 649 goto tryagain; 650 return FALSE; 651 } 652 653 printf ("%d: XYA got lock on /media/.hal-mtab-lock\n", getpid ()); 654 655 656 return TRUE; 657 } 658 659 void 660 unlock_hal_mtab (void) 661 { 662 #if sun 663 lockf (lock_mtab_fd, F_ULOCK, 0); 664 #else 665 flock (lock_mtab_fd, LOCK_UN); 666 #endif 667 close (lock_mtab_fd); 668 lock_mtab_fd = -1; 669 printf ("%d: XYA released lock on /media/.hal-mtab-lock\n", getpid ()); 670 } 671 672 #if sun 673 674 /* map PolicyKit privilege to RBAC authorization */ 675 char * 676 auth_from_privilege(const char *privilege) 677 { 678 char *authname; 679 int i; 680 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"); 689 } else { 690 /* replace '-' with '.' */ 691 authname = g_strdup (privilege); 692 for (i = 0; i < strlen (authname); i++) { 693 if (authname[i] == '-') { 694 authname[i] = '.'; 695 } 696 } 697 } 698 return (authname); 699 } 700 701 void 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) 705 { 706 adt_session_data_t *ah; 707 adt_event_data_t *event; 708 709 if (adt_start_session(&ah, imported_state, 0) != 0) { 710 printf ("adt_start_session failed %d\n", errno); 711 return; 712 } 713 if ((event = adt_alloc_event(ah, event_id)) == NULL) { 714 printf ("adt_alloc_event(ADT_attach)\n", errno); 715 return; 716 } 717 718 switch (event_id) { 719 case ADT_attach: 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; 724 break; 725 case ADT_detach: 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; 730 break; 731 case ADT_remove: 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; 735 break; 736 default: 737 goto out; 738 } 739 740 if (result == 0) { 741 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { 742 printf ("adt_put_event(%d, success)\n", event_id); 743 } 744 } else { 745 if (adt_put_event(event, ADT_FAILURE, result) != 0) { 746 printf ("adt_put_event(%d, failure)\n", event_id); 747 } 748 } 749 out: 750 adt_free_event(event); 751 (void) adt_end_session(ah); 752 } 753 754 #endif /* sun */ 755