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