/ src / core / partitions.cc
partitions.cc
   1  /*
   2   * partitions.cc
   3   *
   4   * This scan tries to guess partitioning information by looking at on-disk
   5   * structures.
   6   * Recognised partitioning schemes:
   7   * - MS-DOS (sometimes called MBR or static disks)
   8   * - Apple partition map
   9   * - EFI
  10   * - raw (whole disk)
  11   *
  12   */
  13  
  14  #define _LARGEFILE_SOURCE
  15  #define _FILE_OFFSET_BITS 64
  16  
  17  #include "version.h"
  18  #include "partitions.h"
  19  #include "blockio.h"
  20  #include "lvm.h"
  21  #include "volumes.h"
  22  #include "osutils.h"
  23  #include "options.h"
  24  #include <stdio.h>
  25  #include <stdlib.h>
  26  #include <cstring>
  27  #include <sys/types.h>
  28  #include <sys/stat.h>
  29  #include <fcntl.h>
  30  #include <string.h>
  31  #include <stdlib.h>
  32  #include <unistd.h>
  33  #include <stdint.h>
  34  #include <ctype.h>
  35  
  36  __ID("@(#) $Id$");
  37  
  38  #define LIFBLOCKSIZE 256
  39  
  40  struct maptypes
  41  {
  42    const char * id;
  43    const char * description;
  44    bool (*detect)(source & s, hwNode & n);
  45  };
  46  
  47  static bool detect_dosmap(source & s, hwNode & n);
  48  static bool detect_macmap(source & s, hwNode & n);
  49  static bool detect_lif(source & s, hwNode & n);
  50  static bool detect_luks(source & s, hwNode & n);
  51  static bool detect_gpt(source & s, hwNode & n);
  52  
  53  static struct maptypes map_types[] =
  54  {
  55    {"bsd", "BSD disklabel", NULL},
  56    {"gpt", "GUID partition table", detect_gpt},
  57    {"dos", "MS-DOS partition table", detect_dosmap},
  58    {"mac", "Apple Macintosh partition map", detect_macmap},
  59    {"lif", "HP-UX LIF", detect_lif},
  60    {"luks", "Linux Unified Key Setup", detect_luks},
  61    {"solaris-x86", "Solaris disklabel", NULL},
  62    {"solaris-sparc", "Solaris disklabel", NULL},
  63    {"raid", "Linux RAID", NULL},
  64    {"lvm", "Linux LVM Physical Volume", NULL},
  65    {"atari", "Atari ST", NULL},
  66    {"amiga", "Amiga", NULL},
  67    { NULL, NULL, NULL }
  68  };
  69  
  70  struct efi_guid_t
  71  {
  72    uint32_t time_low;
  73    uint16_t time_mid;
  74    uint16_t time_hi_and_version;
  75    uint8_t  clock_seq_hi_and_reserved;
  76    uint8_t  clock_seq_low;
  77    uint8_t  node[6];
  78  };
  79  
  80  struct gpth
  81  {
  82    uint64_t Signature;                             /* offset: 0 */
  83    uint32_t Revision;                              /* 8 */
  84    uint32_t HeaderSize;                            /* 12 */
  85    uint32_t HeaderCRC32;                           /* 16 */
  86    uint32_t Reserved1;                             /* 20 */
  87    uint64_t MyLBA;                                 /* 24 */
  88    uint64_t AlternateLBA;                          /* 32 */
  89    uint64_t FirstUsableLBA;                        /* 40 */
  90    uint64_t LastUsableLBA;                         /* 48 */
  91    efi_guid_t DiskGUID;                            /* 56 */
  92    uint64_t PartitionEntryLBA;                     /* 72 */
  93    uint32_t NumberOfPartitionEntries;              /* 80 */
  94    uint32_t SizeOfPartitionEntry;                  /* 84 */
  95    uint32_t PartitionEntryArrayCRC32;              /* 88 */
  96  };
  97  
  98  struct efipartition
  99  {
 100    efi_guid_t PartitionTypeGUID;
 101    efi_guid_t PartitionGUID;
 102    uint64_t StartingLBA;
 103    uint64_t EndingLBA;
 104    uint64_t Attributes;
 105    string PartitionName;
 106  };
 107  
 108  #define GPT_PMBR_LBA 0
 109  #define GPT_PMBR_SECTORS 1
 110  #define GPT_PRIMARY_HEADER_LBA 1
 111  #define GPT_HEADER_SECTORS 1
 112  #define GPT_PRIMARY_PART_TABLE_LBA 2
 113  
 114  #define EFI_PMBR_OSTYPE_EFI 0xee
 115  #define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* "EFI PART" */
 116  
 117  struct dospartition
 118  {
 119    unsigned long long start;
 120    unsigned long long size;
 121    unsigned char type;
 122    unsigned char flags;
 123  };
 124  
 125  /* DOS partition types (from util-linux's fdisk)*/
 126  struct systypes
 127  {
 128    const unsigned char type;
 129    const char * description;
 130    const char * id;
 131    const char * capabilities;
 132    const char * icon;
 133  };
 134  
 135  static struct systypes dos_sys_types[] =
 136  {
 137    {0x00, "Empty", "empty", "nofs", ""},
 138    {0x01, "FAT12", "fat12", "", ""},
 139    {0x02, "XENIX root", "xenix", "", ""},
 140    {0x03, "XENIX usr", "xenix", "", ""},
 141    {0x04, "FAT16 <32M", "fat16", "", ""},
 142    {                                               /* DOS 3.3+ extended partition */
 143      0x05, "Extended", "extended", "multi", ""
 144    },
 145    {                                               /* DOS 16-bit >=32M */
 146      0x06, "FAT16", "fat16", "", ""
 147    },
 148    {                                               /* OS/2 IFS, eg, HPFS or NTFS or QNX */
 149      0x07, "HPFS/NTFS", "ntfs", "", ""
 150    },
 151    {                                               /* AIX boot (AIX -- PS/2 port) or SplitDrive */
 152      0x08, "AIX", "", "", ""
 153    },
 154    {                                               /* AIX data or Coherent */
 155      0x09, "AIX bootable", "", "boot", ""
 156    },
 157    {                                               /* OS/2 Boot Manager */
 158      0x0a, "OS/2 Boot Manager", "", "boot,nofs", ""
 159    },
 160    {0x0b, "W95 FAT32", "fat32", "", ""},
 161    {                                               /* LBA really is `Extended Int 13h' */
 162      0x0c, "W95 FAT32 (LBA)", "fat32", "", ""
 163    },
 164    {0x0e, "W95 FAT16 (LBA)", "fat32", "", ""},
 165    {0x0f, "W95 Ext'd (LBA)", "fat32", "", ""},
 166    {0x10, "OPUS", "", "", ""},
 167    {0x11, "Hidden FAT12", "fat12", "hidden", ""},
 168    {0x12, "Compaq diagnostics", "", "boot", ""},
 169    {0x14, "Hidden FAT16 <32M", "", "hidden", ""},
 170    {0x16, "Hidden FAT16", "", "hidden", ""},
 171    {0x17, "Hidden HPFS/NTFS", "ntfs", "hidden", ""},
 172    {0x18, "AST SmartSleep", "", "nofs", ""},
 173    {0x1b, "Hidden W95 FAT32", "", "hidden", ""},
 174    {0x1c, "Hidden W95 FAT32 (LBA)", "", "hidden", ""},
 175    {0x1e, "Hidden W95 FAT16 (LBA)", "", "hidden", ""},
 176    {0x24, "NEC DOS", "", "", ""},
 177    {0x39, "Plan 9", "plan9", "", ""},
 178    {0x3c, "PartitionMagic recovery", "", "nofs", ""},
 179    {0x40, "Venix 80286", "", "", ""},
 180    {0x41, "PPC PReP Boot", "", "boot", ""},
 181    {0x42, "SFS", "", "", ""},
 182    {0x4d, "QNX4.x", "", "", ""},
 183    {0x4e, "QNX4.x 2nd part", "", "", ""},
 184    {0x4f, "QNX4.x 3rd part", "", "", ""},
 185    {0x50, "OnTrack DM", "", "", ""},
 186    {                                               /* (or Novell) */
 187      0x51, "OnTrack DM6 Aux1", "", "", ""
 188    },
 189    {                                               /* CP/M or Microport SysV/AT */
 190      0x52, "CP/M", "cpm", "", ""
 191    },
 192    {0x53, "OnTrack DM6 Aux3", "", "", ""},
 193    {0x54, "OnTrackDM6", "", "", ""},
 194    {0x55, "EZ-Drive", "", "", ""},
 195    {0x56, "Golden Bow", "", "", ""},
 196    {0x5c, "Priam Edisk", "", "", ""},
 197    {0x61, "SpeedStor", "", "", ""},
 198    {                                               /* GNU HURD or Mach or Sys V/386 (such as ISC UNIX) */
 199      0x63, "GNU HURD or SysV", "", "", ""
 200    },
 201    {0x64, "Novell Netware 286", "", "", ""},
 202    {0x65, "Novell Netware 386", "", "", ""},
 203    {0x70, "DiskSecure Multi-Boot", "", "boot", ""},
 204    {0x75, "PC/IX", "", "", ""},
 205    {                                               /* Minix 1.4a and earlier */
 206      0x80, "Old Minix", "minix", "", ""
 207    },
 208    {                                               /* Minix 1.4b and later */
 209      0x81, "Minix / old Linux", "minix", "", ""
 210    },
 211    {0x82, "Linux swap / Solaris", "swap", "nofs", ""},
 212    {0x83, "Linux filesystem", "linux", "", ""},
 213    {0x84, "OS/2 hidden C: drive", "", "hidden", ""},
 214    {0x85, "Linux extended", "", "multi", ""},
 215    {0x86, "NTFS volume set", "", "multi", "md"},
 216    {0x87, "NTFS volume set", "", "multi", "md"},
 217    {0x88, "Linux plaintext", "", "", ""},
 218    {0x8e, "Linux LVM Physical Volume", "lvm", "multi", "md"},
 219    {0x93, "Amoeba", "", "", ""},
 220    {                                               /* (bad block table) */
 221      0x94, "Amoeba BBT", "", "", ""
 222    },
 223    {                                               /* BSDI */
 224      0x9f, "BSD/OS", "bsdos", "", ""
 225    },
 226    {0xa0, "IBM Thinkpad hibernation", "", "nofs", ""},
 227    {                                               /* various BSD flavours */
 228      0xa5, "FreeBSD", "freebsd", "", ""
 229    },
 230    {0xa6, "OpenBSD", "openbsd", "", ""},
 231    {0xa7, "NeXTSTEP", "nextstep", "", ""},
 232    {0xa8, "Darwin/OS X UFS", "darwin", "", ""},
 233    {0xa9, "NetBSD", "netbsd", "", ""},
 234    {0xab, "Darwin/OS X boot", "", "boot,nofs", ""},
 235    {0xac, "Darwin/OS X RAID", "", "multi", "md"},
 236    {0xaf, "Darwin/OS X HFS+", "hfs", "", ""},
 237    {0xb7, "BSDI fs", "", "", ""},
 238    {0xb8, "BSDI swap", "", "nofs", ""},
 239    {0xbb, "Boot Wizard hidden", "", "boot,nofs", ""},
 240    {0xbe, "Solaris boot", "", "boot,nofs", ""},
 241    {0xbf, "Solaris", "", "", ""},
 242    {0xc1, "DRDOS/sec (FAT-12)", "", "", ""},
 243    {0xc4, "DRDOS/sec (FAT-16 < 32M)", "", "", ""},
 244    {0xc6, "DRDOS/sec (FAT-16)", "", "", ""},
 245    {0xc7, "Syrinx", "", "", ""},
 246    {0xda, "Non-FS data", "", "nofs", ""},
 247    {0xdb, "CP/M / CTOS / ...", "", "", ""},
 248  /* CP/M or Concurrent CP/M or
 249               Concurrent DOS or CTOS */
 250    {                                               /* Dell PowerEdge Server utilities */
 251      0xde, "Dell Utility", "", "", ""
 252    },
 253    {                                               /* BootIt EMBRM */
 254      0xdf, "BootIt", "", "boot,nofs", ""
 255    },
 256    {0xe1, "DOS access", "", "", ""},
 257  /* DOS access or SpeedStor 12-bit FAT
 258               extended partition */
 259    {                                               /* DOS R/O or SpeedStor */
 260      0xe3, "DOS R/O", "", "", ""
 261    },
 262    {0xe4, "SpeedStor", "", "", ""},
 263  /* SpeedStor 16-bit FAT extended
 264               partition < 1024 cyl. */
 265    {0xeb, "BeOS fs", "", "", ""},
 266    {                                               /* Intel EFI GUID Partition Table */
 267      0xee, "EFI GPT", "", "nofs", ""
 268    },
 269    {                                               /* Intel EFI System Partition */
 270      0xef, "EFI (FAT-12/16/32)", "", "boot", ""
 271    },
 272    {                                               /* Linux/PA-RISC boot loader */
 273      0xf0, "Linux/PA-RISC boot", "", "boot", ""
 274    },
 275    {0xf1, "SpeedStor", "", "", ""},
 276    {                                               /* SpeedStor large partition */
 277      0xf4, "SpeedStor", "", "", ""
 278    },
 279    {                                               /* DOS 3.3+ secondary */
 280      0xf2, "DOS secondary", "", "", ""
 281    },
 282    {0xfd, "Linux raid autodetect", "", "multi", "md"},
 283  /* New (2.2.x) raid partition with
 284                   autodetect using persistent
 285                   superblock */
 286    {                                               /* SpeedStor >1024 cyl. or LANstep */
 287      0xfe, "LANstep", "", "", ""
 288    },
 289    {                                               /* Xenix Bad Block Table */
 290      0xff, "BBT", "", "", ""
 291    },
 292    { 0, NULL, NULL, NULL }
 293  };
 294  
 295  static unsigned int lastlogicalpart = 5;
 296  
 297  static string partitionname(string disk, unsigned int n)
 298  {
 299    if(isdigit(disk[disk.length()-1]))
 300  	  return disk+"p"+tostring(n);
 301    else
 302  	  return disk+tostring(n);
 303  }
 304  
 305  static bool guess_logicalname(source & s, const hwNode & disk, unsigned int n, hwNode & partition)
 306  {
 307    struct stat buf;
 308    int dev = 0;
 309  
 310    if(disk.getBusInfo()!="")
 311      partition.setBusInfo(disk.getBusInfo()+string(",")+tostring(n));
 312  
 313    if(fstat(s.fd, &buf)!=0) return false;
 314    if(!S_ISBLK(buf.st_mode)) return false;
 315  
 316    if(s.diskname!="")
 317      dev = open_dev(buf.st_rdev + n, S_IFBLK, partitionname(s.diskname, n));
 318    else
 319      dev = open_dev(buf.st_rdev + n, S_IFBLK, partitionname(disk.getLogicalName(), n));
 320  
 321    if(dev>=0)
 322    {
 323      source spart = s;
 324      unsigned char buffer1[BLOCKSIZE], buffer2[BLOCKSIZE];
 325  
 326      spart.offset = 0;
 327      spart.fd = dev;
 328      spart.diskname = "";
 329  
 330      // read the first sector
 331      if((readlogicalblocks(s, buffer1, 0, 1)!=1) ||
 332        (readlogicalblocks(spart, buffer2, 0, 1)!=1))
 333      {
 334        close(dev);
 335        return false;
 336      }
 337  
 338      close(dev);
 339  
 340      if(memcmp(buffer1, buffer2, BLOCKSIZE)==0)
 341      {
 342        partition.claim();
 343        if(s.diskname!="")
 344          partition.setLogicalName(partitionname(s.diskname,n));
 345        else
 346          partition.setLogicalName(partitionname(disk.getLogicalName(),n));
 347        return true;
 348      }
 349    }
 350  
 351    return false;
 352  }
 353  
 354  
 355  static bool is_extended(unsigned char type)
 356  {
 357    return (type == 0x5) || (type == 0xf) || (type == 0x85);
 358  }
 359  
 360  
 361  static bool read_dospartition(source & s, unsigned short i, dospartition & p)
 362  {
 363    static unsigned char buffer[BLOCKSIZE];
 364    unsigned char flags = 0;
 365  
 366    if(readlogicalblocks(s, buffer, 0, 1)!=1)       // read the first sector
 367      return false;
 368  
 369    if(le_short(buffer+510)!=0xaa55)                // wrong magic number
 370      return false;
 371  
 372    flags = buffer[446 + i*16];
 373    if(flags!=0 && flags!=0x80)
 374  // inconsistency: partition is either
 375      return false;                                 // bootable or non-bootable
 376  
 377    p.flags = flags;
 378    p.type = buffer[446 + i*16 + 4];
 379    p.start = s.blocksize*(unsigned long long)le_long(buffer + 446 + i*16 + 8);
 380    p.size = s.blocksize*(unsigned long long)le_long(buffer + 446 + i*16 + 12);
 381  
 382    return true;
 383  }
 384  
 385  
 386  static bool analyse_dospart(source & s,
 387  unsigned char flags,
 388  unsigned char type,
 389  hwNode & partition);
 390  
 391  static bool analyse_dosextpart(source & s,
 392  unsigned char flags,
 393  unsigned char type,
 394  hwNode & extpart)
 395  {
 396    source extendedpart = s;
 397    int i = 0;
 398    dospartition pte[2];                            // we only read entries #0 and #1
 399  
 400    if(!is_extended(type))                          // this is not an extended partition
 401      return false;
 402  
 403    extpart.setDescription("Extended partition");
 404    extpart.addCapability("extended", "Extended partition");
 405    extpart.addCapability("partitioned", "Partitioned disk");
 406    extpart.addCapability("partitioned:extended", "Extended partition");
 407    extpart.setSize(extpart.getCapacity());
 408  
 409    extpart.describeCapability("nofs", "No filesystem");
 410  
 411    do
 412    {
 413      for(i=0; i<2; i++)
 414        if(!read_dospartition(extendedpart, i, pte[i]))
 415          return false;
 416  
 417      if((pte[0].type == 0) || (pte[0].size == 0))
 418        return true;
 419      else
 420      {
 421        hwNode partition("logicalvolume", hw::volume);
 422        source spart = extendedpart;
 423  
 424        spart.offset = extendedpart.offset + pte[0].start;
 425        spart.size = pte[0].size;
 426  
 427        partition.setPhysId(lastlogicalpart);
 428        partition.setCapacity(spart.size);
 429  
 430        if(analyse_dospart(spart, pte[0].flags, pte[0].type, partition))
 431        {
 432          guess_logicalname(spart, extpart, lastlogicalpart, partition);
 433  	scan_volume(partition, spart);
 434          extpart.addChild(partition);
 435          lastlogicalpart++;
 436        }
 437      }
 438  
 439      if((pte[1].type == 0) || (pte[1].size == 0))
 440        return true;
 441      else
 442      {
 443        extendedpart.offset = s.offset + pte[1].start;
 444        extendedpart.size = pte[1].size;
 445        if(!is_extended(pte[1].type))
 446          return false;
 447      }
 448    } while(true);
 449  
 450    return true;
 451  }
 452  
 453  
 454  static bool analyse_dospart(source & s,
 455  unsigned char flags,
 456  unsigned char type,
 457  hwNode & partition)
 458  {
 459    int i = 0;
 460  
 461    if(is_extended(type))
 462      return analyse_dosextpart(s, flags, type, partition);
 463  
 464    if(flags!=0 && flags!=0x80)                     // inconsistency: partition is either bootable or non-bootable
 465      return false;
 466  
 467    if(s.offset==0 || s.size==0)                    // unused entry
 468      return false;
 469  
 470    partition.setCapacity(s.size);
 471  
 472    if(flags == 0x80)
 473      partition.addCapability("bootable", "Bootable partition (active)");
 474  
 475    while(dos_sys_types[i].id)
 476    {
 477      if(dos_sys_types[i].type == type)
 478      {
 479        partition.setDescription(string(dos_sys_types[i].description)+" partition");
 480        if(lowercase(dos_sys_types[i].capabilities) != "")
 481        {
 482          vector<string> capabilities;
 483          splitlines(dos_sys_types[i].capabilities, capabilities, ',');
 484  
 485          for(unsigned j=0; j<capabilities.size(); j++)
 486            partition.addCapability(capabilities[j]);
 487        }
 488        if(lowercase(dos_sys_types[i].icon) != "")
 489          partition.addHint("icon", string(dos_sys_types[i].icon));
 490        else
 491          partition.addHint("icon", string("disc"));
 492        break;
 493      }
 494      i++;
 495    }
 496  
 497    partition.describeCapability("nofs", "No filesystem");
 498    partition.describeCapability("boot", "Contains boot code");
 499    partition.describeCapability("multi", "Multi-volumes");
 500    partition.describeCapability("hidden", "Hidden partition");
 501  
 502    scan_lvm(partition, s);
 503  
 504    return true;
 505  }
 506  
 507  
 508  #define UNUSED_ENTRY_GUID \
 509  ((efi_guid_t) \
 510  { \
 511    0x00000000, 0x0000, 0x0000, 0x00, 00, \
 512    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } \
 513  })
 514  
 515  #define PARTITION_PRECIOUS 1
 516  #define PARTITION_READONLY (1LL << 60)
 517  #define PARTITION_HIDDEN   (1LL << 62)
 518  #define PARTITION_NOMOUNT  (1LL << 63)
 519  
 520  /* returns the EFI-style CRC32 value for buf
 521   * Dec 5, 2000 Matt Domsch <Matt_Domsch@dell.com>
 522   * - Copied crc32.c from the linux/drivers/net/cipe directory.
 523   * - Now pass seed as an arg
 524   * - changed unsigned long to uint32_t, added #include<stdint.h>
 525   * - changed len to be an unsigned long
 526   * - License remains unchanged!  It's still GPL-compatable!
 527   */
 528  
 529  /* ============================================================= */
 530  /*  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or       */
 531  /*  code or tables extracted from it, as desired without restriction.     */
 532  /*                                                                        */
 533  /*  First, the polynomial itself and its table of feedback terms.  The    */
 534  /*  polynomial is                                                         */
 535  /*  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0   */
 536  /*                                                                        */
 537  /*  Note that we take it "backwards" and put the highest-order term in    */
 538  /*  the lowest-order bit.  The X^32 term is "implied"; the LSB is the     */
 539  /*  X^31 term, etc.  The X^0 term (usually shown as "+1") results in      */
 540  /*  the MSB being 1.                                                      */
 541  /*                                                                        */
 542  /*  Note that the usual hardware shift register implementation, which     */
 543  /*  is what we're using (we're merely optimizing it by doing eight-bit    */
 544  /*  chunks at a time) shifts bits into the lowest-order term.  In our     */
 545  /*  implementation, that means shifting towards the right.  Why do we     */
 546  /*  do it this way?  Because the calculated CRC must be transmitted in    */
 547  /*  order from highest-order term to lowest-order term.  UARTs transmit   */
 548  /*  characters in order from LSB to MSB.  By storing the CRC this way,    */
 549  /*  we hand it to the UART in the order low-byte to high-byte; the UART   */
 550  /*  sends each low-bit to hight-bit; and the result is transmission bit   */
 551  /*  by bit from highest- to lowest-order term without requiring any bit   */
 552  /*  shuffling on our part.  Reception works similarly.                    */
 553  /*                                                                        */
 554  /*  The feedback terms table consists of 256, 32-bit entries.  Notes:     */
 555  /*                                                                        */
 556  /*      The table can be generated at runtime if desired; code to do so   */
 557  /*      is shown later.  It might not be obvious, but the feedback        */
 558  /*      terms simply represent the results of eight shift/xor opera-      */
 559  /*      tions for all combinations of data and CRC register values.       */
 560  /*                                                                        */
 561  /*      The values must be right-shifted by eight bits by the "updcrc"    */
 562  /*      logic; the shift must be unsigned (bring in zeroes).  On some     */
 563  /*      hardware you could probably optimize the shift in assembler by    */
 564  /*      using byte-swap instructions.                                     */
 565  /*      polynomial $edb88320                                              */
 566  /*                                                                        */
 567  /*  --------------------------------------------------------------------  */
 568  
 569  static uint32_t crc32_tab[] =
 570  {
 571    0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
 572    0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
 573    0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
 574    0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
 575    0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
 576    0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
 577    0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
 578    0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
 579    0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
 580    0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
 581    0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
 582    0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
 583    0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
 584    0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
 585    0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
 586    0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
 587    0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
 588    0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
 589    0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
 590    0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
 591    0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
 592    0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
 593    0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
 594    0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
 595    0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
 596    0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
 597    0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
 598    0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
 599    0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
 600    0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
 601    0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
 602    0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
 603    0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
 604    0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
 605    0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
 606    0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
 607    0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
 608    0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
 609    0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
 610    0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
 611    0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
 612    0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
 613    0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
 614    0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
 615    0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
 616    0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
 617    0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
 618    0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
 619    0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
 620    0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
 621    0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
 622    0x2d02ef8dL
 623  };
 624  
 625  /* Return a 32-bit CRC of the contents of the buffer. */
 626  
 627  uint32_t
 628  __efi_crc32(const void *buf, unsigned long len, uint32_t seed)
 629  {
 630    unsigned long i;
 631    uint32_t crc32val;
 632    const unsigned char *s = (const unsigned char *)buf;
 633  
 634    crc32val = seed;
 635    for (i = 0;  i < len;  i ++)
 636    {
 637      crc32val =
 638        crc32_tab[(crc32val ^ s[i]) & 0xff] ^
 639        (crc32val >> 8);
 640    }
 641    return crc32val;
 642  }
 643  
 644  
 645  /*
 646   *      This function uses the crc32 function by Gary S. Brown,
 647   * but seeds the function with ~0, and xor's with ~0 at the end.
 648   */
 649  static inline uint32_t
 650  efi_crc32(const void *buf, unsigned long len)
 651  {
 652    return (__efi_crc32(buf, len, ~0L) ^ ~0L);
 653  }
 654  
 655  
 656  static efi_guid_t read_efi_guid(uint8_t *buffer)
 657  {
 658    efi_guid_t result;
 659  
 660    memset(&result, 0, sizeof(result));
 661  
 662    result.time_low = le_long(buffer);
 663    result.time_mid = le_short(buffer+4);
 664    result.time_hi_and_version = le_short(buffer+4+2);
 665    result.clock_seq_hi_and_reserved = *(buffer+4+2+2);
 666    result.clock_seq_low = *(buffer+4+2+2+1);
 667    memcpy(result.node, buffer+4+2+2+1+1, sizeof(result.node));
 668  
 669    return result;
 670  }
 671  
 672  
 673  bool operator ==(const efi_guid_t & guid1, const efi_guid_t & guid2)
 674  {
 675    return (guid1.time_low == guid2.time_low) &&
 676      (guid1.time_mid == guid2.time_mid) &&
 677      (guid1.time_hi_and_version == guid2.time_hi_and_version) &&
 678      (guid1.clock_seq_hi_and_reserved == guid2.clock_seq_hi_and_reserved) &&
 679      (guid1.clock_seq_low == guid2.clock_seq_low) &&
 680      (memcmp(guid1.node, guid2.node, sizeof(guid1.node))==0);
 681  }
 682  
 683  
 684  static string tostring(const efi_guid_t & guid)
 685  {
 686    char buffer[50];
 687  
 688    snprintf(buffer, sizeof(buffer), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", guid.time_low, guid.time_mid,guid.time_hi_and_version,guid.clock_seq_hi_and_reserved,guid.clock_seq_low,guid.node[0],guid.node[1],guid.node[2],guid.node[3],guid.node[4],guid.node[5]);
 689    return string(buffer);
 690  }
 691  
 692  bool operator ==(const efi_guid_t & guid1, const string & guid2)
 693  {
 694    return strcasecmp(tostring(guid1).c_str(), guid2.c_str()) == 0;
 695  }
 696  
 697  static bool detect_gpt(source & s, hwNode & n)
 698  {
 699    static uint8_t buffer[BLOCKSIZE];
 700    static gpth gpt_header;
 701    uint32_t i = 0;
 702    char gpt_version[13];
 703    uint8_t *partitions = NULL;
 704    uint8_t type;
 705  
 706    if(s.offset!=0)
 707      return false;                                 // partition tables must be at the beginning of the disk
 708  
 709                                                    // read the first sector
 710    if(readlogicalblocks(s, buffer, GPT_PMBR_LBA, 1)!=1)
 711      return false;
 712  
 713    if(le_short(buffer+510)!=0xaa55)                // wrong magic number
 714      return false;
 715  
 716    for(i=0; i<4; i++)
 717    {
 718      type = buffer[446 + i*16 + 4];
 719  
 720      if((type != 0) && (type != EFI_PMBR_OSTYPE_EFI))
 721        return false;                               // the EFI pseudo-partition must be the only partition
 722    }
 723  
 724                                                    // read the second sector
 725    if(readlogicalblocks(s, buffer, GPT_PRIMARY_HEADER_LBA, 1)!=1)
 726      return false;                                 // (partition table header)
 727  
 728    gpt_header.Signature = le_longlong(buffer);
 729    gpt_header.Revision = be_long(buffer + 0x8);    // big endian so that 1.0 -> 0x100
 730    gpt_header.HeaderSize = le_long(buffer + 0xc);
 731    gpt_header.HeaderCRC32 = le_long(buffer + 0x10);
 732    gpt_header.MyLBA = le_longlong(buffer + 0x18);
 733    gpt_header.AlternateLBA = le_longlong(buffer + 0x20);
 734    gpt_header.FirstUsableLBA = le_longlong(buffer + 0x28);
 735    gpt_header.LastUsableLBA = le_longlong(buffer + 0x30);
 736    gpt_header.DiskGUID = read_efi_guid(buffer + 0x38);
 737    gpt_header.PartitionEntryLBA = le_longlong(buffer + 0x48);
 738    gpt_header.NumberOfPartitionEntries = le_long(buffer + 0x50);
 739    gpt_header.SizeOfPartitionEntry = le_long(buffer + 0x54);
 740    gpt_header.PartitionEntryArrayCRC32 = le_long(buffer + 0x58);
 741  
 742                                                    // zero-out the CRC32 before re-calculating it
 743    memset(buffer + 0x10, 0, sizeof(gpt_header.HeaderCRC32));
 744    if(gpt_header.Signature != GPT_HEADER_SIGNATURE)
 745      return false;
 746  
 747    if(efi_crc32(buffer, 92) != gpt_header.HeaderCRC32)
 748      return false;                                 // check CRC32
 749  
 750    snprintf(gpt_version, sizeof(gpt_version), "%d.%02d", (gpt_header.Revision >> 8), (gpt_header.Revision & 0xff));
 751  
 752    n.addCapability("gpt-"+string(gpt_version), "GUID Partition Table version "+string(gpt_version));
 753    n.addHint("partitions", gpt_header.NumberOfPartitionEntries);
 754    n.setConfig("guid",::enabled("output:sanitize")?REMOVED:tostring(gpt_header.DiskGUID));
 755    n.setHandle(::enabled("output:sanitize")?"GUID:":"GUID:" + tostring(gpt_header.DiskGUID));
 756    n.addHint("guid",::enabled("output:sanitize")?REMOVED:tostring(gpt_header.DiskGUID));
 757  
 758    partitions = (uint8_t*)malloc(gpt_header.NumberOfPartitionEntries * gpt_header.SizeOfPartitionEntry + BLOCKSIZE);
 759    if(!partitions)
 760      return false;
 761    memset(partitions, 0, gpt_header.NumberOfPartitionEntries * gpt_header.SizeOfPartitionEntry + BLOCKSIZE);
 762    readlogicalblocks(s, partitions,
 763      gpt_header.PartitionEntryLBA,
 764      (gpt_header.NumberOfPartitionEntries * gpt_header.SizeOfPartitionEntry)/BLOCKSIZE + 1);
 765  
 766    for(i=0; i<gpt_header.NumberOfPartitionEntries; i++)
 767    {
 768      hwNode partition("volume", hw::volume);
 769      efipartition p;
 770  
 771      p.PartitionTypeGUID = read_efi_guid(partitions + gpt_header.SizeOfPartitionEntry * i);
 772      p.PartitionGUID = read_efi_guid(partitions + gpt_header.SizeOfPartitionEntry * i + 0x10);
 773      p.StartingLBA = le_longlong(partitions + gpt_header.SizeOfPartitionEntry * i + 0x20);
 774      p.EndingLBA = le_longlong(partitions + gpt_header.SizeOfPartitionEntry * i + 0x28);
 775      p.Attributes = le_longlong(partitions + gpt_header.SizeOfPartitionEntry * i + 0x30);
 776      for(int j=0; j<36; j++)
 777      {
 778        wchar_t c = le_short(partitions + gpt_header.SizeOfPartitionEntry * i + 0x38 + 2*j);
 779        if(!c)
 780          break;
 781        else
 782          p.PartitionName += utf8(c);
 783      }
 784  
 785      if(!(p.PartitionTypeGUID == UNUSED_ENTRY_GUID))
 786      {
 787        source spart = s;
 788        if(p.PartitionTypeGUID == "C12A7328-F81F-11D2-BA4B-00A0C93EC93B")
 789        {
 790          partition.setDescription("System partition");
 791          partition.setVendor("EFI");
 792          partition.addCapability("boot");
 793        }
 794        else
 795        if(p.PartitionTypeGUID == "024DEE41-33E7-11D3-9D69-0008C781F39F")
 796        {
 797          partition.setDescription("MBR partition scheme");
 798          partition.setVendor("EFI");
 799          partition.addCapability("nofs");
 800        }
 801        else
 802        if(p.PartitionTypeGUID == "21686148-6449-6E6F-744E-656564454649")
 803        {
 804          partition.setDescription("BIOS Boot partition");
 805          partition.setVendor("EFI");
 806          partition.addCapability("nofs");
 807        }
 808        else
 809        if(p.PartitionTypeGUID == "0657FD6D-A4AB-43C4-84E5-0933C84B4F4F")
 810        {
 811          partition.setDescription("swap partition");
 812          partition.setVendor("Linux");
 813          partition.addCapability("nofs");
 814        }
 815        else
 816        if(p.PartitionTypeGUID == "A19D880F-05FC-4D3B-A006-743F0F84911E")
 817        {
 818          partition.setDescription("RAID partition");
 819          partition.setVendor("Linux");
 820          partition.addCapability("multi");
 821        }
 822        else
 823        if(p.PartitionTypeGUID == "E6D6D379-F507-44C2-A23C-238F2A3DF928")
 824        {
 825          partition.setDescription("LVM Physical Volume");
 826          partition.setVendor("Linux");
 827          partition.addCapability("multi");
 828        }
 829        else
 830        if(p.PartitionTypeGUID == "8DA63339-0007-60C0-C436-083AC8230908")
 831        {
 832          partition.setDescription("reserved partition");
 833          partition.setVendor("Linux");
 834          partition.addCapability("nofs");
 835        }
 836        else
 837        if(p.PartitionTypeGUID == "75894C1E-3AEB-11D3-B7C1-7B03A0000000")
 838        {
 839          partition.setDescription("data partition");
 840          partition.setVendor("HP-UX");
 841        }
 842        else
 843        if(p.PartitionTypeGUID == "E2A1E728-32E3-11D6-A682-7B03A0000000")
 844        {
 845          partition.setDescription("service partition");
 846          partition.setVendor("HP-UX");
 847          partition.addCapability("nofs");
 848          partition.addCapability("boot");
 849        }
 850        else
 851        if(p.PartitionTypeGUID == "48465300-0000-11AA-AA11-00306543ECAC")
 852        {
 853          partition.setDescription("Apple HFS+ partition");
 854          partition.setVendor("Mac OS X");
 855        }
 856        else
 857        if(p.PartitionTypeGUID == "7C3457EF-0000-11AA-AA11-00306543ECAC")
 858        {
 859          partition.setDescription("Apple APFS partition");
 860          partition.setVendor("Mac OS X");
 861        }
 862        else
 863        if(p.PartitionTypeGUID == "6A898CC3-1DD2-11B2-99A6-080020736631")
 864        {
 865          partition.setDescription("OS X ZFS partition or Solaris /usr partition");
 866          partition.setVendor("Solaris");
 867        }
 868        else
 869        if(p.PartitionTypeGUID == "52414944-0000-11AA-AA11-00306543ECAC")
 870        {
 871          partition.setDescription("RAID partition");
 872          partition.setVendor("Mac OS X");
 873          partition.addCapability("multi");
 874        }
 875        else
 876        if(p.PartitionTypeGUID == "52414944-5F4F-11AA-AA11-00306543ECAC")
 877        {
 878          partition.setDescription("RAID partition (offline)");
 879          partition.setVendor("Mac OS X");
 880          partition.addCapability("multi");
 881          partition.addCapability("offline");
 882        }
 883        else
 884        if(p.PartitionTypeGUID == "4C616265-6C00-11AA-AA11-00306543ECAC")
 885        {
 886          partition.setDescription("Apple label");
 887          partition.setVendor("Mac OS X");
 888          partition.addCapability("nofs");
 889        }
 890        else
 891        if(p.PartitionTypeGUID == "5265636F-7665-11AA-AA11-00306543ECAC")
 892        {
 893          partition.setDescription("recovery partition");
 894          partition.setVendor("Apple TV");
 895          partition.addCapability("nofs");
 896        }
 897        else
 898        if(p.PartitionTypeGUID == "53746F72-6167-11AA-AA11-00306543ECAC")
 899        {
 900          partition.setDescription("Apple Core Storage (File Vault)");
 901          partition.setVendor("Mac OS X");
 902          partition.addCapability("encrypted");
 903        }
 904        else
 905        if(p.PartitionTypeGUID == "426F6F74-0000-11AA-AA11-00306543ECAC")
 906        {
 907          partition.setDescription("boot partition");
 908          partition.setVendor("Mac OS X");
 909          partition.addCapability("boot");
 910        }
 911        else
 912        if(p.PartitionTypeGUID == "55465300-0000-11AA-AA11-00306543ECAC")
 913        {
 914          partition.setDescription("UFS partition");
 915          partition.setVendor("Mac OS X");
 916        }
 917        else
 918        if(p.PartitionTypeGUID == "516E7CB4-6ECF-11D6-8FF8-00022D09712B")
 919        {
 920          partition.setDescription("data partition");
 921          partition.setVendor("FreeBSD");
 922        }
 923        else
 924        if(p.PartitionTypeGUID == "516E7CB6-6ECF-11D6-8FF8-00022D09712B")
 925        {
 926          partition.setDescription("UFS partition");
 927          partition.setVendor("FreeBSD");
 928        }
 929        else
 930        if(p.PartitionTypeGUID == "516E7CBA-6ECF-11D6-8FF8-00022D09712B")
 931        {
 932          partition.setDescription("ZFS partition");
 933          partition.setVendor("FreeBSD");
 934        }
 935        else
 936        if(p.PartitionTypeGUID == "516E7CB8-6ECF-11D6-8FF8-00022D09712B")
 937        {
 938          partition.setDescription("Vinum Volume Manager partition");
 939          partition.setVendor("FreeBSD");
 940        }
 941        else
 942        if(p.PartitionTypeGUID == "516E7CB5-6ECF-11D6-8FF8-00022D09712B")
 943        {
 944          partition.setDescription("swap partition");
 945          partition.setVendor("FreeBSD");
 946          partition.addCapability("nofs");
 947        }
 948        else
 949        if(p.PartitionTypeGUID == "83BD6B9D-7F41-11DC-BE0B-001560B84F0F")
 950        {
 951          partition.setDescription("boot partition");
 952          partition.setVendor("FreeBSD");
 953          partition.addCapability("boot");
 954        }
 955        else
 956        if(p.PartitionTypeGUID == "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7")
 957        {
 958          partition.setDescription("data partition");
 959          partition.setVendor("Windows");
 960        }
 961        else
 962        if(p.PartitionTypeGUID == "DE94BBA4-06D1-4D40-A16A-BFD50179D6AC")
 963        {
 964          partition.setDescription("recovery environment");
 965          partition.setVendor("Windows");
 966          partition.addCapability("boot");
 967        }
 968        else
 969        if(p.PartitionTypeGUID == "37AFFC90-EF7D-4E96-91C3-2D7AE055B174")
 970        {
 971          partition.setDescription("IBM GPFS partition");
 972          partition.setVendor("Windows");
 973        }
 974        else
 975        if(p.PartitionTypeGUID == "5808C8AA-7E8F-42E0-85D2-E1E90434CFB3")
 976        {
 977          partition.setDescription("LDM configuration");
 978          partition.setVendor("Windows");
 979          partition.addCapability("nofs");
 980        }
 981        else
 982        if(p.PartitionTypeGUID == "AF9B60A0-1431-4F62-BC68-3311714A69AD")
 983        {
 984          partition.setDescription("LDM data partition");
 985          partition.setVendor("Windows");
 986          partition.addCapability("multi");
 987        }
 988        else
 989        if(p.PartitionTypeGUID == "E3C9E316-0B5C-4DB8-817D-F92DF00215AE")
 990        {
 991          partition.setDescription("reserved partition");
 992          partition.setVendor("Windows");
 993          partition.addCapability("nofs");
 994        }
 995        else
 996        if(p.PartitionTypeGUID == "FE3A2A5D-4F32-41A7-B725-ACCC3285A309")
 997        {
 998          partition.setDescription("kernel");
 999          partition.setVendor("ChromeOS");
1000        }
1001        else
1002        if(p.PartitionTypeGUID == "3CB8E202-3B7E-47DD-8A3C-7FF2A13CFCEC")
1003        {
1004          partition.setDescription("root filesystem");
1005          partition.setVendor("ChromeOS");
1006        }
1007        else
1008        if(p.PartitionTypeGUID == "2E0A753D-9E48-43B0-8337-B15192CB1B5E")
1009        {
1010          partition.setDescription("reserved");
1011          partition.setVendor("ChromeOS");
1012        }
1013        else
1014        if(p.PartitionTypeGUID == "6A82CB45-1DD2-11B2-99A6-080020736631")
1015        {
1016          partition.setDescription("boot partition");
1017          partition.setVendor("Solaris");
1018          partition.addCapability("boot");
1019        }
1020        else
1021        if(p.PartitionTypeGUID == "6A85CF4D-1DD2-11B2-99A6-080020736631")
1022        {
1023          partition.setDescription("root partition");
1024          partition.setVendor("Solaris");
1025        }
1026        else
1027        if(p.PartitionTypeGUID == "6A87C46F-1DD2-11B2-99A6-080020736631")
1028        {
1029          partition.setDescription("swap partition");
1030          partition.setVendor("Solaris");
1031          partition.addCapability("nofs");
1032        }
1033        else
1034        if(p.PartitionTypeGUID == "6A8B642B-1DD2-11B2-99A6-080020736631")
1035        {
1036          partition.setDescription("backup partition");
1037          partition.setVendor("Solaris");
1038        }
1039        else
1040        if(p.PartitionTypeGUID == "6A8EF2E9-1DD2-11B2-99A6-080020736631")
1041        {
1042          partition.setDescription("/var partition");
1043          partition.setVendor("Solaris");
1044        }
1045        else
1046        if(p.PartitionTypeGUID == "6A90BA39-1DD2-11B2-99A6-080020736631")
1047        {
1048          partition.setDescription("/home partition");
1049          partition.setVendor("Solaris");
1050        }
1051        else
1052        if(p.PartitionTypeGUID == "6A9283A5-1DD2-11B2-99A6-080020736631")
1053        {
1054          partition.setDescription("alternate sector");
1055          partition.setVendor("Solaris");
1056          partition.addCapability("nofs");
1057        }
1058        else
1059        if(p.PartitionTypeGUID == "6A945A3B-1DD2-11B2-99A6-080020736631" ||
1060           p.PartitionTypeGUID == "6A9630D1-1DD2-11B2-99A6-080020736631" ||
1061           p.PartitionTypeGUID == "6A980767-1DD2-11B2-99A6-080020736631" ||
1062           p.PartitionTypeGUID == "6A96237F-1DD2-11B2-99A6-080020736631" ||
1063           p.PartitionTypeGUID == "6A8D2AC7-1DD2-11B2-99A6-080020736631"
1064          )
1065        {
1066          partition.setDescription("reserved partition");
1067          partition.setVendor("Solaris");
1068        }
1069        else
1070        if(p.PartitionTypeGUID == "49F48D32-B10E-11DC-B99B-0019D1879648")
1071        {
1072          partition.setDescription("swap partition");
1073          partition.setVendor("NetBSD");
1074          partition.addCapability("nofs");
1075        }
1076        else
1077        if(p.PartitionTypeGUID == "49F48D5A-B10E-11DC-B99B-0019D1879648")
1078        {
1079          partition.setDescription("FFS partition");
1080          partition.setVendor("NetBSD");
1081        }
1082        else
1083        if(p.PartitionTypeGUID == "49F48D82-B10E-11DC-B99B-0019D1879648")
1084        {
1085          partition.setDescription("LFS partition");
1086          partition.setVendor("NetBSD");
1087        }
1088        else
1089        if(p.PartitionTypeGUID == "49F48DAA-B10E-11DC-B99B-0019D1879648")
1090        {
1091          partition.setDescription("RAID partition");
1092          partition.setVendor("NetBSD");
1093          partition.addCapability("multi");
1094        }
1095        else
1096        if(p.PartitionTypeGUID == "2DB519C4-B10F-11DC-B99B-0019D1879648")
1097        {
1098          partition.setDescription("concatenated partition");
1099          partition.setVendor("NetBSD");
1100          partition.addCapability("multi");
1101        }
1102        else
1103        if(p.PartitionTypeGUID == "2DB519EC-B10F-11DC-B99B-0019D1879648")
1104        {
1105          partition.setDescription("encrypted partition");
1106          partition.setVendor("NetBSD");
1107          partition.addCapability("encrypted");
1108        }
1109        else
1110        if(p.PartitionTypeGUID == "42465331-3ba3-10f1-802a-4861696b7521")		// is it really used ?
1111        {
1112          partition.setDescription("BeFS partition");
1113          partition.setVendor("Haiku");
1114        }
1115        else
1116          partition.setDescription("EFI partition");
1117        partition.setPhysId(i+1);
1118        partition.setCapacity(BLOCKSIZE * (p.EndingLBA - p.StartingLBA));
1119        partition.addHint("type", tostring(p.PartitionTypeGUID));
1120        partition.addHint("guid", tostring(p.PartitionGUID));
1121        partition.setSerial(tostring(p.PartitionGUID));
1122        partition.setHandle("GUID:" + tostring(p.PartitionGUID));
1123        partition.setConfig("name", p.PartitionName);
1124        if(p.Attributes & PARTITION_PRECIOUS)
1125          partition.addCapability("precious", "This partition is required for the platform to function");
1126        if(p.Attributes & PARTITION_READONLY)
1127          partition.addCapability("readonly", "Read-only partition");
1128        if(p.Attributes & PARTITION_HIDDEN)
1129          partition.addCapability("hidden");
1130        if(p.Attributes & PARTITION_NOMOUNT)
1131          partition.addCapability("nomount", "No automatic mount");
1132  
1133        partition.describeCapability("nofs", "No filesystem");
1134        partition.describeCapability("boot", "Contains boot code");
1135        partition.describeCapability("multi", "Multi-volumes");
1136        partition.describeCapability("hidden", "Hidden partition");
1137        partition.describeCapability("encrypted", "Contains encrypted data");
1138  
1139        spart.blocksize = s.blocksize;
1140        spart.offset = s.offset + p.StartingLBA*spart.blocksize;
1141        spart.size = (p.EndingLBA - p.StartingLBA)*spart.blocksize;
1142        guess_logicalname(spart, n, i+1, partition);
1143        scan_volume(partition, spart);
1144        n.addChild(partition);
1145      }
1146    }
1147  
1148    free(partitions);
1149  
1150    return true;
1151  }
1152  
1153  
1154  static bool detect_dosmap(source & s, hwNode & n)
1155  {
1156    static unsigned char buffer[BLOCKSIZE];
1157    int i = 0;
1158    unsigned char flags;
1159    unsigned char type;
1160    unsigned long long start, size;
1161    bool partitioned = false;
1162    unsigned long signature = 0;
1163  
1164    if(s.offset!=0)
1165      return false;                                 // partition tables must be at the beginning of the disk
1166  
1167    if(readlogicalblocks(s, buffer, 0, 1)!=1)       // read the first sector
1168      return false;
1169  
1170    if(le_short(buffer+510)!=0xaa55)                // wrong magic number
1171      return false;
1172  
1173    signature=le_long(buffer+440);
1174    if(signature == 0xffffffffL)
1175      signature = 0;
1176    if(signature)
1177    {
1178      char buffer[16+1];
1179      snprintf(buffer, sizeof(buffer), "%08lx", signature);
1180      n.setConfig("signature", buffer);
1181    }
1182  
1183    lastlogicalpart = 5;
1184  
1185    for(i=0; i<4; i++)
1186    {
1187      source spart = s;
1188      hwNode partition("volume", hw::volume);
1189  
1190      flags = buffer[446 + i*16];
1191      type = buffer[446 + i*16 + 4];
1192      start = le_long(buffer + 446 + i*16 + 8);
1193      size = le_long(buffer + 446 + i*16 + 12);
1194  
1195      if(flags!=0 && flags!=0x80)                   // inconsistency: partition is either bootable or non-bootable
1196        return false;
1197  
1198      spart.blocksize = s.blocksize;
1199      spart.offset = s.offset + start*spart.blocksize;
1200      spart.size = size*spart.blocksize;
1201  
1202      partition.setDescription("Primary partition");
1203      partition.addCapability("primary", "Primary partition");
1204      partition.setPhysId(i+1);
1205      partition.setCapacity(spart.size);
1206  
1207      if(analyse_dospart(spart, flags, type, partition))
1208      {
1209        guess_logicalname(spart, n, i+1, partition);
1210        scan_volume(partition, spart);
1211        n.addChild(partition);
1212        partitioned = true;
1213      }
1214    }
1215  
1216    return partitioned;
1217  }
1218  
1219  
1220  static bool detect_macmap(source & s, hwNode & n)
1221  {
1222    static unsigned char buffer[BLOCKSIZE];
1223    unsigned long count = 0, i = 0;
1224    unsigned long long start = 0, size = 0;
1225    string type = "";
1226  
1227    if(s.offset!=0)
1228      return false;                                 // partition maps must be at the beginning of the disk
1229  
1230    if(readlogicalblocks(s, buffer, 1, 1)!=1)       // read the second sector
1231      return false;
1232  
1233    if(be_short(buffer)!=0x504d)                    // wrong magic number
1234      return false;
1235  
1236    count = be_long(buffer+4);
1237  
1238    for (i = 1; i <= count; i++)
1239    {
1240      hwNode partition("volume", hw::volume);
1241  
1242      if((i>1) && readlogicalblocks(s, buffer, i, 1)!=1)
1243        return false;
1244  
1245      if(be_short(buffer)!=0x504d) continue;        // invalid map entry
1246  
1247      start = be_long(buffer + 8);
1248      size = be_long(buffer + 12);
1249      type = hw::strip(string((char*)buffer +  48, 32));
1250  
1251      partition.setPhysId(i);
1252      partition.setCapacity(size * s.blocksize);
1253      if(lowercase(type) == "apple_bootstrap")
1254        partition.addCapability("bootable", "Bootstrap partition");
1255  
1256      if(lowercase(type) == "linux_lvm")
1257      {
1258        partition.addHint("icon", string("md"));
1259        partition.addCapability("multi");
1260      }
1261      else
1262        partition.addHint("icon", string("disc"));
1263  
1264      for(unsigned int j=0; j<type.length(); j++)
1265        if(type[j] == '_') type[j] = ' ';
1266      partition.setDescription(type);
1267  
1268      if(true /*analyse_macpart(flags, type, start, size, partition)*/)
1269      {
1270        source spart = s;
1271  
1272        spart.blocksize = s.blocksize;
1273        spart.offset = s.offset + start*spart.blocksize;
1274        spart.size = size*spart.blocksize;
1275  
1276        guess_logicalname(spart, n, i, partition);
1277  
1278        scan_volume(partition, spart);
1279        n.addChild(partition);
1280      }
1281    }
1282  
1283    return true;
1284  }
1285  
1286  
1287  static bool detect_lif(source & s, hwNode & n)
1288  {
1289    static unsigned char buffer[LIFBLOCKSIZE];
1290    source lifvolume;
1291    unsigned long dir_start = 0, dir_length = 0;
1292    unsigned lif_version = 0;
1293    unsigned long ipl_addr = 0, ipl_length = 0;
1294  
1295    if(s.offset!=0)
1296      return false;                                 // LIF boot volumes must be at the beginning of the disk
1297  
1298    lifvolume = s;
1299    lifvolume.blocksize = LIFBLOCKSIZE;             // LIF blocks are 256 bytes long
1300  
1301                                                    // read the first block
1302    if(readlogicalblocks(lifvolume, buffer, 0, 1)!=1)
1303      return false;
1304  
1305    if(be_short(buffer)!=0x8000)                    // wrong magic number
1306      return false;
1307  
1308    dir_start = be_long(buffer+8);
1309    dir_length = be_long(buffer+16);
1310    lif_version = be_short(buffer+20);
1311  
1312    if(dir_start<2) return false;                   // blocks 0 and 1 are reserved
1313    if(dir_length<1) return false;                  // no directory to read from
1314    if(lif_version<1) return false;                 // weird LIF version
1315  
1316    ipl_addr = be_long(buffer+240);                 // byte address of IPL on media
1317    ipl_length = be_long(buffer+244);               // size of boot code
1318  #if 0
1319    ipl_entry = be_long(buffer+248);                // boot code entry point
1320  
1321    fprintf(stderr, "system: %x\n", be_short(buffer+12));
1322    fprintf(stderr, "start of directory: %ld\n", dir_start);
1323    fprintf(stderr, "length of directory: %ld\n", dir_length);
1324    fprintf(stderr, "lif version: %x\n", lif_version);
1325    fprintf(stderr, "tracks per surface: %ld\n", be_long(buffer+24));
1326    fprintf(stderr, "number of surfaces: %ld\n", be_long(buffer+28));
1327    fprintf(stderr, "blocks per track: %ld\n", be_long(buffer+32));
1328    fprintf(stderr, "ipl addr: %ld\n", ipl_addr);
1329    fprintf(stderr, "ipl length: %ld\n", ipl_length);
1330    fprintf(stderr, "ipl entry point: %lx\n", ipl_entry);
1331  #endif
1332  
1333    if((ipl_addr!=0) && (ipl_length>0)) n.addCapability("bootable", "Bootable disk");
1334  
1335    return true;
1336  }
1337  
1338  static bool detect_luks(source & s, hwNode & n)
1339  {
1340    static char buffer[BLOCKSIZE];
1341    source luksvolume;
1342    unsigned luks_version = 0;
1343  
1344    luksvolume = s;
1345    luksvolume.blocksize = BLOCKSIZE;
1346                                                    // read the first block
1347    if(readlogicalblocks(luksvolume, buffer, 0, 1)!=1)
1348      return false;
1349  
1350    if(memcmp(buffer, "LUKS", 4) != 0)                    // wrong magic number
1351      return false;
1352    if(be_short(buffer+4) != 0xbabe)
1353      return false;
1354  
1355    luks_version = be_short(buffer+6);
1356    if(luks_version<1)
1357      return false;                 // weird LUKS version
1358    else
1359    {
1360      hwNode partition("volume", hw::volume);
1361      scan_volume(partition, luksvolume);
1362      partition.setLogicalName(n.getLogicalName());
1363      n.addChild(partition);
1364    }
1365  
1366    return true;
1367  }
1368  
1369  bool scan_partitions(hwNode & n)
1370  {
1371    int i = 0;
1372    source s;
1373    int fd = open(n.getLogicalName().c_str(), O_RDONLY | O_NONBLOCK);
1374    hwNode * medium = NULL;
1375  
1376    if (fd < 0)
1377      return false;
1378  
1379    if(n.isCapable("removable"))
1380    {
1381      medium = n.addChild(hwNode("medium", hw::disk));
1382  
1383      medium->claim();
1384      medium->setSize(n.getSize());
1385      medium->setCapacity(n.getCapacity());
1386      medium->setLogicalName(n.getLogicalName());
1387    }
1388    else
1389      medium = &n;
1390  
1391    s.diskname = n.getLogicalName();
1392    s.fd = fd;
1393    s.offset = 0;
1394    s.blocksize = BLOCKSIZE;
1395    s.size = medium->getSize();
1396  
1397    while(map_types[i].id)
1398    {
1399      if(map_types[i].detect && map_types[i].detect(s, *medium))
1400      {
1401        medium->addCapability(string("partitioned"), "Partitioned disk");
1402        medium->addCapability(string("partitioned:") + string(map_types[i].id), string(map_types[i].description));
1403        break;
1404      }
1405      i++;
1406    }
1407  
1408    if(!medium->isCapable("partitioned"))
1409    {
1410      if(scan_volume(*medium, s))	// whole disk volume?
1411        medium->setClass(hw::volume);
1412    }
1413  
1414    close(fd);
1415  
1416  //if(medium != &n) free(medium);
1417  
1418    return false;
1419  }