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