xref: /illumos-gate/usr/src/cmd/hal/hald/solaris/devinfo_storage.c (revision 00687e57f8c568d4f8fb446b6530a2942842292f)
1 /***************************************************************************
2  *
3  * devinfo_storage.c : storage devices
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 <stdio.h>
19 #include <string.h>
20 #include <strings.h>
21 #include <ctype.h>
22 #include <libdevinfo.h>
23 #include <sys/types.h>
24 #include <sys/mkdev.h>
25 #include <sys/stat.h>
26 #include <sys/mntent.h>
27 #include <sys/mnttab.h>
28 
29 #include "../osspec.h"
30 #include "../logger.h"
31 #include "../hald.h"
32 #include "../hald_dbus.h"
33 #include "../device_info.h"
34 #include "../util.h"
35 #include "../hald_runner.h"
36 #include "hotplug.h"
37 #include "devinfo.h"
38 #include "devinfo_misc.h"
39 #include "devinfo_storage.h"
40 #include "osspec_solaris.h"
41 
42 #ifdef sparc
43 #define	WHOLE_DISK	"s2"
44 #else
45 #define	WHOLE_DISK	"p0"
46 #endif
47 
48 /* some devices,especially CDROMs, may take a while to be probed (values in ms) */
49 #define	DEVINFO_PROBE_STORAGE_TIMEOUT	60000
50 #define	DEVINFO_PROBE_VOLUME_TIMEOUT	60000
51 
52 typedef struct devinfo_storage_minor {
53 	char	*devpath;
54 	char	*devlink;
55 	char	*slice;
56 	dev_t	dev;
57 	int	dosnum;	/* dos disk number or -1 */
58 } devinfo_storage_minor_t;
59 
60 HalDevice *devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
61 static HalDevice *devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path);
62 static HalDevice *devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path);
63 static HalDevice *devinfo_ide_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
64 HalDevice *devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
65 static HalDevice *devinfo_scsi_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
66 HalDevice *devinfo_pcata_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
67 static HalDevice *devinfo_pcata_storage_add(HalDevice *parent, di_node_t node, char *devfs_path);
68 HalDevice *devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
69 static void devinfo_floppy_add_volume(HalDevice *parent, di_node_t node);
70 static HalDevice *devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type);
71 static void devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev);
72 static int walk_devlinks(di_devlink_t devlink, void *arg);
73 static char *get_devlink(di_devlink_handle_t devlink_hdl, char *path);
74 static void devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean);
75 static struct devinfo_storage_minor *devinfo_storage_new_minor(char *maindev_path, char *slice,
76     char *devlink, dev_t dev, int dosnum);
77 static void devinfo_storage_free_minor(struct devinfo_storage_minor *m);
78 HalDevice *devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m);
79 static void devinfo_volume_preprobing_done(HalDevice *d, gpointer userdata1, gpointer userdata2);
80 static void devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
81 static void devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token);
82 static void devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2);
83 const gchar *devinfo_volume_get_prober (HalDevice *d, int *timeout);
84 const gchar *devinfo_storage_get_prober (HalDevice *d, int *timeout);
85 
86 static char *devinfo_scsi_dtype2str(int dtype);
87 static char *devinfo_volume_get_slice_name (char *devlink);
88 static gboolean dos_to_dev(char *path, char **devpath, int *partnum);
89 static gboolean is_dos_path(char *path, int *partnum);
90 
91 static void devinfo_storage_set_nicknames (HalDevice *d);
92 
93 DevinfoDevHandler devinfo_ide_handler = {
94         devinfo_ide_add,
95 	NULL,
96 	NULL,
97 	NULL,
98 	NULL,
99         NULL
100 };
101 DevinfoDevHandler devinfo_scsi_handler = {
102         devinfo_scsi_add,
103 	NULL,
104 	NULL,
105 	NULL,
106 	NULL,
107         NULL
108 };
109 DevinfoDevHandler devinfo_pcata_handler = {
110         devinfo_pcata_add,
111 	NULL,
112 	NULL,
113 	NULL,
114 	NULL,
115         NULL
116 };
117 DevinfoDevHandler devinfo_floppy_handler = {
118         devinfo_floppy_add,
119 	NULL,
120 	NULL,
121 	NULL,
122 	NULL,
123         NULL
124 };
125 DevinfoDevHandler devinfo_lofi_handler = {
126         devinfo_lofi_add,
127 	NULL,
128 	NULL,
129 	NULL,
130 	NULL,
131         NULL
132 };
133 DevinfoDevHandler devinfo_storage_handler = {
134 	NULL,
135 	NULL,
136 	devinfo_storage_hotplug_begin_add,
137 	NULL,
138 	devinfo_storage_probing_done,
139 	devinfo_storage_get_prober
140 };
141 DevinfoDevHandler devinfo_volume_handler = {
142 	NULL,
143 	NULL,
144 	devinfo_volume_hotplug_begin_add,
145 	NULL,
146 	NULL,
147 	devinfo_volume_get_prober
148 };
149 
150 /* IDE */
151 
152 HalDevice *
153 devinfo_ide_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
154 {
155 	char	*s;
156 
157 	if ((device_type != NULL) && (strcmp(device_type, "ide") == 0)) {
158 		return (devinfo_ide_host_add(parent, node, devfs_path));
159 	}
160 
161         if ((di_prop_lookup_strings (DDI_DEV_T_ANY, node, "class", &s) > 0) &&
162 	    (strcmp (s, "dada") == 0)) {
163 		return (devinfo_ide_device_add(parent, node, devfs_path));
164 	}
165 
166 	return (NULL);
167 }
168 
169 static HalDevice *
170 devinfo_ide_host_add(HalDevice *parent, di_node_t node, char *devfs_path)
171 {
172 	HalDevice *d;
173 
174 	d = hal_device_new ();
175 
176 	devinfo_set_default_properties (d, parent, node, devfs_path);
177 	hal_device_property_set_string (d, "info.product", "IDE host controller");
178 	hal_device_property_set_string (d, "info.bus", "ide_host");
179 	hal_device_property_set_int (d, "ide_host.number", 0); /* XXX */
180 
181 	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
182 
183 	return (d);
184 }
185 
186 static HalDevice *
187 devinfo_ide_device_add(HalDevice *parent, di_node_t node, char *devfs_path)
188 {
189 	HalDevice *d;
190 
191 	d = hal_device_new();
192 
193 	devinfo_set_default_properties (d, parent, node, devfs_path);
194         hal_device_property_set_string (parent, "info.product", "IDE device");
195 	hal_device_property_set_string (parent, "info.bus", "ide");
196 	hal_device_property_set_int (parent, "ide.host", 0); /* XXX */
197 	hal_device_property_set_int (parent, "ide.channel", 0);
198 
199 	devinfo_add_enqueue (d, devfs_path, &devinfo_ide_handler);
200 
201 	return (devinfo_ide_storage_add (d, node, devfs_path));
202 }
203 
204 static HalDevice *
205 devinfo_ide_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
206 {
207 	HalDevice *d;
208 	char	*s;
209 	int	*i;
210 	char	*driver_name;
211 	char	udi[HAL_PATH_MAX];
212 
213 	if ((driver_name = di_driver_name (node)) == NULL) {
214 		return (NULL);
215 	}
216 
217         d = hal_device_new ();
218 
219 	devinfo_set_default_properties (d, parent, node, devfs_path);
220         hal_device_property_set_string (d, "info.category", "storage");
221 
222         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
223                 "%s/%s%d", hal_device_get_udi (parent), driver_name, di_instance (node));
224         hal_device_set_udi (d, udi);
225         hal_device_property_set_string (d, "info.udi", udi);
226 	PROP_STR(d, node, s, "devid", "info.product");
227 
228         hal_device_add_capability (d, "storage");
229         hal_device_property_set_string (d, "storage.bus", "ide");
230         hal_device_property_set_int (d, "storage.lun", 0);
231 	hal_device_property_set_string (d, "storage.drive_type", "disk");
232 
233 	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
234 	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
235 
236         hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
237 
238 	/* XXX */
239         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
240 
241 	hal_device_add_capability (d, "block");
242 
243 	devinfo_storage_minors (d, node, (char *)devfs_path, FALSE);
244 
245 	return (d);
246 }
247 
248 /* SCSI */
249 
250 HalDevice *
251 devinfo_scsi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
252 {
253 	int	*i;
254 	char	*driver_name;
255 	HalDevice *d;
256 	char	udi[HAL_PATH_MAX];
257 
258 	driver_name = di_driver_name (node);
259 	if ((driver_name == NULL) || (strcmp (driver_name, "sd") != 0)) {
260 		return (NULL);
261 	}
262 
263 	d = hal_device_new ();
264 
265 	devinfo_set_default_properties (d, parent, node, devfs_path);
266 	hal_device_property_set_string (d, "info.bus", "scsi");
267 
268         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
269                 "%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
270         hal_device_set_udi (d, udi);
271         hal_device_property_set_string (d, "info.udi", udi);
272 
273 	hal_device_property_set_int (d, "scsi.host",
274 		hal_device_property_get_int (parent, "scsi_host.host"));
275 	hal_device_property_set_int (d, "scsi.bus", 0);
276 	PROP_INT(d, node, i, "target", "scsi.target");
277 	PROP_INT(d, node, i, "lun", "scsi.lun");
278         hal_device_property_set_string (d, "info.product", "SCSI Device");
279 
280         devinfo_add_enqueue (d, devfs_path, &devinfo_scsi_handler);
281 
282         return (devinfo_scsi_storage_add (d, node, devfs_path));
283 }
284 
285 static HalDevice *
286 devinfo_scsi_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
287 {
288 	HalDevice *d;
289 	int	*i;
290 	char	*s;
291 	char	udi[HAL_PATH_MAX];
292 
293 	d = hal_device_new ();
294 
295 	devinfo_set_default_properties (d, parent, node, devfs_path);
296         hal_device_property_set_string (d, "info.category", "storage");
297 
298         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
299 		"%s/sd%d", hal_device_get_udi (parent), di_instance (node));
300         hal_device_set_udi (d, udi);
301         hal_device_property_set_string (d, "info.udi", udi);
302 	PROP_STR(d, node, s, "inquiry-product-id", "info.product");
303 
304         hal_device_add_capability (d, "storage");
305 
306         hal_device_property_set_int (d, "storage.lun",
307 		hal_device_property_get_int (parent, "scsi.lun"));
308 	PROP_BOOL(d, node, i, "hotpluggable", "storage.hotpluggable");
309 	PROP_BOOL(d, node, i, "removable-media", "storage.removable");
310         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
311 
312 	/*
313 	 * We have to enable polling not only for drives with removable media,
314 	 * but also for hotpluggable devices, because when a disk is
315 	 * unplugged while busy/mounted, there is not sysevent generated.
316 	 * Instead, the HBA driver (scsa2usb, scsa1394) will notify sd driver
317 	 * and the latter will report DKIO_DEV_GONE via DKIOCSTATE ioctl.
318 	 * So we have to enable media check so that hald-addon-storage notices
319 	 * the "device gone" condition and unmounts all associated volumes.
320 	 */
321 	hal_device_property_set_bool (d, "storage.media_check_enabled",
322 	    ((di_prop_lookup_ints(DDI_DEV_T_ANY, node, "removable-media", &i) >= 0) ||
323 	    (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "hotpluggable", &i) >= 0)));
324 
325         if (di_prop_lookup_ints(DDI_DEV_T_ANY, node, "inquiry-device-type",
326 	    &i) > 0) {
327 		s = devinfo_scsi_dtype2str (*i);
328         	hal_device_property_set_string (d, "storage.drive_type", s);
329 
330 		if (strcmp (s, "cdrom") == 0) {
331 			hal_device_add_capability (d, "storage.cdrom");
332 			hal_device_property_set_bool (d, "storage.no_partitions_hint", TRUE);
333         		hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
334 		}
335 	}
336 
337         hal_device_add_capability (d, "block");
338 
339 	devinfo_storage_minors (d, node, devfs_path, FALSE);
340 
341 	return (d);
342 }
343 
344 static char *
345 devinfo_scsi_dtype2str(int dtype)
346 {
347         char *dtype2str[] = {
348                 "disk"	,         /* DTYPE_DIRECT         0x00 */
349                 "tape"	,         /* DTYPE_SEQUENTIAL     0x01 */
350                 "printer",         /* DTYPE_PRINTER        0x02 */
351                 "processor",         /* DTYPE_PROCESSOR      0x03 */
352                 "worm"	,         /* DTYPE_WORM           0x04 */
353                 "cdrom"	,         /* DTYPE_RODIRECT       0x05 */
354                 "scanner",         /* DTYPE_SCANNER        0x06 */
355                 "cdrom"	,         /* DTYPE_OPTICAL        0x07 */
356                 "changer",         /* DTYPE_CHANGER        0x08 */
357                 "comm"	,         /* DTYPE_COMM           0x09 */
358                 "scsi"	,         /* DTYPE_???            0x0A */
359                 "scsi"	,         /* DTYPE_???            0x0B */
360                 "array_ctrl",         /* DTYPE_ARRAY_CTRL     0x0C */
361                 "esi"	,         /* DTYPE_ESI            0x0D */
362                 "disk"	          /* DTYPE_RBC            0x0E */
363         };
364 
365         if (dtype < NELEM(dtype2str)) {
366                 return (dtype2str[dtype]);
367         } else {
368 		return ("scsi");
369         }
370 
371 }
372 
373 /* PCMCIA */
374 
375 HalDevice *
376 devinfo_pcata_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
377 {
378 	int	*i;
379 	char	*driver_name;
380 	HalDevice *d;
381 	char	udi[HAL_PATH_MAX];
382 
383 	driver_name = di_driver_name (node);
384 	if ((driver_name == NULL) || (strcmp (driver_name, "pcata") != 0)) {
385 		return (NULL);
386 	}
387 
388 	d = hal_device_new ();
389 
390 	devinfo_set_default_properties (d, parent, node, devfs_path);
391 	hal_device_property_set_string (d, "info.bus", "pcmcia");
392 
393         hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
394                 "%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
395         hal_device_set_udi (d, udi);
396         hal_device_property_set_string (d, "info.udi", udi);
397         hal_device_property_set_string (d, "info.product", "PCMCIA Disk");
398 
399         devinfo_add_enqueue (d, devfs_path, &devinfo_pcata_handler);
400 
401         return (devinfo_pcata_storage_add (d, node, devfs_path));
402 }
403 
404 static HalDevice *
405 devinfo_pcata_storage_add(HalDevice *parent, di_node_t node, char *devfs_path)
406 {
407 	HalDevice *d;
408 	char	*driver_name;
409 	int	*i;
410 	char	*s;
411 	char	udi[HAL_PATH_MAX];
412 
413 	d = hal_device_new ();
414 
415 	devinfo_set_default_properties (d, parent, node, devfs_path);
416 	hal_device_property_set_string (d, "info.category", "storage");
417 
418 	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
419 		"%s/sd%d", hal_device_get_udi (parent), di_instance (node));
420 	hal_device_set_udi (d, udi);
421 	hal_device_property_set_string (d, "info.udi", udi);
422 
423 	hal_device_add_capability (d, "storage");
424 
425 	hal_device_property_set_int (d, "storage.lun", 0);
426 	hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
427 	hal_device_property_set_bool (d, "storage.removable", FALSE);
428 	hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
429 	hal_device_property_set_bool (d, "storage.media_check_enabled", TRUE);
430        	hal_device_property_set_string (d, "storage.drive_type", "disk");
431 	hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
432 
433 	hal_device_add_capability (d, "block");
434 
435 	devinfo_storage_minors (d, node, devfs_path, FALSE);
436 
437 	return (d);
438 }
439 
440 /* floppy */
441 
442 HalDevice *
443 devinfo_floppy_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
444 {
445 	char	*driver_name;
446 	char	*raw;
447 	char	udi[HAL_PATH_MAX];
448 	di_devlink_handle_t devlink_hdl;
449         int     major;
450         di_minor_t minor;
451         dev_t   dev;
452 	HalDevice *d = NULL;
453         char    *minor_path = NULL;
454 	char	*devlink = NULL;
455 
456 	driver_name = di_driver_name (node);
457 	if ((driver_name == NULL) || (strcmp (driver_name, "fd") != 0)) {
458 		return (NULL);
459 	}
460 
461 	/*
462 	 * The only minor node we're interested in is /dev/diskette*
463 	 */
464 	major = di_driver_major(node);
465 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
466 		return (NULL);
467 	}
468 	minor = DI_MINOR_NIL;
469 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
470 		dev = di_minor_devt(minor);
471 		if ((major != major(dev)) ||
472 		    (di_minor_type(minor) != DDM_MINOR) ||
473 		    (di_minor_spectype(minor) != S_IFBLK) ||
474 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
475 			continue;
476 		}
477 		if (((devlink = get_devlink(devlink_hdl, minor_path)) != NULL) &&
478 		    (strncmp (devlink, "/dev/diskette", sizeof ("/dev/diskette") - 1) == 0)) {
479 			break;
480 		}
481 		di_devfs_path_free (minor_path);
482 		minor_path = NULL;
483 		free(devlink);
484 		devlink = NULL;
485 	}
486 	di_devlink_fini (&devlink_hdl);
487 
488 	if ((devlink == NULL) || (minor_path == NULL)) {
489 		HAL_INFO (("floppy devlink not found %s", devfs_path));
490 		goto out;
491 	}
492 
493 	d = hal_device_new ();
494 
495 	devinfo_set_default_properties (d, parent, node, devfs_path);
496         hal_device_property_set_string (d, "info.category", "storage");
497         hal_device_add_capability (d, "storage");
498        	hal_device_property_set_string (d, "storage.bus", "platform");
499         hal_device_property_set_bool (d, "storage.hotpluggable", FALSE);
500         hal_device_property_set_bool (d, "storage.removable", TRUE);
501         hal_device_property_set_bool (d, "storage.requires_eject", TRUE);
502         hal_device_property_set_bool (d, "storage.media_check_enabled", FALSE);
503        	hal_device_property_set_string (d, "storage.drive_type", "floppy");
504 
505         hal_device_add_capability (d, "block");
506 	hal_device_property_set_bool (d, "block.is_volume", FALSE);
507 	hal_device_property_set_int (d, "block.major", major(dev));
508 	hal_device_property_set_int (d, "block.minor", minor(dev));
509 	hal_device_property_set_string (d, "block.device", devlink);
510 	raw = dsk_to_rdsk (devlink);
511 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
512 	free (raw);
513 
514 	devinfo_add_enqueue (d, devfs_path, &devinfo_storage_handler);
515 
516 	/* trigger initial probe-volume */
517 	devinfo_floppy_add_volume(d, node);
518 
519 out:
520 	di_devfs_path_free (minor_path);
521 	free(devlink);
522 
523 	return (d);
524 }
525 
526 static void
527 devinfo_floppy_add_volume(HalDevice *parent, di_node_t node)
528 {
529 	char	*devlink;
530 	char	*devfs_path;
531 	int	minor, major;
532 	dev_t	dev;
533 	struct devinfo_storage_minor *m;
534 
535 	devfs_path = (char *)hal_device_property_get_string (parent, "solaris.devfs_path");
536 	devlink = (char *)hal_device_property_get_string (parent, "block.device");
537 	major = hal_device_property_get_int (parent, "block.major");
538 	minor = hal_device_property_get_int (parent, "block.minor");
539 	dev = makedev (major, minor);
540 
541 	m = devinfo_storage_new_minor (devfs_path, WHOLE_DISK, devlink, dev, -1);
542 	devinfo_volume_add (parent, node, m);
543 	devinfo_storage_free_minor (m);
544 }
545 
546 /*
547  * After reprobing storage, reprobe its volumes.
548  */
549 static void
550 devinfo_floppy_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code,
551     char **error, gpointer userdata1, gpointer userdata2)
552 {
553         void *end_token = (void *) userdata1;
554 	const char *devfs_path;
555 	di_node_t node;
556 	HalDevice *v;
557 
558 	if (!hal_device_property_get_bool (d, "storage.removable.media_available")) {
559 		HAL_INFO (("no floppy media", hal_device_get_udi (d)));
560 
561 		/* remove child (can only be single volume) */
562 		if (((v = hal_device_store_match_key_value_string (hald_get_gdl(),
563         	    "info.parent", hal_device_get_udi (d))) != NULL) &&
564 		    ((devfs_path = hal_device_property_get_string (v,
565 		    "solaris.devfs_path")) != NULL)) {
566 			devinfo_remove_enqueue ((char *)devfs_path, NULL);
567 		}
568 	} else {
569 		HAL_INFO (("floppy media found", hal_device_get_udi (d)));
570 
571 		if ((devfs_path = hal_device_property_get_string(d, "solaris.devfs_path")) == NULL) {
572 			HAL_INFO (("no devfs_path", hal_device_get_udi (d)));
573 			hotplug_event_process_queue ();
574 			return;
575 		}
576 		if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
577 			HAL_INFO (("di_init %s failed %d", devfs_path, errno));
578 			hotplug_event_process_queue ();
579 			return;
580 		}
581 
582 		devinfo_floppy_add_volume (d, node);
583 
584 		di_fini (node);
585 	}
586 
587 	hotplug_event_process_queue ();
588 }
589 
590 /* lofi */
591 
592 HalDevice *
593 devinfo_lofi_add(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type)
594 {
595 	return (devinfo_lofi_add_major(parent,node, devfs_path, device_type, FALSE, NULL));
596 }
597 
598 HalDevice *
599 devinfo_lofi_add_major(HalDevice *parent, di_node_t node, char *devfs_path, char *device_type,
600     gboolean rescan, HalDevice *lofi_d)
601 {
602 	char	*driver_name;
603 	HalDevice *d = NULL;
604 	char	udi[HAL_PATH_MAX];
605 	di_devlink_handle_t devlink_hdl;
606         int     major;
607         di_minor_t minor;
608         dev_t   dev;
609         char    *minor_path = NULL;
610         char    *devpath, *devlink;
611 
612 	driver_name = di_driver_name (node);
613 	if ((driver_name == NULL) || (strcmp (driver_name, "lofi") != 0)) {
614 		return (NULL);
615 	}
616 
617 	if (!rescan) {
618 		d = hal_device_new ();
619 
620 		devinfo_set_default_properties (d, parent, node, devfs_path);
621 		hal_device_property_set_string (d, "info.bus", "pseudo");
622 
623         	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
624                 	"%s/%s%d", hal_device_get_udi (parent), di_node_name(node), di_instance (node));
625         	hal_device_set_udi (d, udi);
626         	hal_device_property_set_string (d, "info.udi", udi);
627 
628         	devinfo_add_enqueue (d, devfs_path, &devinfo_lofi_handler);
629 	} else {
630 		d = lofi_d;
631 	}
632 
633 	/*
634 	 * Unlike normal storage, as in devinfo_storage_minors(), where
635 	 * sd instance -> HAL storage, sd minor node -> HAL volume,
636 	 * lofi always has one instance, lofi minor -> HAL storage.
637 	 * lofi storage never has slices, but it can have
638 	 * embedded pcfs partitions that fstyp would recognize
639 	 */
640 	major = di_driver_major(node);
641 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
642 		return (d);
643 	}
644 	minor = DI_MINOR_NIL;
645 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
646 		dev = di_minor_devt(minor);
647 		if ((major != major(dev)) ||
648 		    (di_minor_type(minor) != DDM_MINOR) ||
649 		    (di_minor_spectype(minor) != S_IFBLK) ||
650 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
651 			continue;
652 		}
653 		if ((devlink = get_devlink(devlink_hdl, minor_path)) == NULL) {
654 			di_devfs_path_free (minor_path);
655         		continue;
656 		}
657 
658 		if (!rescan ||
659 		    (hal_device_store_match_key_value_string (hald_get_gdl (),
660 		    "solaris.devfs_path", minor_path) == NULL)) {
661 			devinfo_lofi_add_minor(d, node, minor_path, devlink, dev);
662 		}
663 
664 		di_devfs_path_free (minor_path);
665 		free(devlink);
666 	}
667 	di_devlink_fini (&devlink_hdl);
668 
669 	return (d);
670 }
671 
672 static void
673 devinfo_lofi_add_minor(HalDevice *parent, di_node_t node, char *minor_path, char *devlink, dev_t dev)
674 {
675 	HalDevice *d;
676 	char	*raw;
677 	char	*doslink;
678 	char	dospath[64];
679 	struct devinfo_storage_minor *m;
680 	int	i;
681 
682 	/* add storage */
683 	d = hal_device_new ();
684 
685 	devinfo_set_default_properties (d, parent, node, minor_path);
686         hal_device_property_set_string (d, "info.category", "storage");
687         hal_device_add_capability (d, "storage");
688        	hal_device_property_set_string (d, "storage.bus", "lofi");
689         hal_device_property_set_bool (d, "storage.hotpluggable", TRUE);
690         hal_device_property_set_bool (d, "storage.removable", FALSE);
691         hal_device_property_set_bool (d, "storage.requires_eject", FALSE);
692        	hal_device_property_set_string (d, "storage.drive_type", "disk");
693         hal_device_add_capability (d, "block");
694 	hal_device_property_set_int (d, "block.major", major(dev));
695 	hal_device_property_set_int (d, "block.minor", minor(dev));
696 	hal_device_property_set_string (d, "block.device", devlink);
697 	raw = dsk_to_rdsk (devlink);
698 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
699 	free (raw);
700 	hal_device_property_set_bool (d, "block.is_volume", FALSE);
701 
702 	devinfo_add_enqueue (d, minor_path, &devinfo_storage_handler);
703 
704 	/* add volumes: one on main device and a few pcfs candidates */
705 	m = devinfo_storage_new_minor(minor_path, WHOLE_DISK, devlink, dev, -1);
706 	devinfo_volume_add (d, node, m);
707 	devinfo_storage_free_minor (m);
708 
709 	doslink = (char *)calloc (1, strlen (devlink) + sizeof (":NNN") + 1);
710 	if (doslink != NULL) {
711 		for (i = 1; i < 16; i++) {
712 			snprintf(dospath, sizeof (dospath), WHOLE_DISK":%d", i);
713 			sprintf(doslink, "%s:%d", devlink, i);
714 			m = devinfo_storage_new_minor(minor_path, dospath, doslink, dev, i);
715 			devinfo_volume_add (d, node, m);
716 			devinfo_storage_free_minor (m);
717 		}
718 		free (doslink);
719 	}
720 }
721 
722 void
723 devinfo_lofi_remove_minor(char *parent_devfs_path, char *name)
724 {
725 	GSList *i;
726 	GSList *devices;
727 	HalDevice *d = NULL;
728 	const char *devfs_path;
729 
730 	devices = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
731 		"block.solaris.raw_device", name);
732         for (i = devices; i != NULL; i = g_slist_next (i)) {
733 		if (hal_device_has_capability (HAL_DEVICE (i->data), "storage")) {
734 			d = HAL_DEVICE (i->data);
735 			break;
736 		}
737 	}
738 	g_slist_free (devices);
739 
740 	if (d == NULL) {
741 		HAL_INFO (("device not found %s", name));
742 		return;
743 	}
744 
745 	if ((devfs_path = hal_device_property_get_string (d,
746 	    "solaris.devfs_path")) == NULL) {
747 		HAL_INFO (("devfs_path not found %s", hal_device_get_udi (d)));
748 		return;
749 	}
750 
751 	if (d != NULL) {
752 		devinfo_remove_branch ((char *)devfs_path, d);
753 	}
754 }
755 
756 /* common storage */
757 
758 static int
759 walk_devlinks(di_devlink_t devlink, void *arg)
760 {
761         char    **path= (char **)arg;
762 
763         *path = strdup(di_devlink_path(devlink));
764 
765         return (DI_WALK_TERMINATE);
766 }
767 
768 static char *
769 get_devlink(di_devlink_handle_t devlink_hdl, char *path)
770 {
771         char    *devlink_path = NULL;
772 
773         (void) di_devlink_walk(devlink_hdl, NULL, path,
774             DI_PRIMARY_LINK, &devlink_path, walk_devlinks);
775 
776         return (devlink_path);
777 }
778 
779 static void
780 devinfo_storage_free_minor(struct devinfo_storage_minor *m)
781 {
782 	if (m != NULL) {
783 		free (m->slice);
784 		free (m->devlink);
785 		free (m->devpath);
786 		free (m);
787 	}
788 }
789 
790 static struct devinfo_storage_minor *
791 devinfo_storage_new_minor(char *maindev_path, char *slice, char *devlink, dev_t dev, int dosnum)
792 {
793 	struct devinfo_storage_minor *m;
794 	int pathlen;
795 	char *devpath;
796 
797 	m = (struct devinfo_storage_minor *)calloc (sizeof (struct devinfo_storage_minor), 1);
798 	if (m != NULL) {
799 		/*
800 		 * For volume's devfs_path we'll use minor_path/slice instead of
801 		 * minor_path which we use for parent storage device.
802 		 */
803 		pathlen = strlen (maindev_path) + strlen (slice) + 2;
804 		devpath = (char *)calloc (1, pathlen);
805 		snprintf(devpath, pathlen, "%s/%s", maindev_path, slice);
806 
807 		m->devpath = devpath;
808 		m->devlink = strdup (devlink);
809 		m->slice = strdup (slice);
810 		m->dev = dev;
811 		m->dosnum = dosnum;
812 		if ((m->devpath == NULL) || (m->devlink == NULL)) {
813 			devinfo_storage_free_minor (m);
814 			m = NULL;
815 		}
816 	}
817 	return (m);
818 }
819 
820 /*
821  * Storage minor nodes are potential "volume" objects.
822  * This function also completes building the parent object (main storage device).
823  */
824 static void
825 devinfo_storage_minors(HalDevice *parent, di_node_t node, gchar *devfs_path, gboolean rescan)
826 {
827 	di_devlink_handle_t devlink_hdl;
828 	gboolean is_cdrom;
829 	const char *whole_disk;
830 	int     major;
831 	di_minor_t minor;
832 	dev_t   dev;
833 	char    *minor_path = NULL;
834 	char    *maindev_path = NULL;
835 	char    *devpath, *devlink;
836 	int	doslink_len;
837 	char	*doslink;
838 	char	dospath[64];
839 	char    *slice;
840 	int	pathlen;
841 	int	i;
842 	char	*raw;
843 	boolean_t maindev_is_d0;
844 	GQueue	*mq;
845 	HalDevice *volume;
846 	struct devinfo_storage_minor *m;
847 	struct devinfo_storage_minor *maindev = NULL;
848 
849 	/* for cdroms whole disk is always s2 */
850 	is_cdrom = hal_device_has_capability (parent, "storage.cdrom");
851 	whole_disk = is_cdrom ? "s2" : WHOLE_DISK;
852 
853 	major = di_driver_major(node);
854 
855 	/* the "whole disk" p0/s2/d0 node must come first in the hotplug queue
856 	 * so we put other minor nodes on the local queue and move to the
857 	 * hotplug queue up in the end
858 	 */
859 	if ((mq = g_queue_new()) == NULL) {
860 		goto err;
861 	}
862 	if ((devlink_hdl = di_devlink_init(NULL, 0)) == NULL) {
863 		g_queue_free (mq);
864 		goto err;
865 	}
866 	minor = DI_MINOR_NIL;
867 	while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
868 		dev = di_minor_devt(minor);
869 		if ((major != major(dev)) ||
870 		    (di_minor_type(minor) != DDM_MINOR) ||
871 		    (di_minor_spectype(minor) != S_IFBLK) ||
872 		    ((minor_path = di_devfs_minor_path(minor)) == NULL)) {
873 			continue;
874 		}
875 		if ((devlink = get_devlink(devlink_hdl, minor_path)) == NULL) {
876 			di_devfs_path_free (minor_path);
877         		continue;
878 		}
879 
880 		slice = devinfo_volume_get_slice_name (devlink);
881 		if (strlen (slice) < 2) {
882 			free (devlink);
883 			di_devfs_path_free (minor_path);
884 			continue;
885 		}
886 
887 		/* ignore p1..N - we'll use p0:N instead */
888 		if ((strlen (slice) > 1) && (slice[0] == 'p') && isdigit(slice[1]) &&
889 		    ((atol(&slice[1])) > 0)) {
890 			free (devlink);
891 			di_devfs_path_free (minor_path);
892 			continue;
893 		}
894 
895 		m = devinfo_storage_new_minor(minor_path, slice, devlink, dev, -1);
896 		if (m == NULL) {
897 			free (devlink);
898 			di_devfs_path_free (minor_path);
899 			continue;
900 		}
901 
902 		/* main device is either s2/p0 or d0, the latter taking precedence */
903 		if ((strcmp (slice, "d0") == 0) ||
904 		    (((strcmp (slice, whole_disk) == 0) && (maindev == NULL)))) {
905 			if (maindev_path != NULL) {
906 				di_devfs_path_free (maindev_path);
907 			}
908 			maindev_path = minor_path;
909 			maindev = m;
910 			g_queue_push_head (mq, maindev);
911 		} else {
912 			di_devfs_path_free (minor_path);
913 			g_queue_push_tail (mq, m);
914 		}
915 
916 		free (devlink);
917 	}
918 	di_devlink_fini (&devlink_hdl);
919 
920 	if (maindev == NULL) {
921 		/* shouldn't typically happen */
922 		while (!g_queue_is_empty (mq)) {
923 			devinfo_storage_free_minor (g_queue_pop_head (mq));
924 		}
925 		goto err;
926 	}
927 
928 	/* first enqueue main storage device */
929 	if (!rescan) {
930 		hal_device_property_set_int (parent, "block.major", major);
931 		hal_device_property_set_int (parent, "block.minor", minor(maindev->dev));
932 		hal_device_property_set_string (parent, "block.device", maindev->devlink);
933 		raw = dsk_to_rdsk (maindev->devlink);
934 		hal_device_property_set_string (parent, "block.solaris.raw_device", raw);
935 		free (raw);
936 		hal_device_property_set_bool (parent, "block.is_volume", FALSE);
937 		hal_device_property_set_string (parent, "solaris.devfs_path", maindev_path);
938 		devinfo_add_enqueue (parent, maindev_path, &devinfo_storage_handler);
939 	}
940 
941 	/* add virtual dos volumes to enable pcfs probing */
942 	if (!is_cdrom) {
943 		doslink_len = strlen (maindev->devlink) + sizeof (":NNN") + 1;
944 		if ((doslink = (char *)calloc (1, doslink_len)) != NULL) {
945 			for (i = 1; i < 16; i++) {
946 				snprintf(dospath, sizeof (dospath), "%s:%d", maindev->slice, i);
947 				snprintf(doslink, doslink_len, "%s:%d", maindev->devlink, i);
948 				m = devinfo_storage_new_minor(maindev_path, dospath, doslink, maindev->dev, i);
949 				g_queue_push_tail (mq, m);
950 			}
951 			free (doslink);
952 		}
953 	}
954 
955 	maindev_is_d0 = (strcmp (maindev->slice, "d0") == 0);
956 
957 	/* enqueue all volumes */
958 	while (!g_queue_is_empty (mq)) {
959 		m = g_queue_pop_head (mq);
960 
961 		/* if main device is d0, we'll throw away s2/p0 */
962 		if (maindev_is_d0 && (strcmp (m->slice, whole_disk) == 0)) {
963 			devinfo_storage_free_minor (m);
964 			continue;
965 		}
966 		/* don't do p0 on cdrom */
967 		if (is_cdrom && (strcmp (m->slice, "p0") == 0)) {
968 			devinfo_storage_free_minor (m);
969 			continue;
970 		}
971 		if (rescan) {
972 			/* in rescan mode, don't reprobe existing volumes */
973 			/* XXX detect volume removal? */
974 			volume = hal_device_store_match_key_value_string (hald_get_gdl (),
975 			    "solaris.devfs_path", m->devpath);
976 			if ((volume == NULL) || !hal_device_has_capability(volume, "volume")) {
977 				devinfo_volume_add (parent, node, m);
978 			} else {
979 				HAL_INFO(("rescan volume exists %s", m->devpath));
980 			}
981 		} else {
982 			devinfo_volume_add (parent, node, m);
983 		}
984 		devinfo_storage_free_minor (m);
985 	}
986 
987 	if (maindev_path != NULL) {
988 		di_devfs_path_free (maindev_path);
989 	}
990 
991 	return;
992 
993 err:
994 	if (maindev_path != NULL) {
995 		di_devfs_path_free (maindev_path);
996 	}
997 	if (!rescan) {
998 		devinfo_add_enqueue (parent, devfs_path, &devinfo_storage_handler);
999 	}
1000 }
1001 
1002 HalDevice *
1003 devinfo_volume_add(HalDevice *parent, di_node_t node, devinfo_storage_minor_t *m)
1004 {
1005 	HalDevice *d;
1006 	char	*raw;
1007         char    udi[HAL_PATH_MAX];
1008 	char	*devfs_path = m->devpath;
1009 	char	*devlink = m->devlink;
1010 	dev_t	dev = m->dev;
1011 	int	dosnum = m->dosnum;
1012 	char	*slice = m->slice;
1013 
1014 	HAL_INFO (("volume_add: devfs_path=%s devlink=%s", devfs_path, devlink));
1015 	d = hal_device_new ();
1016 
1017 	devinfo_set_default_properties (d, parent, node, devfs_path);
1018         hal_device_property_set_string (d, "info.category", "volume");
1019 
1020        	hal_util_compute_udi (hald_get_gdl (), udi, sizeof (udi),
1021 		"%s/%s", hal_device_get_udi (parent), slice);
1022         hal_device_set_udi (d, udi);
1023         hal_device_property_set_string (d, "info.udi", udi);
1024         hal_device_property_set_string (d, "info.product", slice);
1025 
1026        	hal_device_add_capability (d, "volume");
1027        	hal_device_add_capability (d, "block");
1028 	hal_device_property_set_int (d, "block.major", major (dev));
1029 	hal_device_property_set_int (d, "block.minor", minor (dev));
1030 	hal_device_property_set_string (d, "block.device", devlink);
1031 	raw = dsk_to_rdsk (devlink);
1032 	hal_device_property_set_string (d, "block.solaris.raw_device", raw);
1033 	free (raw);
1034 	hal_device_property_set_string (d, "block.solaris.slice", slice);
1035 	hal_device_property_set_bool (d, "block.is_volume", TRUE); /* XXX */
1036 
1037 	hal_device_property_set_string (d, "block.storage_device", hal_device_get_udi (parent));
1038 
1039 	/* set volume defaults */
1040 	hal_device_property_set_string (d, "volume.fstype", "");
1041 	hal_device_property_set_string (d, "volume.fsusage", "");
1042 	hal_device_property_set_string (d, "volume.fsversion", "");
1043 	hal_device_property_set_string (d, "volume.uuid", "");
1044 	hal_device_property_set_string (d, "volume.label", "");
1045 	hal_device_property_set_string (d, "volume.mount_point", "");
1046 	hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
1047 	if (strcmp (hal_device_property_get_string (parent, "storage.drive_type"), "cdrom") == 0) {
1048 		hal_device_property_set_bool (d, "volume.is_disc", TRUE);
1049 		hal_device_add_capability (d, "volume.disc");
1050 	} else {
1051 		hal_device_property_set_bool (d, "volume.is_disc", FALSE);
1052 	}
1053 
1054 	if (dosnum > 0) {
1055 		hal_device_property_set_bool (d, "volume.is_partition", TRUE);
1056 		hal_device_property_set_int (d, "volume.partition.number", dosnum);
1057 	} else {
1058 		hal_device_property_set_bool (d, "volume.is_partition", FALSE);
1059 	}
1060 
1061 	/* prober may override these */
1062         hal_device_property_set_int (d, "volume.block_size", 512);
1063 
1064 	devinfo_add_enqueue (d, devfs_path, &devinfo_volume_handler);
1065 
1066 	return (d);
1067 }
1068 
1069 static void
1070 devinfo_volume_preprobing_done (HalDevice *d, gpointer userdata1, gpointer userdata2)
1071 {
1072 	void *end_token = (void *) userdata1;
1073 	char *whole_disk;
1074 	char *block_device;
1075 	const char *storage_udi;
1076 	HalDevice *storage_d;
1077 	const char *slice;
1078 	int dos_num;
1079 
1080 	if (hal_device_property_get_bool (d, "info.ignore")) {
1081 		HAL_INFO (("Preprobing merged info.ignore==TRUE %s", hal_device_get_udi (d)));
1082 		goto skip;
1083 	}
1084 
1085 	/*
1086 	 * Optimizations: only probe if there's a chance to find something
1087 	 */
1088 	block_device = (char *)hal_device_property_get_string (d, "block.device");
1089 	storage_udi = hal_device_property_get_string (d, "block.storage_device");
1090 	slice = hal_device_property_get_string(d, "block.solaris.slice");
1091 	if ((block_device == NULL) || (storage_udi == NULL) ||
1092 	    (slice == NULL) || (strlen (slice) < 2)) {
1093 		HAL_INFO (("Malformed volume properties %s", hal_device_get_udi (d)));
1094 		goto skip;
1095 	}
1096 	storage_d = hal_device_store_match_key_value_string (hald_get_gdl (), "info.udi", storage_udi);
1097 	if (storage_d == NULL) {
1098 		HAL_INFO (("Storage device not found %s", hal_device_get_udi (d)));
1099 		goto skip;
1100 	}
1101 
1102 	whole_disk = hal_device_has_capability (storage_d,
1103 	    "storage.cdrom") ? "s2" : WHOLE_DISK;
1104 
1105 	if (is_dos_path(block_device, &dos_num)) {
1106 		/* don't probe more dos volumes than probe-storage found */
1107 		if ((hal_device_property_get_bool (storage_d, "storage.no_partitions_hint") ||
1108 		    (dos_num > hal_device_property_get_int (storage_d, "storage.solaris.num_dos_partitions")))) {
1109 			    HAL_INFO (("%d > %d %s", dos_num, hal_device_property_get_int (storage_d,
1110 				"storage.solaris.num_dos_partitions"), hal_device_get_udi (storage_d)));
1111 			goto skip;
1112 		}
1113 	} else {
1114 		/* if no VTOC slices found, don't probe slices except s2 */
1115 		if ((slice[0] == 's') && (isdigit(slice[1])) && ((strcmp (slice, whole_disk)) != 0) &&
1116 		    !hal_device_property_get_bool (storage_d, "storage.solaris.vtoc_slices")) {
1117 			HAL_INFO (("Not probing slice %s", hal_device_get_udi (d)));
1118 			goto skip;
1119 		}
1120 	}
1121 
1122 	HAL_INFO(("Probing udi=%s", hal_device_get_udi (d)));
1123 	hald_runner_run (d,
1124 			"hald-probe-volume", NULL,
1125 			DEVINFO_PROBE_VOLUME_TIMEOUT,
1126 			devinfo_callouts_probing_done,
1127 			(gpointer) end_token, userdata2);
1128 
1129 	return;
1130 
1131 skip:
1132 	hal_device_store_remove (hald_get_tdl (), d);
1133 	g_object_unref (d);
1134 	hotplug_event_end (end_token);
1135 }
1136 
1137 static void
1138 devinfo_volume_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
1139 {
1140 	HAL_INFO(("Preprobing volume udi=%s", hal_device_get_udi (d)));
1141 
1142 	if (hal_device_property_get_bool (parent, "info.ignore")) {
1143 		HAL_INFO (("Ignoring volume: parent's info.ignore is TRUE"));
1144 		goto skip;
1145 	}
1146 
1147         /* add to TDL so preprobing callouts and prober can access it */
1148         hal_device_store_add (hald_get_tdl (), d);
1149 
1150         /* Process preprobe fdi files */
1151         di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
1152 
1153         /* Run preprobe callouts */
1154         hal_util_callout_device_preprobe (d, devinfo_volume_preprobing_done, end_token, handler);
1155 
1156 	return;
1157 
1158 skip:
1159 	g_object_unref (d);
1160 	hotplug_event_end (end_token);
1161 }
1162 
1163 void
1164 devinfo_storage_hotplug_begin_add (HalDevice *d, HalDevice *parent, DevinfoDevHandler *handler, void *end_token)
1165 {
1166 	const char *drive_type;
1167 	const char *p_udi;
1168 	HalDevice *p_d;
1169 	HalDevice *phys_d = NULL;
1170 	const char *phys_bus;
1171 	const char *bus;
1172 	static const char *busses[] = { "usb", "ide", "scsi", "ieee1394",
1173 					"pseudo" };
1174 	int i;
1175 
1176 	HAL_INFO (("Preprobing udi=%s", hal_device_get_udi (d)));
1177 
1178 	if (parent == NULL) {
1179 		HAL_INFO (("no parent %s", hal_device_get_udi (d)));
1180 		goto error;
1181 	}
1182 
1183 	/*
1184 	 * figure out physical device and bus, except for floppy
1185 	 */
1186 	drive_type = hal_device_property_get_string (d, "storage.drive_type");
1187 	if ((drive_type != NULL) && (strcmp (drive_type, "floppy") == 0)) {
1188 		goto skip_bus;
1189 	}
1190 
1191 	p_d = parent;
1192 	for (;;) {
1193 		bus = hal_device_property_get_string (p_d, "info.bus");
1194 		if (bus != NULL) {
1195 			for (i = 0; i < NELEM(busses); i++) {
1196 				if (strcmp(bus, busses[i]) == 0) {
1197 					phys_d = p_d;
1198 					phys_bus = busses[i];
1199 					break;
1200 				}
1201 			}
1202 		}
1203 		/* up the tree */
1204 		p_udi = hal_device_property_get_string (p_d, "info.parent");
1205 		if (p_udi == NULL) {
1206 			break;
1207 		}
1208 		p_d = hal_device_store_find (hald_get_gdl (), p_udi);
1209 	}
1210 	if (phys_d == NULL) {
1211 		HAL_INFO (("no physical device %s", hal_device_get_udi (d)));
1212 	} else {
1213 		hal_device_property_set_string (d, "storage.physical_device", hal_device_get_udi (phys_d));
1214 		hal_device_property_set_string (d, "storage.bus", phys_bus);
1215 	}
1216 
1217 skip_bus:
1218 
1219 	/* add to TDL so preprobing callouts and prober can access it */
1220 	hal_device_store_add (hald_get_tdl (), d);
1221 
1222 	/* Process preprobe fdi files */
1223 	di_search_and_merge (d, DEVICE_INFO_TYPE_PREPROBE);
1224 
1225 	/* Run preprobe callouts */
1226 	hal_util_callout_device_preprobe (d, devinfo_callouts_preprobing_done, end_token, handler);
1227 
1228 	return;
1229 
1230 error:
1231 	g_object_unref (d);
1232 	hotplug_event_end (end_token);
1233 }
1234 
1235 static void
1236 devinfo_storage_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
1237 {
1238         void *end_token = (void *) userdata1;
1239 
1240 	HAL_INFO (("devinfo_storage_probing_done %s", hal_device_get_udi (d)));
1241 
1242         /* Discard device if probing reports failure */
1243         if (exit_type != HALD_RUN_SUCCESS || return_code != 0) {
1244 		HAL_INFO (("devinfo_storage_probing_done returning exit_type=%d return_code=%d", exit_type, return_code));
1245                 hal_device_store_remove (hald_get_tdl (), d);
1246                 g_object_unref (d);
1247                 hotplug_event_end (end_token);
1248 		return;
1249         }
1250 
1251 	devinfo_storage_set_nicknames (d);
1252 
1253         /* Merge properties from .fdi files */
1254         di_search_and_merge (d, DEVICE_INFO_TYPE_INFORMATION);
1255         di_search_and_merge (d, DEVICE_INFO_TYPE_POLICY);
1256 
1257 	hal_util_callout_device_add (d, devinfo_callouts_add_done, end_token, NULL);
1258 }
1259 
1260 const gchar *
1261 devinfo_storage_get_prober (HalDevice *d, int *timeout)
1262 {
1263 	*timeout = DEVINFO_PROBE_STORAGE_TIMEOUT;
1264 	return "hald-probe-storage";
1265 }
1266 
1267 const gchar *
1268 devinfo_volume_get_prober (HalDevice *d, int *timeout)
1269 {
1270 	*timeout = DEVINFO_PROBE_VOLUME_TIMEOUT;
1271 	return "hald-probe-volume";
1272 }
1273 
1274 /*
1275  * After reprobing storage, reprobe its volumes.
1276  */
1277 static void
1278 devinfo_storage_rescan_probing_done (HalDevice *d, guint32 exit_type, gint return_code, char **error, gpointer userdata1, gpointer userdata2)
1279 {
1280         void *end_token = (void *) userdata1;
1281 	const char *devfs_path_orig = NULL;
1282 	char *devfs_path = NULL;
1283 	char *p;
1284 	di_node_t node;
1285 
1286 	HAL_INFO (("devinfo_storage_rescan_probing_done %s", hal_device_get_udi (d)));
1287 
1288 	devfs_path_orig = hal_device_property_get_string (d, "solaris.devfs_path");
1289 	if (devfs_path_orig == NULL) {
1290 		HAL_INFO (("device has no solaris.devfs_path"));
1291 		hotplug_event_process_queue ();
1292 		return;
1293 	}
1294 
1295 	/* strip trailing minor part if any */
1296 	if (strrchr(devfs_path_orig, ':') != NULL) {
1297 		if ((devfs_path = strdup (devfs_path_orig)) != NULL) {
1298 			p = strrchr(devfs_path, ':');
1299 			*p = '\0';
1300 		}
1301 	} else {
1302 		devfs_path = (char *)devfs_path_orig;
1303 	}
1304 
1305 	if ((node = di_init (devfs_path, DINFOCPYALL)) == DI_NODE_NIL) {
1306 		HAL_INFO (("di_init %s failed %d %s", devfs_path, errno, hal_device_get_udi (d)));
1307 		hotplug_event_process_queue ();
1308 		return;
1309 	} else {
1310 		devinfo_storage_minors (d, node, (char *)devfs_path, TRUE);
1311 		di_fini (node);
1312 	}
1313 
1314 	if (devfs_path != devfs_path_orig) {
1315 		free (devfs_path);
1316 	}
1317 
1318 	hotplug_event_process_queue ();
1319 }
1320 
1321 /*
1322  * For removable media devices, check for "storage.removable.media_available".
1323  * For non-removable media devices, assume media is always there.
1324  *
1325  * If media is gone, enqueue remove events for all children volumes.
1326  * If media is there, first reprobe storage, then probe for new volumes (but leave existing volumes alone).
1327  */
1328 gboolean
1329 devinfo_storage_device_rescan (HalDevice *d)
1330 {
1331 	GSList *i;
1332 	GSList *volumes;
1333 	HalDevice *v;
1334 	gchar *v_devfs_path;
1335 	const char *drive_type;
1336 	gboolean is_floppy;
1337 	gboolean media_available;
1338 
1339 	HAL_INFO (("devinfo_storage_device_rescan udi=%s", hal_device_get_udi (d)));
1340 
1341 	if (hal_device_property_get_bool (d, "block.is_volume")) {
1342 		HAL_INFO (("nothing to do for volume"));
1343 		return (FALSE);
1344 	}
1345 
1346 	drive_type = hal_device_property_get_string (d, "storage.drive_type");
1347 	is_floppy = (drive_type != NULL) && (strcmp (drive_type, "floppy") == 0);
1348 
1349 	media_available = !hal_device_property_get_bool (d, "storage.removable") ||
1350 	    hal_device_property_get_bool (d, "storage.removable.media_available");
1351 
1352 	if (!media_available && !is_floppy) {
1353 		HAL_INFO (("media gone %s", hal_device_get_udi (d)));
1354 
1355 		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl(),
1356         	    "block.storage_device", hal_device_get_udi (d));
1357 		for (i = volumes; i != NULL; i = g_slist_next (i)) {
1358         		v = HAL_DEVICE (i->data);
1359 			v_devfs_path = (gchar *)hal_device_property_get_string (v, "solaris.devfs_path");
1360 			HAL_INFO (("child volume %s", hal_device_get_udi (v)));
1361 			if ((v_devfs_path != NULL) && hal_device_has_capability (v, "volume")) {
1362 				HAL_INFO (("removing volume %s", hal_device_get_udi (v)));
1363 				devinfo_remove_enqueue (v_devfs_path, NULL);
1364 			} else {
1365 				HAL_INFO (("not a volume %s", hal_device_get_udi (v)));
1366 			}
1367 		}
1368 		g_slist_free (volumes);
1369 
1370 		hotplug_event_process_queue ();
1371 	} else if (is_floppy) {
1372 		HAL_INFO (("rescanning floppy %s", hal_device_get_udi (d)));
1373 
1374 		hald_runner_run (d,
1375 				 "hald-probe-storage --only-check-for-media", NULL,
1376 				 DEVINFO_PROBE_STORAGE_TIMEOUT,
1377 				 devinfo_floppy_rescan_probing_done,
1378 				 NULL, NULL);
1379 	} else {
1380 		HAL_INFO (("media available %s", hal_device_get_udi (d)));
1381 
1382 		hald_runner_run (d,
1383 				 "hald-probe-storage --only-check-for-media", NULL,
1384 				 DEVINFO_PROBE_STORAGE_TIMEOUT,
1385 				 devinfo_storage_rescan_probing_done,
1386 				 NULL, NULL);
1387 	}
1388 
1389 	return TRUE;
1390 }
1391 
1392 static char *
1393 devinfo_volume_get_slice_name (char *devlink)
1394 {
1395 	char	*part, *slice, *disk;
1396 	char	*s = NULL;
1397 	char	*p;
1398 
1399 	if ((p = strstr(devlink, "/lofi/")) != 0) {
1400 		return (p + sizeof ("/lofi/") - 1);
1401 	}
1402 
1403 	part = strrchr(devlink, 'p');
1404 	slice = strrchr(devlink, 's');
1405 	disk = strrchr(devlink, 'd');
1406 
1407 	if ((part != NULL) && (part > slice) && (part > disk)) {
1408 		s = part;
1409 	} else if ((slice != NULL) && (slice > disk)) {
1410 		s = slice;
1411 	} else {
1412 		s = disk;
1413 	}
1414 	if ((s != NULL) && isdigit(s[1])) {
1415 		return (s);
1416 	} else {
1417 		return ("");
1418 	}
1419 }
1420 
1421 static gboolean
1422 is_dos_path(char *path, int *partnum)
1423 {
1424 	char *p;
1425 
1426 	if ((p = strrchr (path, ':')) == NULL) {
1427 		return (FALSE);
1428 	}
1429 	return ((*partnum = atoi(p + 1)) != 0);
1430 }
1431 
1432 static gboolean
1433 dos_to_dev(char *path, char **devpath, int *partnum)
1434 {
1435 	char *p;
1436 
1437 	if ((p = strrchr (path, ':')) == NULL) {
1438 		return (FALSE);
1439 	}
1440 	if ((*partnum = atoi(p + 1)) == 0) {
1441 		return (FALSE);
1442 	}
1443 	p[0] = '\0';
1444 	*devpath = strdup(path);
1445 	p[0] = ':';
1446 	return (*devpath != NULL);
1447 }
1448 
1449 static void
1450 devinfo_storage_cleanup_mountpoint_cb (HalDevice *d, guint32 exit_type,
1451 		       gint return_code, gchar **error,
1452 		       gpointer data1, gpointer data2)
1453 {
1454 	char *mount_point = (char *) data1;
1455 
1456 	HAL_INFO (("Cleaned up mount point '%s'", mount_point));
1457 	g_free (mount_point);
1458 }
1459 
1460 
1461 void
1462 devinfo_storage_mnttab_event (HalDevice *hal_volume)
1463 {
1464 	FILE *fp = NULL;
1465         struct extmnttab m;
1466 	HalDevice *d;
1467 	unsigned int major;
1468 	unsigned int minor;
1469 	GSList *volumes = NULL;
1470 	GSList *v;
1471 	char *mount_point;
1472 	dbus_bool_t is_partition;
1473 	const char *fstype;
1474 	int partition_number;
1475 
1476 	if (hal_volume != NULL) {
1477 		volumes = g_slist_append (NULL, hal_volume);
1478 	} else {
1479 		volumes = hal_device_store_match_multiple_key_value_string (hald_get_gdl (), "info.category", "volume");
1480 	}
1481 	if (volumes == NULL) {
1482 		return;
1483 	}
1484 
1485 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1486 		HAL_ERROR (("Open failed %s errno %d", MNTTAB, errno));
1487 		return;
1488 	}
1489 
1490 	while (getextmntent(fp, &m, 1) == 0) {
1491 		for (v = volumes; v != NULL; v = g_slist_next (v)) {
1492 			d = HAL_DEVICE (v->data);
1493 			major = hal_device_property_get_int (d, "block.major");
1494 			minor = hal_device_property_get_int (d, "block.minor");
1495 
1496 			/*
1497 			 * special handling for pcfs, which encodes logical
1498 			 * drive number into the 6 upper bits of the minor
1499 			 */
1500 			is_partition = hal_device_property_get_bool (d, "volume.is_partition");
1501 			partition_number = hal_device_property_get_int (d, "volume.partition.number");
1502 			fstype = hal_device_property_get_string (d, "volume.fstype");
1503 
1504 			if (is_partition && (partition_number > 0) && (strcmp (fstype, "pcfs") == 0)) {
1505 				minor |= partition_number << 12;
1506 			}
1507 
1508 			if (m.mnt_major != major || m.mnt_minor != minor) {
1509 				continue;
1510 			}
1511 
1512 			/* this volume matches the mnttab entry */
1513 			device_property_atomic_update_begin ();
1514 			hal_device_property_set_bool (d, "volume.is_mounted", TRUE);
1515 			hal_device_property_set_bool (d, "volume.is_mounted_read_only",
1516 						      hasmntopt ((struct mnttab *)&m, "ro") ? TRUE : FALSE);
1517 			hal_device_property_set_string (d, "volume.mount_point", m.mnt_mountp);
1518 			device_property_atomic_update_end ();
1519 
1520 			HAL_INFO (("set %s to be mounted at %s",
1521 				   hal_device_get_udi (d), m.mnt_mountp));
1522 			volumes = g_slist_delete_link (volumes, v);
1523 		}
1524 	}
1525 
1526 	/* all remaining volumes are not mounted */
1527 	for (v = volumes; v != NULL; v = g_slist_next (v)) {
1528 		d = HAL_DEVICE (v->data);
1529 		mount_point = g_strdup (hal_device_property_get_string (d, "volume.mount_point"));
1530 		if (mount_point == NULL || strlen (mount_point) == 0) {
1531 			g_free (mount_point);
1532 			continue;
1533 		}
1534 
1535 		device_property_atomic_update_begin ();
1536 		hal_device_property_set_bool (d, "volume.is_mounted", FALSE);
1537 		hal_device_property_set_bool (d, "volume.is_mounted_read_only", FALSE);
1538 		hal_device_property_set_string (d, "volume.mount_point", "");
1539 		device_property_atomic_update_end ();
1540 
1541 		HAL_INFO (("set %s to unmounted", hal_device_get_udi (d)));
1542 
1543 		/* cleanup if was mounted by us */
1544 		if (hal_util_is_mounted_by_hald (mount_point)) {
1545 			char *cleanup_stdin;
1546 			char *extra_env[2];
1547 
1548 			HAL_INFO (("Cleaning up '%s'", mount_point));
1549 
1550 			extra_env[0] = g_strdup_printf ("HALD_CLEANUP=%s", mount_point);
1551 			extra_env[1] = NULL;
1552 			cleanup_stdin = "\n";
1553 
1554 			hald_runner_run_method (d,
1555 						"hal-storage-cleanup-mountpoint",
1556 						extra_env,
1557 						cleanup_stdin, TRUE,
1558 						0,
1559 						devinfo_storage_cleanup_mountpoint_cb,
1560 						g_strdup (mount_point), NULL);
1561 
1562 			g_free (extra_env[0]);
1563 		}
1564 
1565 		g_free (mount_point);
1566 	}
1567 	g_slist_free (volumes);
1568 
1569 	(void) fclose (fp);
1570 }
1571 
1572 static void
1573 devinfo_volume_force_unmount_cb (HalDevice *d, guint32 exit_type,
1574 		  gint return_code, gchar **error,
1575 		  gpointer data1, gpointer data2)
1576 {
1577 	void *end_token = (void *) data1;
1578 
1579 	HAL_INFO (("devinfo_volume_force_unmount_cb for udi='%s', exit_type=%d, return_code=%d", hal_device_get_udi (d), exit_type, return_code));
1580 
1581 	if (exit_type == HALD_RUN_SUCCESS && error != NULL &&
1582 	    error[0] != NULL && error[1] != NULL) {
1583 		char *exp_name = NULL;
1584 		char *exp_detail = NULL;
1585 
1586 		exp_name = error[0];
1587 		if (error[0] != NULL) {
1588 			exp_detail = error[1];
1589 		}
1590 		HAL_INFO (("failed with '%s' '%s'", exp_name, exp_detail));
1591 	}
1592 
1593 	hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1594 }
1595 
1596 static void
1597 devinfo_volume_force_unmount (HalDevice *d, void *end_token)
1598 {
1599 	const char *device_file;
1600 	const char *mount_point;
1601 	char *unmount_stdin;
1602 	char *extra_env[2];
1603 	extra_env[0] = "HAL_METHOD_INVOKED_BY_UID=0";
1604 	extra_env[1] = NULL;
1605 
1606 	device_file = hal_device_property_get_string (d, "block.device");
1607 	mount_point = hal_device_property_get_string (d, "volume.mount_point");
1608 
1609 	if (mount_point == NULL || strlen (mount_point) == 0 || !hal_util_is_mounted_by_hald (mount_point)) {
1610 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1611 		return;
1612 	}
1613 
1614 	HAL_INFO (("devinfo_volume_force_unmount for udi='%s'", hal_device_get_udi (d)));
1615 
1616 	unmount_stdin = "\n";
1617 
1618 	hald_runner_run_method (d,
1619 				"hal-storage-unmount",
1620 				extra_env,
1621 				unmount_stdin, TRUE,
1622 				0,
1623 				devinfo_volume_force_unmount_cb,
1624 				end_token, NULL);
1625 }
1626 
1627 void
1628 devinfo_volume_hotplug_begin_remove (HalDevice *d, char *devfs_path, void *end_token)
1629 {
1630 	if (hal_device_property_get_bool (d, "volume.is_mounted")) {
1631 		devinfo_volume_force_unmount (d, end_token);
1632 	} else {
1633 		hal_util_callout_device_remove (d, devinfo_callouts_remove_done, end_token, NULL);
1634 	}
1635 }
1636 
1637 
1638 enum {
1639 	LEGACY_CDROM,
1640 	LEGACY_FLOPPY,
1641 	LEGACY_RMDISK
1642 };
1643 
1644 static const char *legacy_media_str[] = {
1645 	"cdrom",
1646 	"floppy",
1647 	"rmdisk"
1648 };
1649 
1650 struct enum_nick {
1651 	const char *type;
1652 	GSList	*nums;
1653 };
1654 
1655 static int
1656 devinfo_storage_get_legacy_media(HalDevice *d)
1657 {
1658 	const char *drive_type;
1659 
1660 	if (hal_device_has_capability (d, "storage.cdrom")) {
1661 		return (LEGACY_CDROM);
1662 	} else if (((drive_type = hal_device_property_get_string (d,
1663 	    "storage.drive_type")) != NULL) && (strcmp (drive_type, "floppy") == 0)) {
1664 		return (LEGACY_FLOPPY);
1665 	} else if (hal_device_property_get_bool (d, "storage.removable") ||
1666 	           hal_device_property_get_bool (d, "storage.hotpluggable")) {
1667 		return (LEGACY_RMDISK);
1668 	} else {
1669 		return (-1);
1670 	}
1671 }
1672 
1673 static gboolean
1674 devinfo_storage_foreach_nick (HalDeviceStore *store, HalDevice *d, gpointer user_data)
1675 {
1676 	struct enum_nick *en = (struct enum_nick *) user_data;
1677 	const char *media_type;
1678 	int media_num;
1679 
1680 	media_type = hal_device_property_get_string (d, "storage.solaris.legacy.media_type");
1681 	media_num = hal_device_property_get_int (d, "storage.solaris.legacy.media_num");
1682 	if ((media_type != NULL) && (strcmp (media_type, en->type) == 0) &&
1683 	    (media_num >= 0)) {
1684 		en->nums = g_slist_prepend (en->nums, GINT_TO_POINTER(media_num));
1685 	}
1686 	return TRUE;
1687 }
1688 
1689 static void
1690 devinfo_storage_append_nickname (HalDevice *d, const char *media_type, int media_num)
1691 {
1692 	char buf[64];
1693 
1694 	if (media_num == 0) {
1695 		hal_device_property_strlist_append (d, "storage.solaris.nicknames", media_type);
1696 	}
1697 	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
1698 	hal_device_property_strlist_append (d, "storage.solaris.nicknames", buf);
1699 }
1700 
1701 static void
1702 devinfo_storage_set_nicknames (HalDevice *d)
1703 {
1704 	int media;
1705 	const char *media_type;
1706 	int media_num;
1707 	GSList *i;
1708 	struct enum_nick en;
1709 	char buf[64];
1710 
1711 	if ((media = devinfo_storage_get_legacy_media (d)) < 0) {
1712 		return;
1713 	}
1714 	media_type = legacy_media_str[media];
1715 
1716 	/* enumerate all storage devices of this media type */
1717 	en.type = media_type;
1718 	en.nums = NULL;
1719 	hal_device_store_foreach (hald_get_gdl (), devinfo_storage_foreach_nick, &en);
1720 
1721 	/* find a free number */
1722 	for (media_num = 0; ; media_num++) {
1723 		for (i = en.nums; i != NULL; i = g_slist_next (i)) {
1724         		if (GPOINTER_TO_INT (i->data) == media_num) {
1725 				break;
1726 			}
1727 		}
1728 		if (i == NULL) {
1729 			break;
1730 		}
1731 	}
1732 	g_slist_free (en.nums);
1733 
1734 	hal_device_property_set_string (d, "storage.solaris.legacy.media_type", media_type);
1735 	hal_device_property_set_int (d, "storage.solaris.legacy.media_num", media_num);
1736 
1737 	/* primary nickname, and also vold-style symdev */
1738 	snprintf(buf, sizeof (buf), "%s%d", media_type, media_num);
1739 	hal_device_property_set_string (d, "storage.solaris.legacy.symdev", buf);
1740 	devinfo_storage_append_nickname(d, media_type, media_num);
1741 
1742 	/* additional nicknames */
1743 	if (media == LEGACY_CDROM) {
1744 		devinfo_storage_append_nickname(d, "cd", media_num);
1745 		devinfo_storage_append_nickname(d, "sr", media_num);
1746 	} else if (media == LEGACY_FLOPPY) {
1747 		devinfo_storage_append_nickname(d, "fd", media_num);
1748 		devinfo_storage_append_nickname(d, "diskette", media_num);
1749 		devinfo_storage_append_nickname(d, "rdiskette", media_num);
1750 	}
1751 }
1752