1 /***************************************************************************
2 *
3 * probe-volume.c : probe volumes
4 *
5 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
6 *
7 * Licensed under the Academic Free License version 2.1
8 *
9 **************************************************************************/
10
11 #ifdef HAVE_CONFIG_H
12 # include <config.h>
13 #endif
14
15 #include <errno.h>
16 #include <string.h>
17 #include <stdlib.h>
18 #include <stdio.h>
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <unistd.h>
24 #include <ctype.h>
25 #include <time.h>
26 #include <sys/time.h>
27 #include <sys/dkio.h>
28 #include <sys/cdio.h>
29 #include <sys/fdio.h>
30 #include <libnvpair.h>
31 #include <libfstyp.h>
32 #include <sys/vtoc.h>
33 #include <sys/efi_partition.h>
34 #include <sys/fs/hsfs_spec.h>
35 #include <sys/fs/hsfs_isospec.h>
36 #include <priv.h>
37 #include <sys/u8_textprep.h>
38
39 #include <libhal.h>
40 #include <cdutils.h>
41 #include <fsutils.h>
42 #include <logger.h>
43
44 static void
my_dbus_error_free(DBusError * error)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 *
rtrim_copy(char * src,int len)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
set_fstyp_properties(LibHalContext * ctx,const char * udi,const char * fstype,nvlist_t * fsattr)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 int err;
83 LibHalChangeSet *cs;
84
85 dbus_error_init (&error);
86
87 if ((cs = libhal_device_new_changeset (udi)) == NULL) {
88 return;
89 }
90
91 libhal_changeset_set_property_string (cs, "volume.fsusage", "filesystem");
92 libhal_changeset_set_property_string (cs, "volume.fstype", fstype);
93
94 /* label */
95 (void) nvlist_lookup_string(fsattr, "gen_volume_label", &label_orig);
96 if (label_orig != NULL) {
97 label = rtrim_copy(label_orig, 0);
98 }
99 /* Check if label is utf8 format */
100 if ((label != NULL) && (label[0] != '\0') &&
101 (u8_validate(label, strlen(label), (char **)NULL,
102 U8_VALIDATE_ENTIRE, &err) != -1)) {
103 libhal_changeset_set_property_string (cs, "volume.label", label);
104 libhal_changeset_set_property_string (cs, "info.product", label);
105 } else {
106 libhal_changeset_set_property_string (cs, "volume.label", "");
107 snprintf (buf, sizeof (buf), "Volume (%s)", fstype);
108 libhal_changeset_set_property_string (cs, "info.product", buf);
109 }
110 free(label);
111
112 /* uuid */
113 if (nvlist_lookup_string(fsattr, "gen_uuid", &uuid) == 0) {
114 libhal_changeset_set_property_string (cs, "volume.uuid", uuid);
115 } else {
116 libhal_changeset_set_property_string (cs, "volume.uuid", "");
117 }
118
119 libhal_device_commit_changeset (ctx, cs, &error);
120 libhal_device_free_changeset (cs);
121
122 my_dbus_error_free (&error);
123 }
124
125 /*
126 * hsfs/iso9660 contents detection: Video DVD, Video CD, etc.
127 */
128 static void
hsfs_contents(int fd,off_t probe_offset,LibHalContext * ctx,const char * udi)129 hsfs_contents(int fd, off_t probe_offset, LibHalContext *ctx, const char *udi)
130 {
131 size_t secsz = ISO_SECTOR_SIZE;
132 uchar_t buf[ISO_SECTOR_SIZE];
133 int ptbl_lbn, ptbl_size;
134 int off, reloff, readoff;
135 uchar_t *p;
136 char *name;
137 int name_len;
138 int ipe_len;
139 DBusError error;
140
141 /*
142 * find 1st Primary Volume Descriptor
143 */
144 readoff = probe_offset + ISO_VOLDESC_SEC * secsz;
145 if (pread (fd, buf, secsz, readoff) != secsz) {
146 return;
147 }
148 while (ISO_DESC_TYPE (buf) != ISO_VD_PVD) {
149 if (ISO_DESC_TYPE (buf) == ISO_VD_EOV) {
150 return;
151 }
152 readoff += secsz;
153 if (pread (fd, buf, secsz, readoff) != secsz) {
154 return;
155 }
156 }
157
158 /*
159 * PVD contains size and offset of the LSB/MSB path table
160 */
161 ptbl_size = ISO_PTBL_SIZE (buf);
162 #if defined(_LITTLE_ENDIAN)
163 ptbl_lbn = ISO_PTBL_MAN_LS (buf);
164 #else
165 ptbl_lbn = ISO_PTBL_MAN_MS (buf);
166 #endif
167
168 /*
169 * Look through path table entries
170 */
171 readoff = probe_offset + ptbl_lbn * secsz;
172 if (pread (fd, buf, secsz, readoff) != secsz) {
173 return;
174 }
175 dbus_error_init (&error);
176
177 for (off = reloff = 0;
178 off < ptbl_size;
179 off += ipe_len, reloff += ipe_len) {
180
181 /* load sectors on demand */
182 if (reloff >= secsz) {
183 readoff += secsz;
184 if (pread (fd, buf, secsz, readoff) != secsz) {
185 break;
186 }
187 reloff -= secsz;
188 }
189
190 p = buf + reloff;
191 name_len = IPE_NAME_LEN(p);
192 ipe_len = IPE_FPESIZE + name_len + (name_len % 2);
193
194 /* only interested in root directories */
195 if (IPE_PARENT_NO (p) != 1) {
196 continue;
197 }
198 if ((name_len < 2) || (name_len > IDE_MAX_NAME_LEN)) {
199 continue;
200 }
201
202 name = (char *)IPE_NAME (p);
203 if (strncasecmp (name, "VIDEO_TS", min (8, name_len)) == 0) {
204 libhal_device_set_property_bool (ctx, udi,
205 "volume.disc.is_videodvd", TRUE, &error);
206 } else if (strncasecmp (name, "VCD", min (3, name_len)) == 0) {
207 libhal_device_set_property_bool (ctx, udi,
208 "volume.disc.is_vcd", TRUE, &error);
209 } else if (strncasecmp (name, "SVCD", min (4, name_len)) == 0) {
210 libhal_device_set_property_bool (ctx, udi,
211 "volume.disc.is_svcd", TRUE, &error);
212 }
213 }
214
215 my_dbus_error_free (&error);
216 }
217
218 static dbus_bool_t
probe_disc(int fd,LibHalContext * ctx,const char * udi,dbus_bool_t * has_data,dbus_bool_t * has_audio)219 probe_disc (int fd, LibHalContext *ctx, const char *udi, dbus_bool_t *has_data,
220 dbus_bool_t *has_audio)
221 {
222 DBusError error;
223 disc_info_t di;
224 int profile;
225 dbus_bool_t is_blank, is_appendable, is_rewritable;
226 char *disc_type = "cd_rom";
227 uint64_t capacity = 0;
228 int i;
229 LibHalChangeSet *cs;
230
231 dbus_error_init (&error);
232
233 if (get_disc_info (fd, &di)) {
234 is_blank = (di.disc_status == 0);
235 is_appendable = (di.disc_status == 1);
236 is_rewritable = (di.erasable != 0);
237 } else {
238 is_blank = is_appendable = is_rewritable = FALSE;
239 }
240
241 if (get_current_profile (fd, &profile)) {
242 switch (profile) {
243 case 0x08: /* CD-ROM */
244 disc_type = "cd_rom";
245 break;
246 case 0x09: /* CD-R */
247 disc_type = "cd_r";
248 break;
249 case 0x0A: /* CD-RW */
250 disc_type = "cd_rw";
251 is_rewritable = TRUE;
252 break;
253 case 0x10: /* DVD-ROM */
254 disc_type = "dvd_rom";
255 break;
256 case 0x11: /* DVD-R Sequential */
257 disc_type = "dvd_r";
258 break;
259 case 0x12: /* DVD-RAM */
260 disc_type = "dvd_ram";
261 is_rewritable = TRUE;
262 break;
263 case 0x13: /* DVD-RW Restricted Overwrite */
264 disc_type = "dvd_rw";
265 is_rewritable = TRUE;
266 break;
267 case 0x14: /* DVD-RW Sequential */
268 disc_type = "dvd_rw";
269 is_rewritable = TRUE;
270 break;
271 case 0x1A: /* DVD+RW */
272 disc_type = "dvd_plus_rw";
273 is_rewritable = TRUE;
274 break;
275 case 0x1B: /* DVD+R */
276 disc_type = "dvd_plus_r";
277 break;
278 case 0x2B: /* DVD+R Double Layer */
279 disc_type = "dvd_plus_r_dl";
280 break;
281 case 0x40: /* BD-ROM */
282 disc_type = "bd_rom";
283 break;
284 case 0x41: /* BD-R Sequential */
285 disc_type = "bd_r";
286 break;
287 case 0x42: /* BD-R Random */
288 disc_type = "bd_r";
289 break;
290 case 0x43: /* BD-RE */
291 disc_type = "bd_re";
292 is_rewritable = TRUE;
293 break;
294 case 0x50: /* HD DVD-ROM */
295 disc_type = "hddvd_rom";
296 break;
297 case 0x51: /* HD DVD-R */
298 disc_type = "hddvd_r";
299 break;
300 case 0x52: /* HD DVD-Rewritable */
301 disc_type = "hddvd_rw";
302 is_rewritable = TRUE;
303 break;
304 }
305
306 (void) get_disc_capacity_for_profile(fd, profile, &capacity);
307 }
308
309 *has_audio = *has_data = FALSE;
310 if (!is_blank) {
311 uchar_t smalltoc[12];
312 size_t toc_size;
313 uchar_t *toc, *p;
314
315 /*
316 * XXX for some reason CDROMREADTOCENTRY fails on video DVDs,
317 * but extracting the toc directly works okay. And the toc
318 * data buffer length passed to read_toc() should be the same
319 * as the real buffer size.
320 */
321 if (!read_toc(fd, 0, 1, 12, smalltoc)) {
322 HAL_DEBUG(("read_toc failed"));
323 *has_data = B_TRUE; /* probe for fs anyway */
324 } else {
325 toc_size = smalltoc[0] * 256 + smalltoc[1] + 2;
326 toc = (uchar_t *)calloc(1, toc_size);
327 if (toc == NULL || !read_toc(fd, 0, 1, toc_size, toc)) {
328 HAL_DEBUG (("read_toc again failed"));
329 } else {
330 for (p = &toc[4]; p < (toc + toc_size); p += 8) {
331 /* skip leadout */
332 if (p[2] == 0xAA) {
333 continue;
334 }
335 if (p[1] & 4) {
336 *has_data = B_TRUE;
337 } else {
338 *has_audio = B_TRUE;
339 }
340 }
341 }
342 free(toc);
343 }
344 }
345
346 if ((cs = libhal_device_new_changeset (udi)) == NULL) {
347 return (FALSE);
348 }
349 libhal_changeset_set_property_string (cs, "volume.disc.type", disc_type);
350 libhal_changeset_set_property_bool (cs, "volume.disc.is_blank", is_blank);
351 libhal_changeset_set_property_bool (cs, "volume.disc.has_audio", *has_audio);
352 libhal_changeset_set_property_bool (cs, "volume.disc.has_data", *has_data);
353 libhal_changeset_set_property_bool (cs, "volume.disc.is_appendable", is_appendable);
354 libhal_changeset_set_property_bool (cs, "volume.disc.is_rewritable", is_rewritable);
355 libhal_changeset_set_property_uint64 (cs, "volume.disc.capacity", capacity);
356
357 libhal_changeset_set_property_bool (cs, "volume.disc.is_videodvd", FALSE);
358 libhal_changeset_set_property_bool (cs, "volume.disc.is_vcd", FALSE);
359 libhal_changeset_set_property_bool (cs, "volume.disc.is_svcd", FALSE);
360
361 libhal_device_commit_changeset (ctx, cs, &error);
362 libhal_device_free_changeset (cs);
363
364 out:
365 my_dbus_error_free (&error);
366
367 return (TRUE);
368 }
369
370 static void
drop_privileges()371 drop_privileges ()
372 {
373 priv_set_t *pPrivSet = NULL;
374 priv_set_t *lPrivSet = NULL;
375
376 /*
377 * Start with the 'basic' privilege set and then remove any
378 * of the 'basic' privileges that will not be needed.
379 */
380 if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
381 return;
382 }
383
384 /* Clear privileges we will not need from the 'basic' set */
385 (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
386 (void) priv_delset(pPrivSet, PRIV_PROC_INFO);
387 (void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
388 (void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
389 (void) priv_delset(pPrivSet, PRIV_PROC_FORK);
390
391 /* for uscsi */
392 (void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
393
394
395 /* to open logindevperm'd devices */
396 (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
397
398 /* Set the permitted privilege set. */
399 if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
400 return;
401 }
402
403 /* Clear the limit set. */
404 if ((lPrivSet = priv_allocset()) == NULL) {
405 return;
406 }
407
408 priv_emptyset(lPrivSet);
409
410 if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
411 return;
412 }
413
414 priv_freeset(lPrivSet);
415 }
416
417 int
main(int argc,char * argv[])418 main (int argc, char *argv[])
419 {
420 int fd, rfd;
421 int ret;
422 char *udi;
423 char *device_file, *raw_device_file;
424 char *devpath, *rdevpath;
425 boolean_t is_dos;
426 int dos_num;
427 LibHalContext *ctx = NULL;
428 DBusError error;
429 DBusConnection *conn;
430 char *parent_udi;
431 char *storage_device;
432 char *is_disc_str;
433 int fdc;
434 dbus_bool_t is_disc = FALSE;
435 dbus_bool_t is_floppy = FALSE;
436 unsigned int block_size;
437 dbus_uint64_t vol_size;
438 dbus_bool_t has_data = TRUE; /* probe for fs by default */
439 dbus_bool_t has_audio = FALSE;
440 char *partition_scheme = NULL;
441 dbus_uint64_t partition_start = 0;
442 int partition_number = 0;
443 struct extvtoc vtoc;
444 dk_gpt_t *gpt;
445 struct dk_minfo mi;
446 int i, dos_cnt;
447 fstyp_handle_t fstyp_handle;
448 off_t probe_offset = 0;
449 int num_volumes;
450 char **volumes;
451 dbus_uint64_t v_start;
452 const char *fstype;
453 nvlist_t *fsattr;
454
455 fd = rfd = -1;
456
457 ret = 1;
458
459 if ((udi = getenv ("UDI")) == NULL) {
460 goto out;
461 }
462 if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) {
463 goto out;
464 }
465 if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) {
466 goto out;
467 }
468 if (!dos_to_dev(raw_device_file, &rdevpath, &dos_num)) {
469 rdevpath = raw_device_file;
470 }
471 if (!(is_dos = dos_to_dev(device_file, &devpath, &dos_num))) {
472 devpath = device_file;
473 }
474 if ((parent_udi = getenv ("HAL_PROP_INFO_PARENT")) == NULL) {
475 goto out;
476 }
477 if ((storage_device = getenv ("HAL_PROP_BLOCK_STORAGE_DEVICE")) == NULL) {
478 goto out;
479 }
480
481 is_disc_str = getenv ("HAL_PROP_VOLUME_IS_DISC");
482 if (is_disc_str != NULL && strcmp (is_disc_str, "true") == 0) {
483 is_disc = TRUE;
484 } else {
485 is_disc = FALSE;
486 }
487
488 drop_privileges ();
489
490 setup_logger ();
491
492 dbus_error_init (&error);
493 if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
494 goto out;
495
496 HAL_DEBUG (("Doing probe-volume for %s\n", device_file));
497
498 fd = open (devpath, O_RDONLY | O_NONBLOCK);
499 if (fd < 0) {
500 goto out;
501 }
502 rfd = open (rdevpath, O_RDONLY | O_NONBLOCK);
503 if (rfd < 0) {
504 goto out;
505 }
506
507 /* if it's a floppy with no media, bail out */
508 if (ioctl(rfd, FDGETCHANGE, &fdc) == 0) {
509 is_floppy = TRUE;
510 if (fdc & FDGC_CURRENT) {
511 goto out;
512 }
513 }
514
515 /* block size and total size */
516 if (ioctl(rfd, DKIOCGMEDIAINFO, &mi) != -1) {
517 block_size = mi.dki_lbsize;
518 vol_size = mi.dki_capacity * block_size;
519 } else if (errno == ENXIO) {
520 /* driver supports ioctl, but media is not available */
521 goto out;
522 } else {
523 /* driver does not support ioctl, e.g. lofi */
524 block_size = 512;
525 vol_size = 0;
526 }
527 libhal_device_set_property_int (ctx, udi, "volume.block_size", block_size, &error);
528 my_dbus_error_free (&error);
529 libhal_device_set_property_uint64 (ctx, udi, "volume.size", vol_size, &error);
530 my_dbus_error_free (&error);
531
532 if (is_disc) {
533 if (!probe_disc (rfd, ctx, udi, &has_data, &has_audio)) {
534 HAL_DEBUG (("probe_disc failed, skipping fstyp"));
535 goto out;
536 }
537 /* with audio present, create volume even if fs probing fails */
538 if (has_audio) {
539 ret = 0;
540 }
541 }
542
543 if (!has_data) {
544 goto skip_fs;
545 }
546
547 /* don't support partitioned floppy */
548 if (is_floppy) {
549 goto skip_part;
550 }
551
552 /*
553 * first get partitioning info
554 */
555 if (is_dos) {
556 /* for a dos drive find partition offset */
557 if (!find_dos_drive(fd, dos_num, block_size, &probe_offset)) {
558 goto out;
559 }
560 partition_scheme = "mbr";
561 partition_start = (dbus_uint64_t)probe_offset;
562 partition_number = dos_num;
563 } else {
564 if ((partition_number = read_extvtoc(rfd, &vtoc)) >= 0) {
565 if (!vtoc_one_slice_entire_disk(&vtoc)) {
566 partition_scheme = "smi";
567 if (partition_number < vtoc.v_nparts) {
568 if (vtoc.v_part[partition_number].p_size == 0) {
569 HAL_DEBUG (("zero size partition"));
570 }
571 partition_start = vtoc.v_part[partition_number].p_start * block_size;
572 }
573 }
574 } else if ((partition_number = efi_alloc_and_read(rfd, &gpt)) >= 0) {
575 partition_scheme = "gpt";
576 if (partition_number < gpt->efi_nparts) {
577 if (gpt->efi_parts[partition_number].p_size == 0) {
578 HAL_DEBUG (("zero size partition"));
579 }
580 partition_start = gpt->efi_parts[partition_number].p_start * block_size;
581 }
582 efi_free(gpt);
583 }
584 probe_offset = 0;
585 }
586
587 if (partition_scheme != NULL) {
588 libhal_device_set_property_string (ctx, udi, "volume.partition.scheme", partition_scheme, &error);
589 my_dbus_error_free (&error);
590 libhal_device_set_property_int (ctx, udi, "volume.partition.number", partition_number, &error);
591 my_dbus_error_free (&error);
592 libhal_device_set_property_uint64 (ctx, udi, "volume.partition.start", partition_start, &error);
593 my_dbus_error_free (&error);
594 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", TRUE, &error);
595 my_dbus_error_free (&error);
596 } else {
597 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", FALSE, &error);
598 my_dbus_error_free (&error);
599 }
600
601 /*
602 * ignore duplicate partitions
603 */
604 if ((volumes = libhal_manager_find_device_string_match (
605 ctx, "block.storage_device", storage_device, &num_volumes, &error)) != NULL) {
606 my_dbus_error_free (&error);
607 for (i = 0; i < num_volumes; i++) {
608 if (strcmp (udi, volumes[i]) == 0) {
609 continue; /* skip self */
610 }
611 v_start = libhal_device_get_property_uint64 (ctx, volumes[i], "volume.partition.start", &error);
612 if (dbus_error_is_set(&error)) {
613 dbus_error_free(&error);
614 continue;
615 }
616 if (v_start == partition_start) {
617 HAL_DEBUG (("duplicate partition"));
618 goto out;
619 }
620 }
621 libhal_free_string_array (volumes);
622 }
623
624 skip_part:
625
626 /*
627 * now determine fs type
628 *
629 * XXX We could get better performance from block device,
630 * but for now we use raw device because:
631 *
632 * - fstyp_udfs has a bug that it only works on raw
633 *
634 * - sd has a bug that causes extremely slow reads
635 * and incorrect probing of hybrid audio/data media
636 */
637 if (fstyp_init(rfd, probe_offset, NULL, &fstyp_handle) != 0) {
638 HAL_DEBUG (("fstyp_init failed"));
639 goto out;
640 }
641 if ((fstyp_ident(fstyp_handle, NULL, &fstype) != 0) ||
642 (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) {
643 HAL_DEBUG (("fstyp ident or get_attr failed"));
644 fstyp_fini(fstyp_handle);
645 goto out;
646 }
647 set_fstyp_properties (ctx, udi, fstype, fsattr);
648
649 if (strcmp (fstype, "hsfs") == 0) {
650 hsfs_contents (fd, probe_offset, ctx, udi);
651 }
652
653 fstyp_fini(fstyp_handle);
654
655 skip_fs:
656
657 ret = 0;
658
659 out:
660 if (fd >= 0)
661 close (fd);
662 if (rfd >= 0)
663 close (rfd);
664
665 if (ctx != NULL) {
666 my_dbus_error_free (&error);
667 libhal_ctx_shutdown (ctx, &error);
668 libhal_ctx_free (ctx);
669 }
670
671 return ret;
672
673 }
674