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 my_dbus_error_free (&error);
365
366 return (TRUE);
367 }
368
369 static void
drop_privileges()370 drop_privileges ()
371 {
372 priv_set_t *pPrivSet = NULL;
373 priv_set_t *lPrivSet = NULL;
374
375 /*
376 * Start with the 'basic' privilege set and then remove any
377 * of the 'basic' privileges that will not be needed.
378 */
379 if ((pPrivSet = priv_str_to_set("basic", ",", NULL)) == NULL) {
380 return;
381 }
382
383 /* Clear privileges we will not need from the 'basic' set */
384 (void) priv_delset(pPrivSet, PRIV_FILE_LINK_ANY);
385 (void) priv_delset(pPrivSet, PRIV_PROC_INFO);
386 (void) priv_delset(pPrivSet, PRIV_PROC_SESSION);
387 (void) priv_delset(pPrivSet, PRIV_PROC_EXEC);
388 (void) priv_delset(pPrivSet, PRIV_PROC_FORK);
389
390 /* for uscsi */
391 (void) priv_addset(pPrivSet, PRIV_SYS_DEVICES);
392
393
394 /* to open logindevperm'd devices */
395 (void) priv_addset(pPrivSet, PRIV_FILE_DAC_READ);
396
397 /* Set the permitted privilege set. */
398 if (setppriv(PRIV_SET, PRIV_PERMITTED, pPrivSet) != 0) {
399 return;
400 }
401
402 /* Clear the limit set. */
403 if ((lPrivSet = priv_allocset()) == NULL) {
404 return;
405 }
406
407 priv_emptyset(lPrivSet);
408
409 if (setppriv(PRIV_SET, PRIV_LIMIT, lPrivSet) != 0) {
410 return;
411 }
412
413 priv_freeset(lPrivSet);
414 }
415
416 int
main(int argc,char * argv[])417 main (int argc, char *argv[])
418 {
419 int fd, rfd;
420 int ret;
421 char *udi;
422 char *device_file, *raw_device_file;
423 char *devpath, *rdevpath;
424 boolean_t is_dos;
425 int dos_num;
426 LibHalContext *ctx = NULL;
427 DBusError error;
428 DBusConnection *conn;
429 char *parent_udi;
430 char *storage_device;
431 char *is_disc_str;
432 int fdc;
433 dbus_bool_t is_disc = FALSE;
434 dbus_bool_t is_floppy = FALSE;
435 unsigned int block_size;
436 dbus_uint64_t vol_size;
437 dbus_bool_t has_data = TRUE; /* probe for fs by default */
438 dbus_bool_t has_audio = FALSE;
439 char *partition_scheme = NULL;
440 dbus_uint64_t partition_start = 0;
441 int partition_number = 0;
442 struct extvtoc vtoc;
443 dk_gpt_t *gpt;
444 struct dk_minfo mi;
445 int i, dos_cnt;
446 fstyp_handle_t fstyp_handle;
447 off_t probe_offset = 0;
448 int num_volumes;
449 char **volumes;
450 dbus_uint64_t v_start;
451 const char *fstype;
452 nvlist_t *fsattr;
453
454 fd = rfd = -1;
455
456 ret = 1;
457
458 if ((udi = getenv ("UDI")) == NULL) {
459 goto out;
460 }
461 if ((device_file = getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL) {
462 goto out;
463 }
464 if ((raw_device_file = getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL) {
465 goto out;
466 }
467 if (!dos_to_dev(raw_device_file, &rdevpath, &dos_num)) {
468 rdevpath = raw_device_file;
469 }
470 if (!(is_dos = dos_to_dev(device_file, &devpath, &dos_num))) {
471 devpath = device_file;
472 }
473 if ((parent_udi = getenv ("HAL_PROP_INFO_PARENT")) == NULL) {
474 goto out;
475 }
476 if ((storage_device = getenv ("HAL_PROP_BLOCK_STORAGE_DEVICE")) == NULL) {
477 goto out;
478 }
479
480 is_disc_str = getenv ("HAL_PROP_VOLUME_IS_DISC");
481 if (is_disc_str != NULL && strcmp (is_disc_str, "true") == 0) {
482 is_disc = TRUE;
483 } else {
484 is_disc = FALSE;
485 }
486
487 drop_privileges ();
488
489 setup_logger ();
490
491 dbus_error_init (&error);
492 if ((ctx = libhal_ctx_init_direct (&error)) == NULL)
493 goto out;
494
495 HAL_DEBUG (("Doing probe-volume for %s\n", device_file));
496
497 fd = open (devpath, O_RDONLY | O_NONBLOCK);
498 if (fd < 0) {
499 goto out;
500 }
501 rfd = open (rdevpath, O_RDONLY | O_NONBLOCK);
502 if (rfd < 0) {
503 goto out;
504 }
505
506 /* if it's a floppy with no media, bail out */
507 if (ioctl(rfd, FDGETCHANGE, &fdc) == 0) {
508 is_floppy = TRUE;
509 if (fdc & FDGC_CURRENT) {
510 goto out;
511 }
512 }
513
514 /* block size and total size */
515 if (ioctl(rfd, DKIOCGMEDIAINFO, &mi) != -1) {
516 block_size = mi.dki_lbsize;
517 vol_size = mi.dki_capacity * block_size;
518 } else if (errno == ENXIO) {
519 /* driver supports ioctl, but media is not available */
520 goto out;
521 } else {
522 /* driver does not support ioctl, e.g. lofi */
523 block_size = 512;
524 vol_size = 0;
525 }
526 libhal_device_set_property_int (ctx, udi, "volume.block_size", block_size, &error);
527 my_dbus_error_free (&error);
528 libhal_device_set_property_uint64 (ctx, udi, "volume.size", vol_size, &error);
529 my_dbus_error_free (&error);
530
531 if (is_disc) {
532 if (!probe_disc (rfd, ctx, udi, &has_data, &has_audio)) {
533 HAL_DEBUG (("probe_disc failed, skipping fstyp"));
534 goto out;
535 }
536 /* with audio present, create volume even if fs probing fails */
537 if (has_audio) {
538 ret = 0;
539 }
540 }
541
542 if (!has_data) {
543 goto skip_fs;
544 }
545
546 /* don't support partitioned floppy */
547 if (is_floppy) {
548 goto skip_part;
549 }
550
551 /*
552 * first get partitioning info
553 */
554 if (is_dos) {
555 /* for a dos drive find partition offset */
556 if (!find_dos_drive(fd, dos_num, block_size, &probe_offset)) {
557 goto out;
558 }
559 partition_scheme = "mbr";
560 partition_start = (dbus_uint64_t)probe_offset;
561 partition_number = dos_num;
562 } else {
563 if ((partition_number = read_extvtoc(rfd, &vtoc)) >= 0) {
564 if (!vtoc_one_slice_entire_disk(&vtoc)) {
565 partition_scheme = "smi";
566 if (partition_number < vtoc.v_nparts) {
567 if (vtoc.v_part[partition_number].p_size == 0) {
568 HAL_DEBUG (("zero size partition"));
569 }
570 partition_start = vtoc.v_part[partition_number].p_start * block_size;
571 }
572 }
573 } else if ((partition_number = efi_alloc_and_read(rfd, &gpt)) >= 0) {
574 partition_scheme = "gpt";
575 if (partition_number < gpt->efi_nparts) {
576 if (gpt->efi_parts[partition_number].p_size == 0) {
577 HAL_DEBUG (("zero size partition"));
578 }
579 partition_start = gpt->efi_parts[partition_number].p_start * block_size;
580 }
581 efi_free(gpt);
582 }
583 probe_offset = 0;
584 }
585
586 if (partition_scheme != NULL) {
587 libhal_device_set_property_string (ctx, udi, "volume.partition.scheme", partition_scheme, &error);
588 my_dbus_error_free (&error);
589 libhal_device_set_property_int (ctx, udi, "volume.partition.number", partition_number, &error);
590 my_dbus_error_free (&error);
591 libhal_device_set_property_uint64 (ctx, udi, "volume.partition.start", partition_start, &error);
592 my_dbus_error_free (&error);
593 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", TRUE, &error);
594 my_dbus_error_free (&error);
595 } else {
596 libhal_device_set_property_bool (ctx, udi, "volume.is_partition", FALSE, &error);
597 my_dbus_error_free (&error);
598 }
599
600 /*
601 * ignore duplicate partitions
602 */
603 if ((volumes = libhal_manager_find_device_string_match (
604 ctx, "block.storage_device", storage_device, &num_volumes, &error)) != NULL) {
605 my_dbus_error_free (&error);
606 for (i = 0; i < num_volumes; i++) {
607 if (strcmp (udi, volumes[i]) == 0) {
608 continue; /* skip self */
609 }
610 v_start = libhal_device_get_property_uint64 (ctx, volumes[i], "volume.partition.start", &error);
611 if (dbus_error_is_set(&error)) {
612 dbus_error_free(&error);
613 continue;
614 }
615 if (v_start == partition_start) {
616 HAL_DEBUG (("duplicate partition"));
617 goto out;
618 }
619 }
620 libhal_free_string_array (volumes);
621 }
622
623 skip_part:
624
625 /*
626 * now determine fs type
627 *
628 * XXX We could get better performance from block device,
629 * but for now we use raw device because:
630 *
631 * - fstyp_udfs has a bug that it only works on raw
632 *
633 * - sd has a bug that causes extremely slow reads
634 * and incorrect probing of hybrid audio/data media
635 */
636 if (fstyp_init(rfd, probe_offset, NULL, &fstyp_handle) != 0) {
637 HAL_DEBUG (("fstyp_init failed"));
638 goto out;
639 }
640 if ((fstyp_ident(fstyp_handle, NULL, &fstype) != 0) ||
641 (fstyp_get_attr(fstyp_handle, &fsattr) != 0)) {
642 HAL_DEBUG (("fstyp ident or get_attr failed"));
643 fstyp_fini(fstyp_handle);
644 goto out;
645 }
646 set_fstyp_properties (ctx, udi, fstype, fsattr);
647
648 if (strcmp (fstype, "hsfs") == 0) {
649 hsfs_contents (fd, probe_offset, ctx, udi);
650 }
651
652 fstyp_fini(fstyp_handle);
653
654 skip_fs:
655
656 ret = 0;
657
658 out:
659 if (fd >= 0)
660 close (fd);
661 if (rfd >= 0)
662 close (rfd);
663
664 if (ctx != NULL) {
665 my_dbus_error_free (&error);
666 libhal_ctx_shutdown (ctx, &error);
667 libhal_ctx_free (ctx);
668 }
669
670 return ret;
671
672 }
673