1 /*************************************************************************** 2 * 3 * probe-volume.c : probe volumes 4 * 5 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 6 * Use is subject to license terms. 7 * 8 * Licensed under the Academic Free License version 2.1 9 * 10 **************************************************************************/ 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #ifdef HAVE_CONFIG_H 15 # include <config.h> 16 #endif 17 18 #include <errno.h> 19 #include <string.h> 20 #include <stdlib.h> 21 #include <stdio.h> 22 #include <sys/ioctl.h> 23 #include <sys/types.h> 24 #include <sys/stat.h> 25 #include <fcntl.h> 26 #include <unistd.h> 27 #include <ctype.h> 28 #include <time.h> 29 #include <sys/time.h> 30 #include <sys/dkio.h> 31 #include <sys/cdio.h> 32 #include <sys/fdio.h> 33 #include <libnvpair.h> 34 #include <libfstyp.h> 35 #include <sys/vtoc.h> 36 #include <sys/efi_partition.h> 37 #include <priv.h> 38 39 #include <libhal.h> 40 #include <cdutils.h> 41 #include <fsutils.h> 42 #include <logger.h> 43 44 static void 45 my_dbus_error_free(DBusError *error) 46 { 47 if (dbus_error_is_set(error)) { 48 dbus_error_free(error); 49 } 50 } 51 52 /* 53 * Return a copy of a string without trailing spaces. If 'len' is non-zero, 54 * it specifies max length, otherwise the string must be null-terminated. 55 */ 56 static char * 57 rtrim_copy(char *src, int len) 58 { 59 char *dst, *p; 60 61 if (len == 0) { 62 len = strlen(src); 63 } 64 if ((dst = calloc(1, len + 1)) != NULL) { 65 strncpy(dst, src, len); 66 p = dst + len - 1; 67 while ((p >= dst) && (isspace(*p))) { 68 *p-- = '\0'; 69 } 70 } 71 return (dst); 72 } 73 74 static void 75 set_fstyp_properties (LibHalContext *ctx, const char *udi, const char *fstype, nvlist_t *fsattr) 76 { 77 char buf[256]; 78 DBusError error; 79 char *uuid = NULL; 80 char *label_orig = NULL; 81 char *label = NULL; 82 LibHalChangeSet *cs; 83 84 dbus_error_init (&error); 85 86 if ((cs = libhal_device_new_changeset (udi)) == NULL) { 87 return; 88 } 89 90 libhal_changeset_set_property_string (cs, "volume.fsusage", "filesystem"); 91 libhal_changeset_set_property_string (cs, "volume.fstype", fstype); 92 93 /* label */ 94 (void) nvlist_lookup_string(fsattr, "gen_volume_label", &label_orig); 95 if (label_orig != NULL) { 96 label = rtrim_copy(label_orig, 0); 97 } 98 if ((label != NULL) && (label[0] != '\0')) { 99 libhal_changeset_set_property_string (cs, "volume.label", label); 100 libhal_changeset_set_property_string (cs, "info.product", label); 101 } else { 102 libhal_changeset_set_property_string (cs, "volume.label", ""); 103 snprintf (buf, sizeof (buf), "Volume (%s)", fstype); 104 libhal_changeset_set_property_string (cs, "info.product", buf); 105 } 106 free(label); 107 108 /* uuid */ 109 if (nvlist_lookup_string(fsattr, "gen_uuid", &uuid) == 0) { 110 libhal_changeset_set_property_string (cs, "volume.uuid", uuid); 111 } else { 112 libhal_changeset_set_property_string (cs, "volume.uuid", ""); 113 } 114 115 libhal_device_commit_changeset (ctx, cs, &error); 116 libhal_device_free_changeset (cs); 117 118 my_dbus_error_free (&error); 119 } 120 121 dbus_bool_t 122 probe_disc (int fd, LibHalContext *ctx, const char *udi, dbus_bool_t *should_probe_for_fs) 123 { 124 DBusError error; 125 disc_info_t di; 126 int profile; 127 dbus_bool_t has_audio, has_data, is_blank, is_appendable, is_rewritable; 128 char *disc_type = "cd_rom"; 129 uint64_t capacity = 0; 130 int i; 131 LibHalChangeSet *cs; 132 133 dbus_error_init (&error); 134 135 if (get_disc_info (fd, &di)) { 136 is_blank = (di.disc_status == 0); 137 is_appendable = (di.disc_status == 1); 138 is_rewritable = (di.erasable != 0); 139 } else { 140 is_blank = is_appendable = is_rewritable = FALSE; 141 } 142 143 if (get_current_profile (fd, &profile)) { 144 switch (profile) { 145 case 0x08: /* CD-ROM */ 146 disc_type = "cd_rom"; 147 break; 148 case 0x09: /* CD-R */ 149 disc_type = "cd_r"; 150 break; 151 case 0x0A: /* CD-RW */ 152 disc_type = "cd_rw"; 153 is_rewritable = TRUE; 154 break; 155 case 0x10: /* DVD-ROM */ 156 disc_type = "dvd_rom"; 157 break; 158 case 0x11: /* DVD-R Sequential */ 159 disc_type = "dvd_r"; 160 break; 161 case 0x12: /* DVD-RAM */ 162 disc_type = "dvd_ram"; 163 is_rewritable = TRUE; 164 break; 165 case 0x13: /* DVD-RW Restricted Overwrite */ 166 disc_type = "dvd_rw"; 167 is_rewritable = TRUE; 168 break; 169 case 0x14: /* DVD-RW Sequential */ 170 disc_type = "dvd_rw"; 171 is_rewritable = TRUE; 172 break; 173 case 0x1A: /* DVD+RW */ 174 disc_type = "dvd_plus_rw"; 175 is_rewritable = TRUE; 176 break; 177 case 0x1B: /* DVD+R */ 178 disc_type = "dvd_plus_r"; 179 break; 180 case 0x2B: /* DVD+R Double Layer */ 181 disc_type = "dvd_plus_r_dl"; 182 break; 183 case 0x40: /* BD-ROM */ 184 disc_type = "bd_rom"; 185 break; 186 case 0x41: /* BD-R Sequential */ 187 disc_type = "bd_r"; 188 break; 189 case 0x42: /* BD-R Random */ 190 disc_type = "bd_r"; 191 break; 192 case 0x43: /* BD-RE */ 193 disc_type = "bd_re"; 194 is_rewritable = TRUE; 195 break; 196 case 0x50: /* HD DVD-ROM */ 197 disc_type = "hddvd_rom"; 198 break; 199 case 0x51: /* HD DVD-R */ 200 disc_type = "hddvd_r"; 201 break; 202 case 0x52: /* HD DVD-Rewritable */ 203 disc_type = "hddvd_rw"; 204 is_rewritable = TRUE; 205 break; 206 } 207 208 (void) get_disc_capacity_for_profile(fd, profile, &capacity); 209 } 210 211 has_audio = has_data = FALSE; 212 if (!is_blank) { 213 uchar_t smalltoc[12]; 214 size_t toc_size; 215 uchar_t *toc, *p; 216 217 /* 218 * XXX for some reason CDROMREADTOCENTRY fails on video DVDs, 219 * but extracting the toc directly works okay. 220 */ 221 if (!read_toc(fd, 0, 1, 4, smalltoc)) { 222 HAL_DEBUG(("read_toc failed")); 223 has_data = B_TRUE; /* probe for fs anyway */ 224 } else { 225 toc_size = smalltoc[0] * 256 + smalltoc[1] + 2; 226 toc = (uchar_t *)calloc(1, toc_size); 227 if (toc == NULL || !read_toc(fd, 0, 1, toc_size, toc)) { 228 HAL_DEBUG (("read_toc again failed")); 229 } else { 230 for (p = &toc[4]; p < (toc + toc_size); p += 8) { 231 /* skip leadout */ 232 if (p[2] == 0xAA) { 233 continue; 234 } 235 if (p[1] & 4) { 236 has_data = B_TRUE; 237 } else { 238 has_audio = B_TRUE; 239 } 240 } 241 } 242 free(toc); 243 } 244 } 245 246 if ((cs = libhal_device_new_changeset (udi)) == NULL) { 247 return (FALSE); 248 } 249 libhal_changeset_set_property_string (cs, "volume.disc.type", disc_type); 250 libhal_changeset_set_property_bool (cs, "volume.disc.is_blank", is_blank); 251 libhal_changeset_set_property_bool (cs, "volume.disc.has_audio", has_audio); 252 libhal_changeset_set_property_bool (cs, "volume.disc.has_data", has_data); 253 libhal_changeset_set_property_bool (cs, "volume.disc.is_appendable", is_appendable); 254 libhal_changeset_set_property_bool (cs, "volume.disc.is_rewritable", is_rewritable); 255 libhal_changeset_set_property_uint64 (cs, "volume.disc.capacity", capacity); 256 257 libhal_device_commit_changeset (ctx, cs, &error); 258 libhal_device_free_changeset (cs); 259 260 out: 261 262 *should_probe_for_fs = has_data; 263 264 my_dbus_error_free (&error); 265 266 return (TRUE); 267 } 268 269 void 270 drop_privileges () 271 { 272 priv_set_t *pPrivSet = NULL; 273 priv_set_t *lPrivSet = NULL; 274 275 /* 276 * Start with the 'basic' privilege set and then remove any 277 * of the 'basic' privileges that will not be needed. 278 */ 279 if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) { 280 return; 281 } 282 283 /* Clear privileges we will not need from the 'basic' set */ 284 (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY); 285 (void) priv_delset(pPrivSet, PRIV_PROC_INFO); 286 (void) priv_delset(pPrivSet, PRIV_PROC_SESSION); 287 (void) priv_delset(pPrivSet, PRIV_PROC_EXEC); 288 (void) priv_delset(pPrivSet, PRIV_PROC_FORK); 289 290 /* for uscsi */ 291 (void) priv_addset(pPrivSet, PRIV_SYS_DEVICES); 292 293 294 /* to open logindevperm'd devices */ 295 (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ); 296 297 /* Set the permitted privilege set. */ 298 if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) { 299 return; 300 } 301 302 /* Clear the limit set. */ 303 if ((lPrivSet = priv_allocset()) == NULL) { 304 return; 305 } 306 307 priv_emptyset(lPrivSet); 308 309 if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) { 310 return; 311 } 312 313 priv_freeset(lPrivSet); 314 } 315 316 int 317 main (int argc, char *argv[]) 318 { 319 int fd, rfd; 320 int ret; 321 char *udi; 322 char *device_file, *raw_device_file; 323 char *devpath, *rdevpath; 324 boolean_t is_dos; 325 int dos_num; 326 LibHalContext *ctx = NULL; 327 DBusError error; 328 DBusConnection *conn; 329 char *parent_udi; 330 char *storage_device; 331 char *is_disc_str; 332 int fdc; 333 dbus_bool_t is_disc = FALSE; 334 dbus_bool_t is_floppy = FALSE; 335 unsigned int block_size; 336 dbus_uint64_t vol_size; 337 dbus_bool_t should_probe_for_fs; 338 char *partition_scheme = NULL; 339 dbus_uint64_t partition_start = 0; 340 int partition_number = 0; 341 struct vtoc vtoc; 342 dk_gpt_t *gpt; 343 struct dk_minfo mi; 344 int i, dos_cnt; 345 fstyp_handle_t fstyp_handle; 346 int systid, relsect, numsect; 347 off_t probe_offset = 0; 348 int num_volumes; 349 char **volumes; 350 dbus_uint64_t v_start; 351 const char *fstype; 352 nvlist_t *fsattr; 353 354 fd = rfd = -1; 355 356 ret = 1; 357 358 if ((udi = getenv ("UDI")) == NULL) { 359 goto out; 360 } 361 if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) { 362 goto out; 363 } 364 if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) { 365 goto out; 366 } 367 if (!dos_to_dev(device_file, &rdevpath, &dos_num)) { 368 rdevpath = raw_device_file; 369 } 370 if (!(is_dos = dos_to_dev(device_file, &devpath, &dos_num))) { 371 devpath = device_file; 372 } 373 if ((parent_udi = getenv ("HAL_PROP_INFO_PARENT")) == NULL) { 374 goto out; 375 } 376 if ((storage_device = getenv ("HAL_PROP_BLOCK_STORAGE_DEVICE")) == NULL) { 377 goto out; 378 } 379 380 is_disc_str = getenv ("HAL_PROP_VOLUME_IS_DISC"); 381 if (is_disc_str != NULL && strcmp (is_disc_str, "true") == 0) { 382 is_disc = TRUE; 383 } else { 384 is_disc = FALSE; 385 } 386 387 drop_privileges (); 388 389 setup_logger (); 390 391 dbus_error_init (&error); 392 if ((ctx = libhal_ctx_init_direct (&error)) == NULL) 393 goto out; 394 395 HAL_DEBUG (("Doing probe-volume for %s\n", device_file)); 396 397 fd = open (devpath, O_RDONLY | O_NONBLOCK); 398 if (fd < 0) { 399 goto out; 400 } 401 rfd = open (rdevpath, O_RDONLY | O_NONBLOCK); 402 if (rfd < 0) { 403 goto out; 404 } 405 406 /* if it's a floppy with no media, bail out */ 407 if (ioctl(rfd, FDGETCHANGE, &fdc) == 0) { 408 is_floppy = TRUE; 409 if (fdc & FDGC_CURRENT) { 410 goto out; 411 } 412 } 413 414 /* block size and total size */ 415 if (ioctl(rfd, DKIOCGMEDIAINFO, &mi) != -1) { 416 block_size = mi.dki_lbsize; 417 vol_size = mi.dki_capacity * block_size; 418 } else { 419 block_size = 512; 420 vol_size = 0; 421 } 422 libhal_device_set_property_int (ctx, udi, "volume.block_size", block_size, &error); 423 my_dbus_error_free (&error); 424 libhal_device_set_property_uint64 (ctx, udi, "volume.size", vol_size, &error); 425 my_dbus_error_free (&error); 426 427 should_probe_for_fs = TRUE; 428 429 if (is_disc) { 430 if (!probe_disc (rfd, ctx, udi, &should_probe_for_fs)) { 431 HAL_DEBUG (("probe_disc failed, skipping fstyp")); 432 goto out; 433 } 434 /* XXX vol_probe_offset for multisession discs? */ 435 } 436 437 if (!should_probe_for_fs) { 438 goto skip_fs; 439 } 440 441 /* don't support partitioned floppy */ 442 if (is_floppy) { 443 goto skip_part; 444 } 445 446 /* 447 * first get partitioning info 448 */ 449 if (is_dos) { 450 /* for a dos drive find partition offset */ 451 if (!find_dos_drive(fd, dos_num, &relsect, &numsect, &systid)) { 452 goto out; 453 } 454 partition_scheme = "mbr"; 455 partition_start = (dbus_uint64_t)relsect * 512; 456 partition_number = dos_num; 457 probe_offset = (off_t)relsect * 512; 458 } else { 459 if ((partition_number = read_vtoc(rfd, &vtoc)) >= 0) { 460 if (!vtoc_one_slice_entire_disk(&vtoc)) { 461 partition_scheme = "smi"; 462 if (partition_number < vtoc.v_nparts) { 463 if (vtoc.v_part[partition_number].p_size == 0) { 464 HAL_DEBUG (("zero size partition")); 465 } 466 partition_start = vtoc.v_part[partition_number].p_start * block_size; 467 } 468 } 469 } else if ((partition_number = efi_alloc_and_read(rfd, &gpt)) >= 0) { 470 partition_scheme = "gpt"; 471 if (partition_number < gpt->efi_nparts) { 472 if (gpt->efi_parts[partition_number].p_size == 0) { 473 HAL_DEBUG (("zero size partition")); 474 } 475 partition_start = gpt->efi_parts[partition_number].p_start * block_size; 476 } 477 efi_free(gpt); 478 } 479 probe_offset = 0; 480 } 481 482 if (partition_scheme != NULL) { 483 libhal_device_set_property_string (ctx, udi, "volume.partition.scheme", partition_scheme, &error); 484 my_dbus_error_free (&error); 485 libhal_device_set_property_int (ctx, udi, "volume.partition.number", partition_number, &error); 486 my_dbus_error_free (&error); 487 libhal_device_set_property_uint64 (ctx, udi, "volume.partition.start", partition_start, &error); 488 my_dbus_error_free (&error); 489 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", TRUE, &error); 490 my_dbus_error_free (&error); 491 } else { 492 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", FALSE, &error); 493 my_dbus_error_free (&error); 494 } 495 496 /* 497 * ignore duplicate partitions 498 */ 499 if ((volumes = libhal_manager_find_device_string_match ( 500 ctx, "block.storage_device", storage_device, &num_volumes, &error)) != NULL) { 501 my_dbus_error_free (&error); 502 for (i = 0; i < num_volumes; i++) { 503 if (strcmp (udi, volumes[i]) == 0) { 504 continue; /* skip self */ 505 } 506 v_start = libhal_device_get_property_uint64 (ctx, volumes[i], "volume.partition.start", &error); 507 if (dbus_error_is_set(&error)) { 508 dbus_error_free(&error); 509 continue; 510 } 511 if (v_start == partition_start) { 512 HAL_DEBUG (("duplicate partition")); 513 goto out; 514 } 515 } 516 libhal_free_string_array (volumes); 517 } 518 519 skip_part: 520 521 /* 522 * now determine fs type 523 */ 524 if (fstyp_init(fd, probe_offset, NULL, &fstyp_handle) != 0) { 525 HAL_DEBUG (("fstyp_init failed")); 526 goto out; 527 } 528 if ((fstyp_ident(fstyp_handle, NULL, &fstype) != 0) || 529 (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) { 530 HAL_DEBUG (("fstyp ident or get_attr failed")); 531 532 /* 533 * XXX fstyp_udfs has a bug that it only works on raw, 534 * but we don't want to slow down the fast path above. 535 * Try raw for just udfs here until the bug is fixed. 536 */ 537 HAL_DEBUG (("trying udfs workaround")); 538 fstyp_fini(fstyp_handle); 539 if (fstyp_init(rfd, probe_offset, NULL, &fstyp_handle) != 0) { 540 goto out; 541 } 542 if ((fstyp_ident(fstyp_handle, "udfs", &fstype) != 0) || 543 (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) { 544 fstyp_fini(fstyp_handle); 545 goto out; 546 } 547 } 548 set_fstyp_properties (ctx, udi, fstype, fsattr); 549 fstyp_fini(fstyp_handle); 550 551 skip_fs: 552 553 ret = 0; 554 555 out: 556 if (fd >= 0) 557 close (fd); 558 if (rfd >= 0) 559 close (rfd); 560 561 if (ctx != NULL) { 562 my_dbus_error_free (&error); 563 libhal_ctx_shutdown (ctx, &error); 564 libhal_ctx_free (ctx); 565 } 566 567 return ret; 568 569 } 570