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