1 /*
   2     libparted - a library for manipulating disk partitions
   3     Copyright (C) 1999 - 2005 Free Software Foundation, Inc.
   4         Copyright (C) 2007 Nikhil,Sujay,Nithin,Srivatsa.
   5 
   6     Bug fixes and completion of the module in 2009 by Mark.Logan@sun.com.
   7 
   8     This program is free software; you can redistribute it and/or modify
   9     it under the terms of the GNU General Public License as published by
  10     the Free Software Foundation; either version 2 of the License, or
  11     (at your option) any later version.
  12 
  13     This program is distributed in the hope that it will be useful,
  14     but WITHOUT ANY WARRANTY; without even the implied warranty of
  15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16     GNU General Public License for more details.
  17 
  18     You should have received a copy of the GNU General Public License
  19     along with this program; if not, write to the Free Software
  20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  21 */
  22 
  23 #include <sys/types.h>
  24 #include <sys/mkdev.h>
  25 #include "config.h"
  26 #include "xalloc.h"
  27 #include <sys/dkio.h>
  28 
  29 /*
  30  * __attribute doesn't exist on solaris
  31  */
  32 #define __attribute__(X)        /* nothing */
  33 
  34 #include <sys/vtoc.h>
  35 
  36 #include <parted/parted.h>
  37 #include <parted/debug.h>
  38 #include <parted/solaris.h>
  39 #include <malloc.h>
  40 
  41 #include <ctype.h>
  42 #include <errno.h>
  43 #include <fcntl.h>
  44 #include <libgen.h>
  45 #include <stdint.h>
  46 #include <stdio.h>
  47 #include <stdlib.h>
  48 #include <string.h>
  49 
  50 #include <unistd.h>
  51 #include <dirent.h>
  52 #include <libdiskmgt.h>
  53 
  54 #include <sys/stat.h>
  55 #include <sys/types.h>
  56 #include <sys/swap.h>
  57 #include <sys/mnttab.h>
  58 #include <sys/mntent.h>
  59 
  60 #if ENABLE_NLS
  61 #include <libintl.h>
  62 #define _(String)       dgettext(PACKAGE, String)
  63 #else
  64 #define _(String)       (String)
  65 #endif /* ENABLE_NLS */
  66 
  67 #ifndef UINT_MAX64
  68 #define UINT_MAX64      0xffffffffffffffffULL
  69 #endif
  70 
  71 /*
  72  * Macro to convert a device number into a partition number
  73  */
  74 #define PARTITION(dev)  (minor(dev) & 0x07)
  75 
  76 
  77 char *
  78 canonicalize_file_name(const char *name)
  79 {
  80         char *buf;
  81 
  82         buf = malloc(MAXPATHLEN);
  83         if (!buf) {
  84                 errno = ENOMEM;
  85                 return (NULL);
  86         }
  87 
  88         return (strcpy(buf, name));
  89 }
  90 
  91 static int
  92 _device_stat(PedDevice* dev, struct stat *dev_stat)
  93 {
  94         PED_ASSERT(dev != NULL, return (0));
  95         PED_ASSERT(!dev->external_mode, return (0));
  96 
  97         while (1) {
  98                 if (!stat(dev->path, dev_stat)) {
  99                         return (1);
 100                 } else {
 101                         if (ped_exception_throw(
 102                             PED_EXCEPTION_ERROR,
 103                             PED_EXCEPTION_RETRY_CANCEL,
 104                             _("Could not stat device %s - %s."),
 105                             dev->path, strerror(errno)) != PED_EXCEPTION_RETRY)
 106                                 return (0);
 107                 }
 108         }
 109 }
 110 
 111 static void
 112 _device_set_length_and_sector_size(PedDevice* dev)
 113 {
 114         SolarisSpecific* arch_specific;
 115         PedSector size;
 116         struct dk_minfo dk_minfo;
 117         struct dk_geom dk_geom;
 118 
 119         PED_ASSERT(dev != NULL, return);
 120         PED_ASSERT(dev->open_count > 0, return);
 121 
 122         arch_specific = SOLARIS_SPECIFIC(dev);
 123 
 124         dev->sector_size = PED_SECTOR_SIZE_DEFAULT;
 125         dev->phys_sector_size = PED_SECTOR_SIZE_DEFAULT;
 126 
 127         /* this ioctl requires the raw device */
 128         if (ioctl(arch_specific->fd, DKIOCGMEDIAINFO, &dk_minfo) < 0) {
 129                 printf("_device_get_length: ioctl DKIOCGMEDIAINFO failed\n");
 130                 ped_exception_throw(
 131                     PED_EXCEPTION_BUG,
 132                     PED_EXCEPTION_CANCEL,
 133                     _("Unable to determine the size of %s (%s)."),
 134                     dev->path,
 135                     strerror(errno));
 136         } else {
 137                 size = dk_minfo.dki_capacity;
 138                 dev->length = size;
 139                 dev->sector_size = dk_minfo.dki_lbsize;
 140                 if (dev->sector_size != PED_SECTOR_SIZE_DEFAULT) {
 141                         ped_exception_throw(
 142                             PED_EXCEPTION_WARNING,
 143                             PED_EXCEPTION_OK,
 144                             _("Device %s has a logical sector size of "
 145                             "%lld. Not all parts of GNU Parted support "
 146                             "this at the moment, and the working code "
 147                             "is HIGHLY EXPERIMENTAL.\n"),
 148                             dev->path, dev->sector_size);
 149                 }
 150                 if (size > 0) {
 151                         return;
 152                 }
 153         }
 154 
 155         /*
 156          * On some disks DKIOCGMEDIAINFO doesn't work, it returns 0,
 157          * so try DKIOCG_PHYGEOM next.
 158          */
 159         /* this ioctl requires the raw device */
 160         if (ioctl(arch_specific->fd, DKIOCG_PHYGEOM, &dk_geom) < 0) {
 161                 printf("_device_get_length: ioctl DKIOCG_PHYGEOM failed\n");
 162                 ped_exception_throw(
 163                     PED_EXCEPTION_BUG,
 164                     PED_EXCEPTION_CANCEL,
 165                     _("Unable to determine the size of %s (%s)."),
 166                     dev->path, strerror(errno));
 167 
 168                 return;
 169         }
 170 
 171         /*
 172          * XXX For large disks, I am adding 16064 to the size of the disk.
 173          * Solaris underreports the size of the disk, because it rounds down to
 174          * a multiple of 16065. This causes a problem with Vista because Vista
 175          * creates a partition that occupies the whole disk, including the
 176          * blocks at the end of the disk that Solaris loses.
 177          */
 178         if (dk_geom.dkg_nhead == 255 && dk_geom.dkg_nsect == 63) {
 179                 size = ((PedSector) dk_geom.dkg_pcyl *
 180                     (255 * 63)) + ((255*63)-1);
 181         } else {
 182                 size = (PedSector) dk_geom.dkg_pcyl *
 183                     dk_geom.dkg_nhead * dk_geom.dkg_nsect;
 184         }
 185 
 186         dev->length = size;
 187 }
 188 
 189 static int
 190 _device_probe_geometry(PedDevice* dev)
 191 {
 192         SolarisSpecific* arch_specific;
 193         struct stat dev_stat;
 194         struct dk_geom dk_geom;
 195 
 196         PED_ASSERT(dev != NULL, return (0));
 197         PED_ASSERT(dev->open_count > 0, return (0));
 198 
 199         arch_specific = SOLARIS_SPECIFIC(dev);
 200 
 201         _device_set_length_and_sector_size(dev);
 202         if (dev->length == 0) {
 203                 printf("_device_probe_geometry: _device_get_length = 0\n");
 204                 return (0);
 205         }
 206 
 207         dev->bios_geom.sectors = 63;
 208         dev->bios_geom.heads = 255;
 209         dev->bios_geom.cylinders = dev->length / (63 * 255);
 210         if ((ioctl(arch_specific->fd, DKIOCG_PHYGEOM, &dk_geom) >= 0) &&
 211             dk_geom.dkg_nsect && dk_geom.dkg_nhead) {
 212                 dev->hw_geom.sectors = dk_geom.dkg_nsect;
 213                 dev->hw_geom.heads = dk_geom.dkg_nhead;
 214                 dev->hw_geom.cylinders = dk_geom.dkg_pcyl;
 215         } else {
 216                 perror("_device_probe_geometry: DKIOCG_PHYGEOM");
 217                 dev->hw_geom = dev->bios_geom;
 218         }
 219 
 220         return (1);
 221 }
 222 
 223 static int
 224 init_ide(PedDevice *dev)
 225 {
 226         struct stat dev_stat;
 227 
 228         PED_ASSERT(dev != NULL, return (0));
 229 
 230         if (!_device_stat(dev, &dev_stat)) {
 231                 printf("init_ide: _device_stat failed\n");
 232                 goto error;
 233         }
 234         if (!ped_device_open(dev)) {
 235                 printf("init_ide: ped_device_open failed\n");
 236                 goto error;
 237         }
 238         if (!_device_probe_geometry(dev)) {
 239                 printf("init_ide: _device_probe_geometry failed\n");
 240                 goto error_close_dev;
 241         }
 242 
 243         ped_device_close(dev);
 244         return (1);
 245 
 246 error_close_dev:
 247         ped_device_close(dev);
 248 error:
 249         return (0);
 250 }
 251 
 252 static PedDevice*
 253 solaris_new(const char *path)
 254 {
 255         PedDevice* dev;
 256 
 257         PED_ASSERT(path != NULL, return (NULL));
 258 
 259         dev = (PedDevice*) ped_malloc(sizeof (PedDevice));
 260         if (!dev)
 261                 goto error;
 262 
 263         dev->path = strdup(path);
 264         if (!dev->path)
 265                 goto error_free_dev;
 266 
 267         dev->arch_specific
 268             = (SolarisSpecific*) ped_malloc(sizeof (SolarisSpecific));
 269         if (!dev->arch_specific)
 270                 goto error_free_path;
 271 
 272         dev->open_count = 0;
 273         dev->read_only = 0;
 274         dev->external_mode = 0;
 275         dev->dirty = 0;
 276         dev->boot_dirty = 0;
 277         dev->model = strdup("Generic Ide");
 278         dev->type = PED_DEVICE_IDE;
 279         if (!init_ide(dev)) {
 280                 goto error_free_arch_specific;
 281         }
 282 
 283         return (dev);
 284 
 285 error_free_arch_specific:
 286         ped_free(dev->arch_specific);
 287         ped_free(dev->model);
 288 error_free_path:
 289         ped_free(dev->path);
 290 error_free_dev:
 291         ped_free(dev);
 292 error:
 293         return (NULL);
 294 }
 295 
 296 static void
 297 solaris_destroy(PedDevice* dev)
 298 {
 299         PED_ASSERT(dev != NULL, return);
 300 
 301         ped_free(dev->arch_specific);
 302         ped_free(dev->model);
 303         ped_free(dev->path);
 304         ped_free(dev);
 305 }
 306 
 307 /*
 308  * This function constructs the Solaris device name for
 309  * partition num on a disk given the *p0 device for that disk.
 310  * For example: partition 2 of /dev/dsk/c0d0p0 becomes /dev/dsk/c0d0p2.
 311  */
 312 static char *
 313 _device_get_part_path(PedDevice* dev, int num)
 314 {
 315         int path_len = strlen(dev->path);
 316         int result_len = path_len + 16;
 317         char *result;
 318 
 319         PED_ASSERT(dev != NULL, return (NULL));
 320         PED_ASSERT(num >= 1, return (NULL));
 321 
 322         result = (char *)ped_malloc(result_len);
 323         if (!result)
 324                 return (NULL);
 325 
 326         strncpy(result, dev->path, result_len);
 327         if (path_len > 10 && result[path_len - 2] == 'p' &&
 328             result[path_len - 1] == '0') {
 329                 (void) snprintf(result + path_len - 1,
 330                     result_len - path_len + 1, "%d", num);
 331         } else {
 332                 (void) snprintf(result, result_len, "partition %d", num);
 333         }
 334 
 335         return (result);
 336 }
 337 
 338 static struct swaptable *
 339 getswapentries(void)
 340 {
 341         register struct swaptable *st;
 342         register struct swapent *swapent;
 343         int     i, num;
 344         char    fullpathname[MAXPATHLEN];
 345 
 346         /*
 347          * get the number of swap entries
 348          */
 349         if ((num = swapctl(SC_GETNSWP, (void *)NULL)) == -1) {
 350                 perror("getswapentries: swapctl SC_GETNSWP");
 351                 return (NULL);
 352         }
 353         if (num == 0)
 354                 return (NULL);
 355         if ((st = (swaptbl_t *)malloc(num * sizeof (swapent_t) + sizeof (int)))
 356             == NULL) {
 357                 printf("getswapentries: malloc 1 failed.\n");
 358                 return (NULL);
 359         }
 360         swapent = st->swt_ent;
 361         for (i = 0; i < num; i++, swapent++) {
 362                 if ((swapent->ste_path = malloc(MAXPATHLEN)) == NULL) {
 363                         printf("getswapentries: malloc 2 failed.\n");
 364                         goto error;
 365                 }
 366         }
 367         st->swt_n = num;
 368         if ((num = swapctl(SC_LIST, (void *)st)) == -1) {
 369                 perror("getswapentries: swapctl SC_LIST");
 370                 goto error;
 371         }
 372         swapent = st->swt_ent;
 373         for (i = 0; i < num; i++, swapent++) {
 374                 if (*swapent->ste_path != '/') {
 375                         printf("getswapentries: %s\n", swapent->ste_path);
 376                         (void) snprintf(fullpathname, sizeof (fullpathname),
 377                             "/dev/%s", swapent->ste_path);
 378                         (void) strcpy(swapent->ste_path, fullpathname);
 379                 }
 380         }
 381 
 382         return (st);
 383 
 384 error:
 385         free(st);
 386         return (NULL);
 387 }
 388 
 389 static void
 390 freeswapentries(st)
 391 struct swaptable *st;
 392 {
 393         register struct swapent *swapent;
 394         int i;
 395 
 396         swapent = st->swt_ent;
 397         for (i = 0; i < st->swt_n; i++, swapent++)
 398                 free(swapent->ste_path);
 399         free(st);
 400 }
 401 
 402 /*
 403  *  function getpartition:
 404  */
 405 static int
 406 getpartition(PedDevice* dev, char *pathname)
 407 {
 408         SolarisSpecific* arch_specific;
 409         int             mfd;
 410         struct dk_cinfo dkinfo;
 411         struct dk_cinfo cur_disk_dkinfo;
 412         struct stat     stbuf;
 413         char            raw_device[MAXPATHLEN];
 414         int             found = -1;
 415 
 416         PED_ASSERT(dev != NULL, return (found));
 417         PED_ASSERT(pathname != NULL, return (found));
 418 
 419         arch_specific = SOLARIS_SPECIFIC(dev);
 420 
 421         /*
 422          * Map the block device name to the raw device name.
 423          * If it doesn't appear to be a device name, skip it.
 424          */
 425         if (strncmp(pathname, "/dev/", 5))
 426                 return (found);
 427         (void) strcpy(raw_device, "/dev/r");
 428         (void) strcat(raw_device, pathname + strlen("/dev/"));
 429         /*
 430          * Determine if this appears to be a disk device.
 431          * First attempt to open the device.  If if fails, skip it.
 432          */
 433         if ((mfd = open(raw_device, O_RDWR | O_NDELAY)) < 0) {
 434                 return (found);
 435         }
 436         if (fstat(mfd, &stbuf) == -1) {
 437                 perror("getpartition: fstat raw_device");
 438                 (void) close(mfd);
 439                 return (found);
 440         }
 441         /*
 442          * Must be a character device
 443          */
 444         if (!S_ISCHR(stbuf.st_mode)) {
 445                 printf("getpartition: not character device\n");
 446                 (void) close(mfd);
 447                 return (found);
 448         }
 449         /*
 450          * Attempt to read the configuration info on the disk.
 451          */
 452         if (ioctl(mfd, DKIOCINFO, &dkinfo) < 0) {
 453                 perror("getpartition: ioctl DKIOCINFO raw_device");
 454                 (void) close(mfd);
 455                 return (found);
 456         }
 457         /*
 458          * Finished with the opened device
 459          */
 460         (void) close(mfd);
 461 
 462         /*
 463          * Now get the info about the current disk
 464          */
 465         if (ioctl(arch_specific->fd, DKIOCINFO, &cur_disk_dkinfo) < 0) {
 466                 (void) close(mfd);
 467                 return (found);
 468         }
 469 
 470         /*
 471          * If it's not the disk we're interested in, it doesn't apply.
 472          */
 473         if (cur_disk_dkinfo.dki_ctype != dkinfo.dki_ctype ||
 474             cur_disk_dkinfo.dki_cnum != dkinfo.dki_cnum ||
 475             cur_disk_dkinfo.dki_unit != dkinfo.dki_unit ||
 476             strcmp(cur_disk_dkinfo.dki_dname, dkinfo.dki_dname) != 0) {
 477                 return (found);
 478         }
 479 
 480         /*
 481          *  Extract the partition that is mounted.
 482          */
 483         return (PARTITION(stbuf.st_rdev));
 484 }
 485 
 486 /*
 487  * This Routine checks to see if there are partitions used for swapping overlaps
 488  * a given portion of a disk. If the start parameter is < 0, it means
 489  * that the entire disk should be checked
 490  */
 491 static int
 492 checkswap(PedDevice* dev, diskaddr_t start, diskaddr_t end)
 493 {
 494         SolarisSpecific* arch_specific;
 495         struct extvtoc  extvtoc;
 496         struct swaptable *st;
 497         struct swapent  *swapent;
 498         int             i;
 499         int             found = 0;
 500         int             part;
 501         diskaddr_t      p_start;
 502         diskaddr_t      p_size;
 503 
 504         PED_ASSERT(dev != NULL, return (0));
 505 
 506         arch_specific = SOLARIS_SPECIFIC(dev);
 507 
 508         if (ioctl(arch_specific->fd, DKIOCGEXTVTOC, &extvtoc) == -1) {
 509                 return (0);
 510         }
 511 
 512         /*
 513          * check for swap entries
 514          */
 515         st = getswapentries();
 516         /*
 517          * if there are no swap entries return.
 518          */
 519         if (st == (struct swaptable *)NULL)
 520                 return (0);
 521         swapent = st->swt_ent;
 522         for (i = 0; i < st->swt_n; i++, swapent++) {
 523                 if ((part = getpartition(dev, swapent->ste_path)) != -1) {
 524                         if (start == UINT_MAX64) {
 525                                 found = -1;
 526                                 break;
 527                         }
 528                         p_start = extvtoc.v_part[part].p_start;
 529                         p_size = extvtoc.v_part[part].p_size;
 530                         if (start >= p_start + p_size || end < p_start) {
 531                                         continue;
 532                         }
 533                         found = -1;
 534                         break;
 535                 }
 536         }
 537         freeswapentries(st);
 538 
 539         return (found);
 540 }
 541 
 542 /*
 543  * Determines if there are partitions that are a part of an SVM, VxVM, zpool
 544  * volume or a live upgrade device,  overlapping a given portion of a disk.
 545  * Mounts and swap devices are checked in legacy format code.
 546  */
 547 static int
 548 checkdevinuse(PedDevice *dev, diskaddr_t start, diskaddr_t end, int print)
 549 {
 550         int             error;
 551         int             found = 0;
 552         int             check = 0;
 553         int             i;
 554         int             part = 0;
 555         uint64_t        slice_start, slice_size;
 556         dm_descriptor_t *slices = NULL;
 557         nvlist_t        *attrs = NULL;
 558         char            *usage;
 559         char            *name;
 560         char            cur_disk_path[MAXPATHLEN];
 561         char            *pcur_disk_path;
 562 
 563         PED_ASSERT(dev != NULL, return (found));
 564 
 565         /*
 566          * Truncate the characters following "d*", such as "s*" or "p*"
 567          */
 568         strcpy(cur_disk_path, dev->path);
 569         pcur_disk_path = basename(cur_disk_path);
 570         name = strrchr(pcur_disk_path, 'd');
 571         if (name) {
 572                 name++;
 573                 for (; (*name <= '9') && (*name >= '0'); name++)
 574                         ;
 575                 *name = (char)0;
 576         }
 577 
 578         /*
 579          * For format, we get basic 'in use' details from libdiskmgt. After
 580          * that we must do the appropriate checking to see if the 'in use'
 581          * details require a bit of additional work.
 582          */
 583 
 584         dm_get_slices(pcur_disk_path, &slices, &error);
 585         if (error) {
 586                 /*
 587                  * If ENODEV, it actually means the device is not in use.
 588                  * We will return (0) without displaying error.
 589                  */
 590                 if (error != ENODEV) {
 591                         printf("checkdevinuse: Error1 occurred with device in "
 592                             "use checking: %s\n", strerror(error));
 593                         return (found);
 594                 }
 595         }
 596         if (slices == NULL)
 597                 return (found);
 598 
 599         for (i = 0; slices[i] != NULL; i++) {
 600                 /*
 601                  * If we are checking the whole disk
 602                  * then any and all in use data is
 603                  * relevant.
 604                  */
 605                 if (start == UINT_MAX64) {
 606                         name = dm_get_name(slices[i], &error);
 607                         if (error != 0 || !name) {
 608                                 printf("checkdevinuse: Error2 occurred with "
 609                                     "device in use checking: %s\n",
 610                                     strerror(error));
 611                                 continue;
 612                         }
 613                         printf("checkdevinuse: name1 %s\n", name);
 614                         if (dm_inuse(name, &usage, DM_WHO_FORMAT, &error) ||
 615                             error) {
 616                                 if (error != 0) {
 617                                         dm_free_name(name);
 618                                         name = NULL;
 619                                         printf("checkdevinuse: Error3 "
 620                                             "occurred with device "
 621                                             "in use checking: %s\n",
 622                                             strerror(error));
 623                                         continue;
 624                                 }
 625                                 dm_free_name(name);
 626                                 name = NULL;
 627                                 /*
 628                                  * If this is a dump device, then it is
 629                                  * a failure. You cannot format a slice
 630                                  * that is a dedicated dump device.
 631                                  */
 632 
 633                                 if (strstr(usage, DM_USE_DUMP)) {
 634                                         if (print) {
 635                                                 printf(usage);
 636                                                 free(usage);
 637                                         }
 638                                         dm_free_descriptors(slices);
 639                                         return (1);
 640                                 }
 641                                 /*
 642                                  * We really found a device that is in use.
 643                                  * Set 'found' for the return value.
 644                                  */
 645                                 found ++;
 646                                 check = 1;
 647                                 if (print) {
 648                                         printf(usage);
 649                                         free(usage);
 650                                 }
 651                         }
 652                 } else {
 653                         /*
 654                          * Before getting the in use data, verify that the
 655                          * current slice is within the range we are checking.
 656                          */
 657                         attrs = dm_get_attributes(slices[i], &error);
 658                         if (error) {
 659                                 printf("checkdevinuse: Error4 occurred with "
 660                                     "device in use checking: %s\n",
 661                                     strerror(error));
 662                                 continue;
 663                         }
 664                         if (attrs == NULL) {
 665                                 continue;
 666                         }
 667 
 668                         (void) nvlist_lookup_uint64(attrs, DM_START,
 669                             &slice_start);
 670                         (void) nvlist_lookup_uint64(attrs, DM_SIZE,
 671                             &slice_size);
 672                         if (start >= (slice_start + slice_size) ||
 673                             (end < slice_start)) {
 674                                 nvlist_free(attrs);
 675                                 attrs = NULL;
 676                                 continue;
 677                         }
 678                         name = dm_get_name(slices[i], &error);
 679                         if (error != 0 || !name) {
 680                                 printf("checkdevinuse: Error5 occurred with "
 681                                     "device in use checking: %s\n",
 682                                     strerror(error));
 683                                 nvlist_free(attrs);
 684                                 attrs = NULL;
 685                                 continue;
 686                         }
 687                         if (dm_inuse(name, &usage,
 688                             DM_WHO_FORMAT, &error) || error) {
 689                                 if (error != 0) {
 690                                         dm_free_name(name);
 691                                         name = NULL;
 692                                         printf("checkdevinuse: Error6 "
 693                                             "occurred with device "
 694                                             "in use checking: %s\n",
 695                                             strerror(error));
 696                                         nvlist_free(attrs);
 697                                         attrs = NULL;
 698                                         continue;
 699                                 }
 700                                 dm_free_name(name);
 701                                 name = NULL;
 702                                 /*
 703                                  * If this is a dump device, then it is
 704                                  * a failure. You cannot format a slice
 705                                  * that is a dedicated dump device.
 706                                  */
 707                                 if (strstr(usage, DM_USE_DUMP)) {
 708                                         if (print) {
 709                                                 printf(usage);
 710                                                 free(usage);
 711                                         }
 712                                         dm_free_descriptors(slices);
 713                                         nvlist_free(attrs);
 714                                         return (1);
 715                                 }
 716                                 /*
 717                                  * We really found a device that is in use.
 718                                  * Set 'found' for the return value.
 719                                  */
 720                                 found ++;
 721                                 check = 1;
 722                                 if (print) {
 723                                         printf(usage);
 724                                         free(usage);
 725                                 }
 726                         }
 727                 }
 728                 /*
 729                  * If check is set it means we found a slice(the current slice)
 730                  * on this device in use in some way.  We potentially want
 731                  * to check this slice when labeling is requested.
 732                  */
 733                 if (check) {
 734                         name = dm_get_name(slices[i], &error);
 735                         if (error != 0 || !name) {
 736                                 printf("checkdevinuse: Error7 occurred with "
 737                                     "device in use checking: %s\n",
 738                                     strerror(error));
 739                                 nvlist_free(attrs);
 740                                 attrs = NULL;
 741                                 continue;
 742                         }
 743                         part = getpartition(dev, name);
 744                         dm_free_name(name);
 745                         name = NULL;
 746                         check = 0;
 747                 }
 748                 /*
 749                  * If we have attributes then we have successfully
 750                  * found the slice we were looking for and we also
 751                  * know this means we are not searching the whole
 752                  * disk so break out of the loop
 753                  * now.
 754                  */
 755                 if (attrs) {
 756                         nvlist_free(attrs);
 757                         break;
 758                 }
 759         }
 760 
 761         if (slices) {
 762                 dm_free_descriptors(slices);
 763         }
 764 
 765         return (found);
 766 }
 767 
 768 /*
 769  * This routine checks to see if there are mounted partitions overlapping
 770  * a given portion of a disk.  If the start parameter is < 0, it means
 771  * that the entire disk should be checked.
 772  */
 773 static int
 774 checkmount(PedDevice* dev, diskaddr_t start, diskaddr_t end)
 775 {
 776         SolarisSpecific* arch_specific;
 777         struct extvtoc  extvtoc;
 778         diskaddr_t      p_start;
 779         diskaddr_t      p_size;
 780         FILE            *fp;
 781         int             found = 0;
 782         int             part;
 783         struct mnttab   mnt_record;
 784         struct mnttab   *mp = &mnt_record;
 785 
 786         PED_ASSERT(dev != NULL, return (found));
 787 
 788         arch_specific = SOLARIS_SPECIFIC(dev);
 789 
 790         if (ioctl(arch_specific->fd, DKIOCGEXTVTOC, &extvtoc) == -1) {
 791                 return (0);
 792         }
 793 
 794         /*
 795          * Open the mount table.
 796          */
 797         fp = fopen(MNTTAB, "r");
 798         if (fp == NULL) {
 799                 printf("checkmount: Unable to open mount table.\n");
 800                 return (0);
 801         }
 802         /*
 803          * Loop through the mount table until we run out of entries.
 804          */
 805         while ((getmntent(fp, mp)) != -1) {
 806 
 807                 if ((part = getpartition(dev, mp->mnt_special)) == -1)
 808                         continue;
 809 
 810                 /*
 811                  * It's a mount on the disk we're checking.  If we are
 812                  * checking whole disk, then we found trouble.  We can
 813                  * quit searching.
 814                  */
 815                 if (start == UINT_MAX64) {
 816                         found = -1;
 817                         break;
 818                 }
 819 
 820                 /*
 821                  * If the partition overlaps the zone we're checking,
 822                  * then we found trouble.  We can quit searching.
 823                  */
 824                 p_start = extvtoc.v_part[part].p_start;
 825                 p_size = extvtoc.v_part[part].p_size;
 826                 if (start >= p_start + p_size || end < p_start) {
 827                         continue;
 828                 }
 829                 found = -1;
 830                 break;
 831         }
 832         /*
 833          * Close down the mount table.
 834          */
 835         (void) fclose(fp);
 836 
 837         return (found);
 838 }
 839 
 840 /*
 841  * Return 1 if the device is busy, 0 otherwise.
 842  */
 843 static int
 844 solaris_is_busy(PedDevice* dev)
 845 {
 846         PED_ASSERT(dev != NULL, return (0));
 847         PED_ASSERT(dev->open_count > 0, return (0));
 848 
 849         if (checkmount(dev, (diskaddr_t)-1, (diskaddr_t)-1))
 850                 return (1);
 851 
 852         if (checkswap(dev, (diskaddr_t)-1, (diskaddr_t)-1))
 853                 return (1);
 854 
 855         if (checkdevinuse(dev, (diskaddr_t)-1, (diskaddr_t)-1, 1))
 856                 return (1);
 857 
 858         return (0);
 859 }
 860 
 861 /*
 862  * This will accept a dev->path that looks like this:
 863  *      /devices/pci@0,0/pci-ide@1f,2/ide@0/cmdk@0,0:q
 864  *      /devices/pci@0,0/pci-ide@1f,2/ide@0/cmdk@0,0:q,raw
 865  * or this:
 866  *      /dev/dsk/c0d0p0
 867  *      /dev/rdsk/c0d0p0
 868  * It has to open the raw device, so it converts to it locally, if necessary.
 869  */
 870 static int
 871 solaris_open(PedDevice* dev)
 872 {
 873         SolarisSpecific* arch_specific;
 874         char rawname[MAXPATHLEN];
 875 
 876         PED_ASSERT(dev != NULL, return (0));
 877 
 878         arch_specific = SOLARIS_SPECIFIC(dev);
 879 
 880         /*
 881          * Convert to the raw device, unless it already is.
 882          */
 883         if (strncmp(dev->path, "/devices", 8) == 0) {
 884                 if (strncmp(&dev->path[strlen(dev->path)-4], ",raw", 4)) {
 885                         snprintf(rawname, sizeof (rawname), "%s,raw",
 886                             dev->path);
 887                 } else {
 888                         strcpy(rawname, dev->path);
 889                 }
 890         } else {
 891                 /*
 892                  * Assumes it is of the form: /dev/dsk/ or /dev/rdsk/
 893                  */
 894                 if (strncmp(dev->path, "/dev/dsk/", 9) == 0) {
 895                         snprintf(rawname, sizeof (rawname), "/dev/rdsk/%s",
 896                             &dev->path[9]);
 897                 } else {
 898                         strcpy(rawname, dev->path);
 899                 }
 900         }
 901 
 902 retry:
 903         arch_specific->fd = open(rawname, O_RDWR);
 904 
 905         if (arch_specific->fd == -1) {
 906                 char *rw_error_msg = strerror(errno);
 907 
 908                 arch_specific->fd = open(rawname, O_RDONLY);
 909 
 910                 if (arch_specific->fd == -1) {
 911                         printf("solaris_open: open(\"%s\") failed\n", rawname);
 912                         if (ped_exception_throw(
 913                             PED_EXCEPTION_ERROR,
 914                             PED_EXCEPTION_RETRY_CANCEL,
 915                             _("Error opening %s: %s"),
 916                             rawname, strerror(errno)) != PED_EXCEPTION_RETRY) {
 917                                 return (0);
 918                         } else {
 919                                 goto retry;
 920                         }
 921                 } else {
 922                         ped_exception_throw(
 923                             PED_EXCEPTION_WARNING,
 924                             PED_EXCEPTION_OK,
 925                             _("Unable to open %s read-write (%s). %s has "
 926                             "been opened read-only."),
 927                             rawname, rw_error_msg, rawname);
 928                         dev->read_only = 1;
 929                 }
 930         } else {
 931                 dev->read_only = 0;
 932         }
 933 
 934         return (1);
 935 }
 936 
 937 static int
 938 solaris_refresh_open(PedDevice* dev)
 939 {
 940         return (1);
 941 }
 942 
 943 static int
 944 solaris_close(PedDevice* dev)
 945 {
 946         SolarisSpecific* arch_specific;
 947 
 948         PED_ASSERT(dev != NULL, return (0));
 949 
 950         arch_specific = SOLARIS_SPECIFIC(dev);
 951 
 952         close(arch_specific->fd);
 953         return (1);
 954 }
 955 
 956 static int
 957 _do_fsync(PedDevice* dev)
 958 {
 959         SolarisSpecific*        arch_specific;
 960         int                     status;
 961         PedExceptionOption      ex_status;
 962 
 963         PED_ASSERT(dev != NULL, return (0));
 964         PED_ASSERT(dev->open_count > 0, return (0));
 965 
 966         arch_specific = SOLARIS_SPECIFIC(dev);
 967 
 968         while (1) {
 969                 status = fsync(arch_specific->fd);
 970                 if (status >= 0)
 971                         break;
 972 
 973                 ex_status = ped_exception_throw(
 974                     PED_EXCEPTION_ERROR,
 975                     PED_EXCEPTION_RETRY_IGNORE_CANCEL,
 976                     _("%s during fsync on %s"),
 977                     strerror(errno), dev->path);
 978 
 979                 switch (ex_status) {
 980                         case PED_EXCEPTION_IGNORE:
 981                                 return (1);
 982 
 983                         case PED_EXCEPTION_RETRY:
 984                                 break;
 985 
 986                         case PED_EXCEPTION_UNHANDLED:
 987                                 ped_exception_catch();
 988                         case PED_EXCEPTION_CANCEL:
 989                                 return (0);
 990                 }
 991         }
 992         return (1);
 993 }
 994 
 995 static int
 996 solaris_refresh_close(PedDevice* dev)
 997 {
 998         if (dev->dirty)
 999                 _do_fsync(dev);
1000         return (1);
1001 }
1002 
1003 static int
1004 _device_seek(const PedDevice* dev, PedSector sector)
1005 {
1006         SolarisSpecific* arch_specific;
1007 
1008         PED_ASSERT(dev != NULL, return (0));
1009         PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
1010         PED_ASSERT(dev->open_count > 0, return (0));
1011         PED_ASSERT(!dev->external_mode, return (0));
1012 
1013         arch_specific = SOLARIS_SPECIFIC(dev);
1014 
1015         if (sizeof (off_t) < 8) {
1016                 off64_t pos = (off64_t)(sector * dev->sector_size);
1017                 return (lseek64(arch_specific->fd, pos, SEEK_SET) == pos);
1018         } else {
1019                 off_t pos = sector * dev->sector_size;
1020                 return (lseek(arch_specific->fd, pos, SEEK_SET) == pos);
1021         }
1022 }
1023 
1024 static int
1025 solaris_read(const PedDevice* dev, void* vbuffer, PedSector start,
1026     PedSector count)
1027 {
1028         SolarisSpecific* arch_specific;
1029         int status;
1030         PedExceptionOption ex_status;
1031         size_t read_length = count * dev->sector_size;
1032         void *diobuf;
1033         char *buffer = vbuffer;
1034 
1035         PED_ASSERT(dev != NULL, return (0));
1036         PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
1037         PED_ASSERT(dev->open_count > 0, return (0));
1038         PED_ASSERT(!dev->external_mode, return (0));
1039 
1040         arch_specific = SOLARIS_SPECIFIC(dev);
1041 
1042         while (1) {
1043                 if (_device_seek(dev, start))
1044                         break;
1045 
1046                 ex_status = ped_exception_throw(
1047                     PED_EXCEPTION_ERROR,
1048                     PED_EXCEPTION_RETRY_IGNORE_CANCEL,
1049                     _("%s during seek for read on %s"),
1050                     strerror(errno), dev->path);
1051 
1052                 switch (ex_status) {
1053                         case PED_EXCEPTION_IGNORE:
1054                                 return (1);
1055 
1056                         case PED_EXCEPTION_RETRY:
1057                                 break;
1058 
1059                         case PED_EXCEPTION_UNHANDLED:
1060                                 ped_exception_catch();
1061                         case PED_EXCEPTION_CANCEL:
1062                                 return (0);
1063                 }
1064         }
1065 
1066         diobuf = memalign(dev->sector_size, read_length);
1067         if (diobuf == NULL) {
1068                 printf("solaris_read: cannot memalign %u\n", read_length);
1069                 return (0);
1070         }
1071 
1072         while (1) {
1073                 status = read(arch_specific->fd, diobuf, read_length);
1074 
1075                 if (status > 0)
1076                         memcpy(buffer, diobuf, status);
1077 
1078                 if (status == read_length)
1079                         break;
1080 
1081                 if (status > 0) {
1082                         printf("solaris_read: partial read %d of %d\n",
1083                             status, read_length);
1084                         read_length -= status;
1085                         buffer += status;
1086                         continue;
1087                 }
1088 
1089                 ex_status = ped_exception_throw(
1090                     PED_EXCEPTION_ERROR,
1091                     PED_EXCEPTION_RETRY_IGNORE_CANCEL,
1092                     _("%s during read on %s"),
1093                     strerror(errno),
1094                     dev->path);
1095 
1096                 switch (ex_status) {
1097                         case PED_EXCEPTION_IGNORE:
1098                                 free(diobuf);
1099                                 return (1);
1100 
1101                         case PED_EXCEPTION_RETRY:
1102                                 break;
1103 
1104                         case PED_EXCEPTION_UNHANDLED:
1105                                 ped_exception_catch();
1106                         case PED_EXCEPTION_CANCEL:
1107                                 free(diobuf);
1108                                 return (0);
1109                 }
1110         }
1111 
1112         free(diobuf);
1113 
1114         return (1);
1115 }
1116 
1117 static int
1118 solaris_write(PedDevice* dev, const void* buffer, PedSector start,
1119     PedSector count)
1120 {
1121         SolarisSpecific* arch_specific;
1122         int status;
1123         PedExceptionOption ex_status;
1124         size_t write_length = count * dev->sector_size;
1125         char *diobuf;
1126         char *diobuf_start;
1127 
1128         PED_ASSERT(dev != NULL, return (0));
1129         PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0, return (0));
1130         PED_ASSERT(dev->open_count > 0, return (0));
1131         PED_ASSERT(!dev->external_mode, return (0));
1132 
1133         arch_specific = SOLARIS_SPECIFIC(dev);
1134 
1135         if (dev->read_only) {
1136                 if (ped_exception_throw(
1137                     PED_EXCEPTION_ERROR,
1138                     PED_EXCEPTION_IGNORE_CANCEL,
1139                     _("Can't write to %s, because it is opened read-only."),
1140                     dev->path) != PED_EXCEPTION_IGNORE)
1141                         return (0);
1142                 else
1143                         return (1);
1144         }
1145 
1146         while (1) {
1147                 if (_device_seek(dev, start))
1148                         break;
1149 
1150                 ex_status = ped_exception_throw(
1151                     PED_EXCEPTION_ERROR, PED_EXCEPTION_RETRY_IGNORE_CANCEL,
1152                     _("%s during seek for write on %s"),
1153                     strerror(errno), dev->path);
1154 
1155                 switch (ex_status) {
1156                         case PED_EXCEPTION_IGNORE:
1157                                 return (1);
1158 
1159                         case PED_EXCEPTION_RETRY:
1160                                 break;
1161 
1162                         case PED_EXCEPTION_UNHANDLED:
1163                                 ped_exception_catch();
1164                         case PED_EXCEPTION_CANCEL:
1165                                 return (0);
1166                 }
1167         }
1168 
1169 #ifdef READ_ONLY
1170         printf("solaris_write(\"%s\", %p, %d, %d)\n",
1171             dev->path, buffer, (int)start, (int)count);
1172 #else
1173         dev->dirty = 1;
1174 
1175         diobuf = memalign((size_t)PED_SECTOR_SIZE_DEFAULT, write_length);
1176         if (diobuf == NULL) {
1177                 printf("solaris_write: cannot memalign %u\n", write_length);
1178                 return (0);
1179         }
1180 
1181         memcpy(diobuf, buffer, write_length);
1182         diobuf_start = diobuf;
1183         while (1) {
1184                 status = write(arch_specific->fd, diobuf, write_length);
1185                 if (status == write_length)
1186                         break;
1187                 if (status > 0) {
1188                         printf("solaris_write: partial write %d of %d\n",
1189                             status, write_length);
1190                         write_length -= status;
1191                         diobuf += status;
1192                         continue;
1193                 }
1194 
1195                 ex_status = ped_exception_throw(
1196                     PED_EXCEPTION_ERROR,
1197                     PED_EXCEPTION_RETRY_IGNORE_CANCEL,
1198                     _("%s during write on %s"),
1199                     strerror(errno), dev->path);
1200 
1201                 switch (ex_status) {
1202                         case PED_EXCEPTION_IGNORE:
1203                                 free(diobuf_start);
1204                                 return (1);
1205 
1206                         case PED_EXCEPTION_RETRY:
1207                                 break;
1208 
1209                         case PED_EXCEPTION_UNHANDLED:
1210                                 ped_exception_catch();
1211                         case PED_EXCEPTION_CANCEL:
1212                                 free(diobuf_start);
1213                                 return (0);
1214                 }
1215         }
1216         free(diobuf_start);
1217 #endif /* !READ_ONLY */
1218 
1219         return (1);
1220 }
1221 
1222 
1223 /*
1224  * returns the number of sectors that are ok.
1225  * This is never called. It would get called through ped_device_check().
1226  */
1227 static PedSector
1228 solaris_check(PedDevice* dev, void* buffer, PedSector start, PedSector count)
1229 {
1230         SolarisSpecific* arch_specific;
1231         PedSector done;
1232         int status;
1233         void* diobuf;
1234 
1235         PED_ASSERT(dev != NULL, return (0LL));
1236         PED_ASSERT(dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
1237             return (0LL));
1238         PED_ASSERT(dev->open_count > 0, return (0LL));
1239         PED_ASSERT(!dev->external_mode, return (0LL));
1240 
1241         printf("solaris_check: start %lld count %lld\n", start, count);
1242 
1243         arch_specific = SOLARIS_SPECIFIC(dev);
1244 
1245         if (!_device_seek(dev, start))
1246                 return (0LL);
1247 
1248         diobuf = memalign(PED_SECTOR_SIZE_DEFAULT, count * dev->sector_size);
1249         if (diobuf == NULL) {
1250                 printf("solaris_check: cannot memalign %u\n",
1251                     count * dev->sector_size);
1252                 return (0LL);
1253         }
1254 
1255         for (done = 0; done < count; done += status / dev->sector_size) {
1256                 status = read(arch_specific->fd, diobuf,
1257                     (size_t)((count - done) * dev->sector_size));
1258                 if (status < 0)
1259                         break;
1260         }
1261         free(diobuf);
1262 
1263         return (done);
1264 }
1265 
1266 static int
1267 solaris_sync(PedDevice* dev)
1268 {
1269         PED_ASSERT(dev != NULL, return (0));
1270         PED_ASSERT(!dev->external_mode, return (0));
1271 
1272         if (dev->read_only)
1273                 return (1);
1274         if (!_do_fsync(dev))
1275                 return (0);
1276         return (1);
1277 }
1278 
1279 /*
1280  * Returns all *p0 block devices.
1281  * open the raw device so ioctl works.
1282  */
1283 static void
1284 solaris_probe_all()
1285 {
1286         DIR *dir;
1287         struct dirent *dp;
1288         char *pname;
1289         char block_path[256];
1290         char raw_path[256];
1291         struct stat buffer;
1292         int fd;
1293 
1294         dir = opendir("/dev/dsk");
1295         while ((dp = readdir(dir)) != NULL) {
1296 
1297                 pname = dp->d_name + strlen(dp->d_name) - 2;
1298                 if (strcmp(pname, "p0") == 0) {
1299 
1300                         strncpy(block_path, "/dev/dsk/", sizeof (block_path));
1301                         strncat(block_path, dp->d_name, sizeof (block_path));
1302 
1303                         strncpy(raw_path, "/dev/rdsk/", sizeof (raw_path));
1304                         strncat(raw_path, dp->d_name, sizeof (raw_path));
1305 
1306                         if (stat(block_path, &buffer) == 0) {
1307 
1308                                 if ((fd = open(raw_path, O_RDONLY)) < 0) {
1309                                         continue;
1310                                 }
1311 
1312 #ifdef DONT_ALLOW_REMOVEABLE_DEVICES
1313                                 int n = 0;
1314                                 if (ioctl(fd, DKIOCREMOVABLE, &n) < 0) {
1315                                         char msg[MAXPATHLEN];
1316                                         snprintf(msg, sizeof (msg),
1317                                             "ioctl(\"%s\", DKIOCREMOVABLE)",
1318                                             raw_path);
1319                                         perror(msg);
1320                                 } else if (!n) {
1321                                         /*
1322                                          * Not a removable device
1323                                          * printf("solaris_probe_all: %s\n",
1324                                          * block_path);
1325                                          */
1326                                 }
1327 #endif /* DONT_ALLOW_REMOVEABLE_DEVICES */
1328 
1329                                 _ped_device_probe(block_path);
1330                                 close(fd);
1331                         }
1332                 }
1333         }
1334 }
1335 
1336 static char *
1337 solaris_partition_get_path(const PedPartition* part)
1338 {
1339         return (_device_get_part_path(part->disk->dev, part->num));
1340 }
1341 
1342 /*
1343  * Returns 1 if the partition is busy in some way, 0 otherwise.
1344  */
1345 static int
1346 solaris_partition_is_busy(const PedPartition* part)
1347 {
1348         int r1, r2, r3;
1349 
1350         PED_ASSERT(part != NULL, return (0));
1351 
1352         r1 = checkmount(part->geom.dev, part->geom.start, part->geom.end);
1353         r2 = checkswap(part->geom.dev, part->geom.start, part->geom.end);
1354         r3 = checkdevinuse(part->geom.dev, part->geom.start, part->geom.end, 1);
1355 
1356         if (r1 || r2 || r3)
1357                 return (1);
1358 
1359         return (0);
1360 }
1361 
1362 static int
1363 solaris_disk_commit(PedDisk* disk)
1364 {
1365         return (1);
1366 }
1367 
1368 static PedDeviceArchOps solaris_dev_ops = {
1369         ._new =          solaris_new,
1370         .destroy =       solaris_destroy,
1371         .is_busy =       solaris_is_busy,
1372         .open =          solaris_open,
1373         .refresh_open =  solaris_refresh_open,
1374         .close =         solaris_close,
1375         .refresh_close = solaris_refresh_close,
1376         .read =          solaris_read,
1377         .write =         solaris_write,
1378         .check =         solaris_check,
1379         .sync =          solaris_sync,
1380         .sync_fast =     solaris_sync,
1381         .probe_all =     solaris_probe_all
1382 };
1383 
1384 PedDiskArchOps solaris_disk_ops = {
1385         .partition_get_path =   solaris_partition_get_path,
1386         .partition_is_busy =    solaris_partition_is_busy,
1387         .disk_commit =          solaris_disk_commit
1388 };
1389 
1390 PedArchitecture ped_solaris_arch = {
1391         .dev_ops =      &solaris_dev_ops,
1392         .disk_ops =     &solaris_disk_ops
1393 };