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