1 /***************************************************************************
3 * probe-volume.c : probe volumes
5 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
7 * Licensed under the Academic Free License version 2.1
9 **************************************************************************/
19 #include <sys/ioctl.h>
20 #include <sys/types.h>
30 #include <libnvpair.h>
33 #include <sys/efi_partition.h>
34 #include <sys/fs/hsfs_spec.h>
35 #include <sys/fs/hsfs_isospec.h>
37 #include <sys/u8_textprep.h>
45 my_dbus_error_free(DBusError
*error
)
47 if (dbus_error_is_set(error
)) {
48 dbus_error_free(error
);
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.
57 rtrim_copy(char *src
, int len
)
64 if ((dst
= calloc(1, len
+ 1)) != NULL
) {
65 strncpy(dst
, src
, len
);
67 while ((p
>= dst
) && (isspace(*p
))) {
75 set_fstyp_properties (LibHalContext
*ctx
, const char *udi
, const char *fstype
, nvlist_t
*fsattr
)
80 char *label_orig
= NULL
;
85 dbus_error_init (&error
);
87 if ((cs
= libhal_device_new_changeset (udi
)) == NULL
) {
91 libhal_changeset_set_property_string (cs
, "volume.fsusage", "filesystem");
92 libhal_changeset_set_property_string (cs
, "volume.fstype", fstype
);
95 (void) nvlist_lookup_string(fsattr
, "gen_volume_label", &label_orig
);
96 if (label_orig
!= NULL
) {
97 label
= rtrim_copy(label_orig
, 0);
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
);
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
);
113 if (nvlist_lookup_string(fsattr
, "gen_uuid", &uuid
) == 0) {
114 libhal_changeset_set_property_string (cs
, "volume.uuid", uuid
);
116 libhal_changeset_set_property_string (cs
, "volume.uuid", "");
119 libhal_device_commit_changeset (ctx
, cs
, &error
);
120 libhal_device_free_changeset (cs
);
122 my_dbus_error_free (&error
);
126 * hsfs/iso9660 contents detection: Video DVD, Video CD, etc.
129 hsfs_contents(int fd
, off_t probe_offset
, LibHalContext
*ctx
, const char *udi
)
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
;
142 * find 1st Primary Volume Descriptor
144 readoff
= probe_offset
+ ISO_VOLDESC_SEC
* secsz
;
145 if (pread (fd
, buf
, secsz
, readoff
) != secsz
) {
148 while (ISO_DESC_TYPE (buf
) != ISO_VD_PVD
) {
149 if (ISO_DESC_TYPE (buf
) == ISO_VD_EOV
) {
153 if (pread (fd
, buf
, secsz
, readoff
) != secsz
) {
159 * PVD contains size and offset of the LSB/MSB path table
161 ptbl_size
= ISO_PTBL_SIZE (buf
);
162 #if defined(_LITTLE_ENDIAN)
163 ptbl_lbn
= ISO_PTBL_MAN_LS (buf
);
165 ptbl_lbn
= ISO_PTBL_MAN_MS (buf
);
169 * Look through path table entries
171 readoff
= probe_offset
+ ptbl_lbn
* secsz
;
172 if (pread (fd
, buf
, secsz
, readoff
) != secsz
) {
175 dbus_error_init (&error
);
177 for (off
= reloff
= 0;
179 off
+= ipe_len
, reloff
+= ipe_len
) {
181 /* load sectors on demand */
182 if (reloff
>= secsz
) {
184 if (pread (fd
, buf
, secsz
, readoff
) != secsz
) {
191 name_len
= IPE_NAME_LEN(p
);
192 ipe_len
= IPE_FPESIZE
+ name_len
+ (name_len
% 2);
194 /* only interested in root directories */
195 if (IPE_PARENT_NO (p
) != 1) {
198 if ((name_len
< 2) || (name_len
> IDE_MAX_NAME_LEN
)) {
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
);
215 my_dbus_error_free (&error
);
219 probe_disc (int fd
, LibHalContext
*ctx
, const char *udi
, dbus_bool_t
*has_data
,
220 dbus_bool_t
*has_audio
)
225 dbus_bool_t is_blank
, is_appendable
, is_rewritable
;
226 char *disc_type
= "cd_rom";
227 uint64_t capacity
= 0;
231 dbus_error_init (&error
);
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);
238 is_blank
= is_appendable
= is_rewritable
= FALSE
;
241 if (get_current_profile (fd
, &profile
)) {
243 case 0x08: /* CD-ROM */
244 disc_type
= "cd_rom";
246 case 0x09: /* CD-R */
249 case 0x0A: /* CD-RW */
251 is_rewritable
= TRUE
;
253 case 0x10: /* DVD-ROM */
254 disc_type
= "dvd_rom";
256 case 0x11: /* DVD-R Sequential */
259 case 0x12: /* DVD-RAM */
260 disc_type
= "dvd_ram";
261 is_rewritable
= TRUE
;
263 case 0x13: /* DVD-RW Restricted Overwrite */
264 disc_type
= "dvd_rw";
265 is_rewritable
= TRUE
;
267 case 0x14: /* DVD-RW Sequential */
268 disc_type
= "dvd_rw";
269 is_rewritable
= TRUE
;
271 case 0x1A: /* DVD+RW */
272 disc_type
= "dvd_plus_rw";
273 is_rewritable
= TRUE
;
275 case 0x1B: /* DVD+R */
276 disc_type
= "dvd_plus_r";
278 case 0x2B: /* DVD+R Double Layer */
279 disc_type
= "dvd_plus_r_dl";
281 case 0x40: /* BD-ROM */
282 disc_type
= "bd_rom";
284 case 0x41: /* BD-R Sequential */
287 case 0x42: /* BD-R Random */
290 case 0x43: /* BD-RE */
292 is_rewritable
= TRUE
;
294 case 0x50: /* HD DVD-ROM */
295 disc_type
= "hddvd_rom";
297 case 0x51: /* HD DVD-R */
298 disc_type
= "hddvd_r";
300 case 0x52: /* HD DVD-Rewritable */
301 disc_type
= "hddvd_rw";
302 is_rewritable
= TRUE
;
306 (void) get_disc_capacity_for_profile(fd
, profile
, &capacity
);
309 *has_audio
= *has_data
= FALSE
;
311 uchar_t smalltoc
[12];
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.
321 if (!read_toc(fd
, 0, 1, 12, smalltoc
)) {
322 HAL_DEBUG(("read_toc failed"));
323 *has_data
= B_TRUE
; /* probe for fs anyway */
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"));
330 for (p
= &toc
[4]; p
< (toc
+ toc_size
); p
+= 8) {
346 if ((cs
= libhal_device_new_changeset (udi
)) == NULL
) {
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
);
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
);
361 libhal_device_commit_changeset (ctx
, cs
, &error
);
362 libhal_device_free_changeset (cs
);
365 my_dbus_error_free (&error
);
373 priv_set_t
*pPrivSet
= NULL
;
374 priv_set_t
*lPrivSet
= NULL
;
377 * Start with the 'basic' privilege set and then remove any
378 * of the 'basic' privileges that will not be needed.
380 if ((pPrivSet
= priv_str_to_set("basic", ",", NULL
)) == NULL
) {
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
);
392 (void) priv_addset(pPrivSet
, PRIV_SYS_DEVICES
);
395 /* to open logindevperm'd devices */
396 (void) priv_addset(pPrivSet
, PRIV_FILE_DAC_READ
);
398 /* Set the permitted privilege set. */
399 if (setppriv(PRIV_SET
, PRIV_PERMITTED
, pPrivSet
) != 0) {
403 /* Clear the limit set. */
404 if ((lPrivSet
= priv_allocset()) == NULL
) {
408 priv_emptyset(lPrivSet
);
410 if (setppriv(PRIV_SET
, PRIV_LIMIT
, lPrivSet
) != 0) {
414 priv_freeset(lPrivSet
);
418 main (int argc
, char *argv
[])
423 char *device_file
, *raw_device_file
;
424 char *devpath
, *rdevpath
;
427 LibHalContext
*ctx
= NULL
;
429 DBusConnection
*conn
;
431 char *storage_device
;
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;
447 fstyp_handle_t fstyp_handle
;
448 off_t probe_offset
= 0;
451 dbus_uint64_t v_start
;
459 if ((udi
= getenv ("UDI")) == NULL
) {
462 if ((device_file
= getenv ("HAL_PROP_BLOCK_DEVICE")) == NULL
) {
465 if ((raw_device_file
= getenv ("HAL_PROP_BLOCK_SOLARIS_RAW_DEVICE")) == NULL
) {
468 if (!dos_to_dev(raw_device_file
, &rdevpath
, &dos_num
)) {
469 rdevpath
= raw_device_file
;
471 if (!(is_dos
= dos_to_dev(device_file
, &devpath
, &dos_num
))) {
472 devpath
= device_file
;
474 if ((parent_udi
= getenv ("HAL_PROP_INFO_PARENT")) == NULL
) {
477 if ((storage_device
= getenv ("HAL_PROP_BLOCK_STORAGE_DEVICE")) == NULL
) {
481 is_disc_str
= getenv ("HAL_PROP_VOLUME_IS_DISC");
482 if (is_disc_str
!= NULL
&& strcmp (is_disc_str
, "true") == 0) {
492 dbus_error_init (&error
);
493 if ((ctx
= libhal_ctx_init_direct (&error
)) == NULL
)
496 HAL_DEBUG (("Doing probe-volume for %s\n", device_file
));
498 fd
= open (devpath
, O_RDONLY
| O_NONBLOCK
);
502 rfd
= open (rdevpath
, O_RDONLY
| O_NONBLOCK
);
507 /* if it's a floppy with no media, bail out */
508 if (ioctl(rfd
, FDGETCHANGE
, &fdc
) == 0) {
510 if (fdc
& FDGC_CURRENT
) {
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 */
523 /* driver does not support ioctl, e.g. lofi */
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
);
533 if (!probe_disc (rfd
, ctx
, udi
, &has_data
, &has_audio
)) {
534 HAL_DEBUG (("probe_disc failed, skipping fstyp"));
537 /* with audio present, create volume even if fs probing fails */
547 /* don't support partitioned floppy */
553 * first get partitioning info
556 /* for a dos drive find partition offset */
557 if (!find_dos_drive(fd
, dos_num
, block_size
, &probe_offset
)) {
560 partition_scheme
= "mbr";
561 partition_start
= (dbus_uint64_t
)probe_offset
;
562 partition_number
= dos_num
;
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"));
571 partition_start
= vtoc
.v_part
[partition_number
].p_start
* block_size
;
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"));
580 partition_start
= gpt
->efi_parts
[partition_number
].p_start
* block_size
;
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
);
597 libhal_device_set_property_bool (ctx
, udi
, "volume.is_partition", FALSE
, &error
);
598 my_dbus_error_free (&error
);
602 * ignore duplicate partitions
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 */
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
);
616 if (v_start
== partition_start
) {
617 HAL_DEBUG (("duplicate partition"));
621 libhal_free_string_array (volumes
);
627 * now determine fs type
629 * XXX We could get better performance from block device,
630 * but for now we use raw device because:
632 * - fstyp_udfs has a bug that it only works on raw
634 * - sd has a bug that causes extremely slow reads
635 * and incorrect probing of hybrid audio/data media
637 if (fstyp_init(rfd
, probe_offset
, NULL
, &fstyp_handle
) != 0) {
638 HAL_DEBUG (("fstyp_init failed"));
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
);
647 set_fstyp_properties (ctx
, udi
, fstype
, fsattr
);
649 if (strcmp (fstype
, "hsfs") == 0) {
650 hsfs_contents (fd
, probe_offset
, ctx
, udi
);
653 fstyp_fini(fstyp_handle
);
666 my_dbus_error_free (&error
);
667 libhal_ctx_shutdown (ctx
, &error
);
668 libhal_ctx_free (ctx
);