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