xref: /illumos-gate/usr/src/cmd/hal/probing/volume/probe-volume.c (revision 9e26e16f703d2dfcc0689de957c21efcb72473e6)
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