Back to index

lshw  02.16
volumes.cc
Go to the documentation of this file.
00001 /*
00002  * volumes.cc
00003  *
00004  */
00005 
00006 #define _LARGEFILE_SOURCE
00007 #define _FILE_OFFSET_BITS 64
00008 
00009 #include "version.h"
00010 #include "config.h"
00011 #include "volumes.h"
00012 #include "blockio.h"
00013 #include "lvm.h"
00014 #include "osutils.h"
00015 #include <stdio.h>
00016 #include <sys/types.h>
00017 #include <sys/stat.h>
00018 #include <fcntl.h>
00019 #include <string.h>
00020 #include <unistd.h>
00021 #include <stdint.h>
00022 #include <time.h>
00023 
00024 __ID("@(#) $Id: volumes.cc 2433 2012-01-10 22:01:30Z lyonel $");
00025 
00026 struct fstypes
00027 {
00028   const char * id;
00029   const char * description;
00030   const char * capabilities;
00031   bool (*detect)(hwNode & n, source & s);
00032 };
00033 
00034 static bool detect_luks(hwNode & n, source & s);
00035 static bool detect_ext2(hwNode & n, source & s);
00036 static bool detect_reiserfs(hwNode & n, source & s);
00037 static bool detect_fat(hwNode & n, source & s);
00038 static bool detect_hfsx(hwNode & n, source & s);
00039 static bool detect_hfs(hwNode & n, source & s);
00040 static bool detect_ntfs(hwNode & n, source & s);
00041 static bool detect_swap(hwNode & n, source & s);
00042 
00043 static struct fstypes fs_types[] =
00044 {
00045   {"blank", "Blank", "", NULL},
00046   {"fat", "Windows FAT", "", detect_fat},
00047   {"ntfs", "Windows NTFS", "secure", detect_ntfs},
00048   {"hpfs", "OS/2 HPFS", "secure", NULL},
00049   {"ext2", "EXT2/EXT3", "secure", detect_ext2},
00050   {"reiserfs", "Linux ReiserFS", "secure,journaled", detect_reiserfs},
00051   {"romfs", "Linux ROMFS", "ro", NULL},
00052   {"squashfs", "Linux SquashFS", "ro", NULL},
00053   {"cramfs", "Linux CramFS", "ro", NULL},
00054   {"minixfs", "MinixFS", "secure", NULL},
00055   {"sysvfs", "System V FS", "secure", NULL},
00056   {"jfs", "Linux JFS", "secure,journaled", NULL},
00057   {"xfs", "Linux XFS", "secure,journaled", NULL},
00058   {"iso9660", "ISO-9660", "secure,ro", NULL},
00059   {"xboxdvd", "X-Box DVD", "ro", NULL},
00060   {"udf", "UDF", "secure,ro", NULL},
00061   {"ufs", "UFS", "secure", NULL},
00062   {"hphfs", "HP-UX HFS", "secure", NULL},
00063   {"vxfs", "VxFS", "secure,journaled", NULL},
00064   {"ffs", "FFS", "secure", NULL},
00065   {"befs", "BeOS BFS", "journaled", NULL},
00066   {"qnxfs", "QNX FS", "", NULL},
00067   {"mfs", "MacOS MFS", "", NULL},
00068   {"hfsplus", "MacOS HFS+", "secure,journaled", detect_hfsx},
00069   {"hfs", "MacOS HFS", "", detect_hfs},
00070   {"luks", "Linux Unified Key Setup", "encrypted", detect_luks},
00071   {"swap", "Linux swap", "", detect_swap},
00072   { NULL, NULL, NULL, NULL }
00073 };
00074 
00075 struct ext2_super_block {
00076         uint32_t   s_inodes_count;         /* Inodes count */
00077         uint32_t   s_blocks_count;         /* Blocks count */
00078         uint32_t   s_r_blocks_count;       /* Reserved blocks count */
00079         uint32_t   s_free_blocks_count;    /* Free blocks count */
00080         uint32_t   s_free_inodes_count;    /* Free inodes count */
00081         uint32_t   s_first_data_block;     /* First Data Block */
00082         uint32_t   s_log_block_size;       /* Block size */
00083         int32_t   s_log_frag_size;        /* Fragment size */
00084         uint32_t   s_blocks_per_group;     /* # Blocks per group */
00085         uint32_t   s_frags_per_group;      /* # Fragments per group */
00086         uint32_t   s_inodes_per_group;     /* # Inodes per group */
00087         uint32_t   s_mtime;                /* Mount time */
00088         uint32_t   s_wtime;                /* Write time */
00089         uint16_t   s_mnt_count;            /* Mount count */
00090         int16_t   s_max_mnt_count;        /* Maximal mount count */
00091         uint16_t   s_magic;                /* Magic signature */
00092         uint16_t   s_state;                /* File system state */
00093         uint16_t   s_errors;               /* Behaviour when detecting errors */
00094         uint16_t   s_minor_rev_level;      /* minor revision level */
00095         uint32_t   s_lastcheck;            /* time of last check */
00096         uint32_t   s_checkinterval;        /* max. time between checks */
00097         uint32_t   s_creator_os;           /* OS */
00098         uint32_t   s_rev_level;            /* Revision level */
00099         uint16_t   s_def_resuid;           /* Default uid for reserved blocks */
00100         uint16_t   s_def_resgid;           /* Default gid for reserved blocks */
00101         /*
00102          * These fields are for EXT2_DYNAMIC_REV superblocks only.
00103          *
00104          * Note: the difference between the compatible feature set and
00105          * the incompatible feature set is that if there is a bit set
00106          * in the incompatible feature set that the kernel doesn't
00107          * know about, it should refuse to mount the filesystem.
00108          *
00109          * e2fsck's requirements are more strict; if it doesn't know
00110          * about a feature in either the compatible or incompatible
00111          * feature set, it must abort and not try to meddle with
00112          * things it doesn't understand...
00113          */
00114         uint32_t   s_first_ino;            /* First non-reserved inode */
00115         uint16_t   s_inode_size;           /* size of inode structure */
00116         uint16_t   s_block_group_nr;       /* block group # of this superblock */
00117         uint32_t   s_feature_compat;       /* compatible feature set */
00118         uint32_t   s_feature_incompat;     /* incompatible feature set */
00119         uint32_t   s_feature_ro_compat;    /* readonly-compatible feature set */
00120         uint8_t    s_uuid[16];             /* 128-bit uuid for volume */
00121         char    s_volume_name[16];      /* volume name */
00122         char    s_last_mounted[64];     /* directory where last mounted */
00123         uint32_t   s_algorithm_usage_bitmap; /* For compression */
00124         /*
00125          * Performance hints.  Directory preallocation should only
00126          * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
00127          */
00128         uint8_t    s_prealloc_blocks;      /* Nr of blocks to try to preallocate*/
00129         uint8_t    s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
00130         uint16_t   s_reserved_gdt_blocks;  /* Per group table for online growth */
00131         /*
00132          * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
00133          */
00134         uint8_t    s_journal_uuid[16];     /* uuid of journal superblock */
00135         uint32_t   s_journal_inum;         /* inode number of journal file */
00136         uint32_t   s_journal_dev;          /* device number of journal file */
00137         uint32_t   s_last_orphan;          /* start of list of inodes to delete */
00138         uint32_t   s_hash_seed[4];         /* HTREE hash seed */
00139         uint8_t    s_def_hash_version;     /* Default hash version to use */
00140         uint8_t    s_jnl_backup_type;      /* Default type of journal backup */
00141         uint16_t   s_desc_size;            /* Group desc. size: INCOMPAT_64BIT */
00142         uint32_t   s_default_mount_opts;
00143         uint32_t   s_first_meta_bg;        /* First metablock group */
00144         uint32_t   s_mkfs_time;            /* When the filesystem was created */
00145         uint32_t   s_jnl_blocks[17];       /* Backup of the journal inode */
00146         uint32_t   s_blocks_count_hi;      /* Blocks count high 32bits */
00147         uint32_t   s_r_blocks_count_hi;    /* Reserved blocks count high 32 bits*/
00148         uint32_t   s_free_blocks_hi;       /* Free blocks count */
00149         uint16_t   s_min_extra_isize;      /* All inodes have at least # bytes */
00150         uint16_t   s_want_extra_isize;     /* New inodes should reserve # bytes */
00151         uint32_t   s_flags;                /* Miscellaneous flags */
00152         uint16_t   s_raid_stride;          /* RAID stride */
00153         uint16_t   s_mmp_interval;         /* # seconds to wait in MMP checking */
00154         uint64_t   s_mmp_block;            /* Block for multi-mount protection */
00155         uint32_t   s_raid_stripe_width;    /* blocks on all data disks (N*stride)*/
00156         uint32_t   s_reserved[163];        /* Padding to the end of the block */
00157 };
00158 
00159 /*
00160  * The second extended file system magic number
00161  */
00162 #define EXT2_SUPER_MAGIC    0xEF53
00163 
00164 #define EXT2_DEFAULT_BLOCK_SIZE    1024
00165 
00166 /*
00167  * File system states
00168  */
00169 #define EXT2_VALID_FS                   0x0001  /* Unmounted cleanly */
00170 #define EXT2_ERROR_FS                   0x0002  /* Errors detected */
00171 
00172 /*
00173  * Codes for operating systems
00174  */
00175 #define EXT2_OS_LINUX           0
00176 #define EXT2_OS_HURD            1
00177 #define EXT2_OS_MASIX           2
00178 #define EXT2_OS_FREEBSD         3
00179 #define EXT2_OS_LITES           4
00180 
00181 #define EXT2_FEATURE_COMPAT_DIR_PREALLOC        0x0001
00182 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES       0x0002
00183 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL         0x0004
00184 #define EXT2_FEATURE_COMPAT_EXT_ATTR            0x0008
00185 #define EXT2_FEATURE_COMPAT_RESIZE_INODE        0x0010
00186 #define EXT2_FEATURE_COMPAT_DIR_INDEX           0x0020
00187 #define EXT2_FEATURE_COMPAT_LAZY_BG             0x0040
00188 
00189 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER     0x0001
00190 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE       0x0002
00191 /* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR     0x0004 not used */
00192 #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE        0x0008
00193 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM         0x0010
00194 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK        0x0020
00195 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE      0x0040
00196 
00197 #define EXT2_FEATURE_INCOMPAT_COMPRESSION       0x0001
00198 #define EXT2_FEATURE_INCOMPAT_FILETYPE          0x0002
00199 #define EXT3_FEATURE_INCOMPAT_RECOVER           0x0004 /* Needs recovery */
00200 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV       0x0008 /* Journal device */
00201 #define EXT2_FEATURE_INCOMPAT_META_BG           0x0010
00202 #define EXT3_FEATURE_INCOMPAT_EXTENTS           0x0040
00203 #define EXT4_FEATURE_INCOMPAT_EXTENTS           0x0040
00204 #define EXT4_FEATURE_INCOMPAT_64BIT             0x0080
00205 #define EXT4_FEATURE_INCOMPAT_MMP               0x0100
00206 
00207 static string uuid(const uint8_t s_uuid[16])
00208 {
00209   char buffer[50];
00210 
00211   snprintf(buffer, sizeof(buffer), "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", s_uuid[0], s_uuid[1], s_uuid[2], s_uuid[3], s_uuid[4], s_uuid[5], s_uuid[6], s_uuid[7], s_uuid[8], s_uuid[9], s_uuid[10], s_uuid[11], s_uuid[12], s_uuid[13], s_uuid[14], s_uuid[15]);
00212   return string(buffer);
00213 }
00214 
00215 static string datetime(time_t timestamp, bool utc = true)
00216 {
00217   char buffer[50];
00218   tm ltime;
00219 
00220   if(timestamp)
00221   {
00222     if(utc)
00223       localtime_r(&timestamp, &ltime);
00224     else
00225       gmtime_r(&timestamp, &ltime);
00226     strftime(buffer, sizeof(buffer), "%F %T", &ltime);
00227     return string(buffer);
00228   }
00229   else
00230     return "";
00231 }
00232 
00233 static bool detect_ext2(hwNode & n, source & s)
00234 {
00235   static char buffer[EXT2_DEFAULT_BLOCK_SIZE];
00236   source ext2volume;
00237   ext2_super_block *sb = (ext2_super_block*)buffer;
00238   uint32_t ext2_version = 0;
00239   time_t mtime, wtime, mkfstime;
00240   unsigned long long blocksize = EXT2_DEFAULT_BLOCK_SIZE;
00241 
00242   ext2volume = s;
00243   ext2volume.blocksize = EXT2_DEFAULT_BLOCK_SIZE;
00244 
00245   if(readlogicalblocks(ext2volume, buffer, 1, 1)!=1) // read the second block
00246     return false;
00247 
00248   if(le_short(&sb->s_magic) != EXT2_SUPER_MAGIC) // wrong magic number
00249     return false;
00250 
00251   blocksize = 1024LL*(1 << le_long(&sb->s_log_block_size));
00252   if(blocksize < EXT2_DEFAULT_BLOCK_SIZE)
00253     blocksize = EXT2_DEFAULT_BLOCK_SIZE;
00254   n.setSize(blocksize * le_long(&sb->s_blocks_count));
00255 
00256   switch(le_long(&sb->s_creator_os))
00257   {
00258     case EXT2_OS_LINUX:
00259        n.setVendor("Linux");
00260        break;
00261     case EXT2_OS_HURD:
00262        n.setVendor("GNU Hurd");
00263        break;
00264     case EXT2_OS_MASIX:
00265        n.setVendor("MASIX");
00266        break;
00267     case EXT2_OS_FREEBSD:
00268        n.setVendor("FreeBSD");
00269        break;
00270     case EXT2_OS_LITES:
00271        n.setVendor("LITES");
00272        break;
00273   }
00274 
00275   ext2_version = le_short(&sb->s_rev_level);
00276   n.setVersion(tostring(ext2_version)+"."+tostring(le_short(&sb->s_minor_rev_level)));
00277 
00278   mtime = (time_t)le_long(&sb->s_mtime);
00279   n.setConfig("mounted", datetime(mtime));
00280   wtime = (time_t)le_long(&sb->s_wtime);
00281   n.setConfig("modified", datetime(wtime));
00282 
00283   if(ext2_version >= 1)
00284   {
00285     switch(le_short(&sb->s_state))
00286     {
00287       case EXT2_VALID_FS:
00288        n.setConfig("state", "clean");
00289        break;
00290       case EXT2_ERROR_FS:
00291        n.setConfig("state", "unclean");
00292        break;
00293       default:
00294        n.setConfig("state", "unknown");
00295     }
00296     n.setConfig("label", hw::strip(string(sb->s_volume_name, sizeof(sb->s_volume_name))));
00297     n.setConfig("lastmountpoint", hw::strip(string(sb->s_last_mounted, sizeof(sb->s_last_mounted))));
00298     n.setSerial(hw::strip(uuid(sb->s_uuid)));
00299 
00300     if(le_long(&sb->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)
00301     {
00302       n.addCapability("journaled");
00303       
00304       mkfstime = (time_t)le_long(&sb->s_mkfs_time);
00305       n.setConfig("created", datetime(mkfstime));
00306     }
00307     if(le_long(&sb->s_feature_compat) & EXT2_FEATURE_COMPAT_EXT_ATTR)
00308       n.addCapability("extended_attributes", _("Extended Attributes"));
00309     if(le_long(&sb->s_feature_ro_compat) & EXT2_FEATURE_RO_COMPAT_LARGE_FILE)
00310       n.addCapability("large_files", _("4GB+ files"));
00311     if(le_long(&sb->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_HUGE_FILE)
00312       n.addCapability("huge_files", _("16TB+ files"));
00313     if(le_long(&sb->s_feature_ro_compat) & EXT4_FEATURE_RO_COMPAT_DIR_NLINK)
00314       n.addCapability("dir_nlink", _("directories with 65000+ subdirs"));
00315     if(le_long(&sb->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_RECOVER)
00316       n.addCapability("recover", _("needs recovery"));
00317     if(le_long(&sb->s_feature_incompat) & EXT4_FEATURE_INCOMPAT_64BIT)
00318       n.addCapability("64bit", _("64bit filesystem"));
00319     if(le_long(&sb->s_feature_incompat) & EXT4_FEATURE_INCOMPAT_EXTENTS)
00320       n.addCapability("extents", _("extent-based allocation"));
00321   }
00322 
00323   if(n.isCapable("journaled"))
00324   {
00325     if(n.isCapable("huge_files") || n.isCapable("64bit") || n.isCapable("extents") || n.isCapable("dir_nlink"))
00326     {
00327       n.addCapability("ext4");
00328       n.setDescription(_("EXT4 volume"));
00329       n.setConfig("filesystem", "ext4");
00330     }
00331     else
00332     {
00333       n.addCapability("ext3");
00334       n.setDescription(_("EXT3 volume"));
00335       n.setConfig("filesystem", "ext3");
00336     }
00337   }
00338 
00339   return true;
00340 }
00341 
00342 static bool detect_luks(hwNode & n, source & s)
00343 {
00344   static char buffer[BLOCKSIZE];
00345   source luksvolume;
00346   unsigned luks_version = 0;
00347 
00348   luksvolume = s;
00349   luksvolume.blocksize = BLOCKSIZE;
00350 
00351                                                   // read the first block
00352   if(readlogicalblocks(luksvolume, buffer, 0, 1)!=1)
00353     return false;
00354 
00355   if(memcmp(buffer, "LUKS", 4) != 0)                    // wrong magic number
00356     return false;
00357   if(be_short(buffer+4) != 0xbabe)
00358     return false;
00359 
00360   luks_version = be_short(buffer+6);
00361   if(luks_version<1) return false;                 // weird LUKS version
00362 
00363   n.addCapability("encrypted", _("Encrypted volume"));
00364   n.setConfig("version", luks_version);
00365   n.setConfig("cipher", hw::strip(std::string(buffer+8, 32)));
00366   n.setConfig("mode", hw::strip(std::string(buffer+40, 32)));
00367   n.setConfig("hash", hw::strip(std::string(buffer+72, 32)));
00368   n.setConfig("bits", 8*be_long(buffer+108));
00369   n.setWidth(8*be_long(buffer+108));
00370   n.setSerial(hw::strip(std::string(buffer+168, 40)));
00371   n.setCapacity(luksvolume.size);
00372   n.setSize(luksvolume.size);
00373   return true;
00374 }
00375 
00376 #define REISERFSBLOCKSIZE 0x1000
00377 
00378 static bool detect_reiserfs(hwNode & n, source & s)
00379 {
00380   static char buffer[REISERFSBLOCKSIZE];
00381   source reiserfsvolume;
00382   string magic;
00383   long long blocksize = 0;
00384 
00385   reiserfsvolume = s;
00386   reiserfsvolume.blocksize = REISERFSBLOCKSIZE;
00387                                                   // read the 16th block
00388   if(readlogicalblocks(reiserfsvolume, buffer, 0x10, 1)!=1)
00389     return false;
00390 
00391   magic = hw::strip(string(buffer+52, 10));
00392   if(magic != "ReIsEr2Fs" && magic != "ReIsErFs" && magic != "ReIsEr3Fs")                    // wrong magic
00393     return false;
00394 
00395   n.setConfig("label", hw::strip(string(buffer + 0x64, 16)));
00396 
00397   blocksize = le_short(buffer+44);
00398   n.setSize(le_long(buffer)*blocksize);
00399 
00400   if(le_long(buffer+20) != 0)
00401     n.addCapability("journaled");
00402 
00403   n.setSerial(hw::strip(uuid((uint8_t*)buffer+0x54)));
00404 
00405   switch(le_long(buffer+64))
00406   {
00407     case 1:
00408        n.setConfig("hash", "tea");
00409        break;
00410     case 2:
00411        n.setConfig("hash", "yura");
00412        break;
00413     case 3:
00414        n.setConfig("hash", "r5");
00415   }
00416 
00417   switch(le_short(buffer+50))
00418   {
00419     case 1:
00420        n.setConfig("state", "clean");
00421        break;
00422     case 2:
00423        n.setConfig("state", "unclean");
00424        break;
00425     default:
00426        n.setConfig("state", "unknown");
00427   }
00428 
00429   if(magic == "ReIsErFs")
00430     n.setVersion("3.5");
00431   if(magic == "ReIsEr2Fs")
00432     n.setVersion("3.6");
00433   if(magic == "ReIsEr3Fs")
00434     n.setVersion("nonstandard " + tostring(le_short(buffer+72)));
00435 
00436   n.setCapacity(reiserfsvolume.size);
00437   return true;
00438 }
00439 
00440 static string dos_serial(unsigned long serial)
00441 {
00442   char buffer[16];
00443 
00444   snprintf(buffer, sizeof(buffer), "%04lx-%04lx", serial >> 16, serial & 0xffff);
00445 
00446   return string(buffer);
00447 }
00448 
00449 static bool detect_fat(hwNode & n, source & s)
00450 {
00451   static char buffer[BLOCKSIZE];
00452   source fatvolume;
00453   string magic, label;
00454   unsigned long long bytes_per_sector = 512;
00455   unsigned long long reserved_sectors = 0;
00456   unsigned long long hidden_sectors = 0;
00457   unsigned long long size = 0;
00458   unsigned long serial = 0;
00459 
00460   fatvolume = s;
00461   fatvolume.blocksize = BLOCKSIZE;
00462 
00463                                                   // read the first block
00464   if(readlogicalblocks(fatvolume, buffer, 0, 1)!=1)
00465     return false;
00466 
00467   if(be_short(buffer+0x1fe) != 0x55aa)           // no "boot" signature
00468     return false;
00469 
00470   magic = hw::strip(string(buffer+0x52, 8));
00471   if(magic != "FAT32")
00472     magic = hw::strip(string(buffer+0x36, 8));
00473   if(magic != "FAT12" && magic != "FAT16" && magic != "FAT32")                    // wrong magic
00474     return false;
00475 
00476   n.setVendor(hw::strip(string(buffer+0x3, 8)));
00477   n.setVersion(magic);
00478 
00479   bytes_per_sector = le_short(buffer+0xb);
00480   reserved_sectors = le_short(buffer+0xe);
00481   hidden_sectors = le_short(buffer+0x1c);
00482   size = le_long(buffer+0x20);
00483   size -= reserved_sectors + hidden_sectors;
00484   size *= bytes_per_sector;
00485   n.setSize(size);
00486   n.setCapacity(fatvolume.size);
00487 
00488   n.setConfig("FATs", buffer[0x10]);
00489 
00490   if(magic == "FAT32")
00491   {
00492     label = hw::strip(std::string(buffer+0x47, 11));
00493     serial = le_long(buffer+0x43);
00494   }
00495   else
00496   {
00497     label = hw::strip(std::string(buffer+0x2b, 11));
00498     serial = le_long(buffer+0x27);
00499   }
00500 
00501   if(label != "NO NAME" && label != "")
00502     n.setConfig("label", label);
00503 
00504   n.setSerial(dos_serial(serial));
00505   n.setDescription("");
00506 
00507   return true;
00508 }
00509 
00510 typedef uint32_t HFSCatalogNodeID;
00511 
00512 struct HFSPlusExtentDescriptor {
00513     uint32_t                  startBlock;
00514     uint32_t                  blockCount;
00515 };
00516 typedef HFSPlusExtentDescriptor HFSPlusExtentRecord[8];
00517 
00518 struct HFSPlusForkData {
00519     uint64_t                  logicalSize;
00520     uint32_t                  clumpSize;
00521     uint32_t                  totalBlocks;
00522     HFSPlusExtentRecord     extents;
00523 };
00524 
00525 struct HFSPlusVolumeHeader {
00526     uint16_t              signature;
00527     uint16_t              version;
00528     uint32_t              attributes;
00529     uint32_t              lastMountedVersion;
00530     uint32_t              journalInfoBlock;
00531  
00532     uint32_t              createDate;
00533     uint32_t              modifyDate;
00534     uint32_t              backupDate;
00535     uint32_t              checkedDate;
00536  
00537     uint32_t              fileCount;
00538     uint32_t              folderCount;
00539  
00540     uint32_t              blockSize;
00541     uint32_t              totalBlocks;
00542     uint32_t              freeBlocks;
00543  
00544     uint32_t              nextAllocation;
00545     uint32_t              rsrcClumpSize;
00546     uint32_t              dataClumpSize;
00547     HFSCatalogNodeID    nextCatalogID;
00548  
00549     uint32_t              writeCount;
00550     uint64_t              encodingsBitmap;
00551  
00552     uint32_t              finderInfo[8];
00553  
00554     HFSPlusForkData     allocationFile;
00555     HFSPlusForkData     extentsFile;
00556     HFSPlusForkData     catalogFile;
00557     HFSPlusForkData     attributesFile;
00558     HFSPlusForkData     startupFile;
00559 };
00560 
00561 enum {
00562     /* Bits 0-6 are reserved */
00563     kHFSVolumeHardwareLockBit       =  7,
00564     kHFSVolumeUnmountedBit          =  8,
00565     kHFSVolumeSparedBlocksBit       =  9,
00566     kHFSVolumeNoCacheRequiredBit    = 10,
00567     kHFSBootVolumeInconsistentBit   = 11,
00568     kHFSCatalogNodeIDsReusedBit     = 12,
00569     kHFSVolumeJournaledBit          = 13,
00570     /* Bit 14 is reserved */
00571     kHFSVolumeSoftwareLockBit       = 15
00572     /* Bits 16-31 are reserved */
00573 };
00574 
00575 #define HFSBLOCKSIZE 1024
00576 
00577 #define HFSTIMEOFFSET 2082844800UL // time difference between 1904-01-01 00:00:00 and 1970-01-01 00:00:00
00578 
00579 static bool detect_hfsx(hwNode & n, source & s)
00580 {
00581   static char buffer[HFSBLOCKSIZE];
00582   source hfsvolume;
00583   string magic;
00584   HFSPlusVolumeHeader *vol = (HFSPlusVolumeHeader*)buffer;
00585   uint16_t version = 0;
00586   uint32_t attributes = 0;
00587   time_t mkfstime, fscktime, wtime;
00588 
00589   hfsvolume = s;
00590   hfsvolume.blocksize = HFSBLOCKSIZE;
00591 
00592                                                   // read the second block
00593   if(readlogicalblocks(hfsvolume, buffer, 1, 1)!=1)
00594     return false;
00595 
00596   magic = hw::strip(string(buffer, 2));
00597   if((magic != "H+") && (magic != "HX"))         // wrong signature
00598     return false;
00599 
00600   version = be_short(&vol->version);
00601   if(version >= 5)
00602     n.addCapability("hfsx");
00603 
00604   magic = hw::strip(string(buffer+8, 4));
00605   if(magic == "10.0")
00606     n.setVendor("Mac OS X");
00607   if(magic == "8.10")
00608     n.setVendor("Mac OS");
00609   if(magic == "HFSJ")
00610     n.setVendor("Mac OS X (journaled)");
00611   if(magic == "fsck")
00612     n.setVendor("Mac OS X (fsck)");
00613   n.setConfig("lastmountedby", hw::strip(magic));
00614 
00615   n.setSize((unsigned long long)be_long(&vol->blockSize) * (unsigned long long)be_long(&vol->totalBlocks));
00616   n.setVersion(tostring(version));
00617  
00618   attributes = be_long(&vol->attributes);
00619   if(attributes & (1 << kHFSVolumeJournaledBit))
00620     n.addCapability("journaled");
00621   if(attributes & (1 << kHFSVolumeSoftwareLockBit))
00622     n.addCapability("ro");
00623   if(attributes & (1 << kHFSBootVolumeInconsistentBit))
00624     n.addCapability("recover");
00625   if(attributes & (1 << kHFSVolumeUnmountedBit))
00626     n.setConfig("state", "clean");
00627   else
00628     n.setConfig("state", "unclean");
00629   
00630   n.setSerial(uuid((uint8_t*)&vol->finderInfo[6]));     // finderInfo[6] and finderInfo[7] contain uuid
00631 
00632   if(vol->finderInfo[0])
00633     n.addCapability("bootable");
00634   if(vol->finderInfo[3])
00635     n.addCapability("macos", _("Contains a bootable Mac OS installation"));
00636   if(vol->finderInfo[5])
00637     n.addCapability("osx", _("Contains a bootable Mac OS X installation"));
00638   if(vol->finderInfo[0] && (vol->finderInfo[0] == vol->finderInfo[3]))
00639     n.setConfig("boot", "macos");
00640   if(vol->finderInfo[0] && (vol->finderInfo[0] == vol->finderInfo[5]))
00641     n.setConfig("boot", "osx");
00642 
00643   mkfstime = (time_t)(be_long(&vol->createDate) - HFSTIMEOFFSET);
00644   fscktime = (time_t)(be_long(&vol->checkedDate) - HFSTIMEOFFSET);
00645   wtime = (time_t)(be_long(&vol->modifyDate) - HFSTIMEOFFSET);
00646   n.setConfig("created", datetime(mkfstime, false));    // creation time uses local time
00647   n.setConfig("checked", datetime(fscktime));
00648   n.setConfig("modified", datetime(wtime));
00649 
00650   return true;
00651 }
00652 
00653 /* Master Directory Block (HFS only) */
00654 
00655 #pragma pack(2)
00656 struct HFSMasterDirectoryBlock
00657 {
00658   uint16_t drSigWord;           /* volume signature */
00659 
00660   uint32_t drCrDate;              /* date and time of volume creation */
00661   uint32_t drLsMod;               /* date and time of last modification */
00662   uint16_t drAtrb;                /* volume attributes */
00663   int16_t drNmFls;               /* number of files in root folder */
00664   uint16_t drVBMSt;               /* first block of volume bitmap */
00665   uint16_t drAllocPtr;            /* start of next allocation search */
00666   uint16_t drNmAlBlks;            /* number of allocation blocks in volume */
00667   int32_t drAlBlkSiz;            /* size (in bytes) of allocation blocks */
00668   int32_t drClpSiz;              /* default clump size */
00669   int16_t drAlBlSt;              /* first allocation block in volume */
00670   uint32_t drNxtCNID;             /* next unused catalog node ID */
00671   int16_t drFreeBks;             /* number of unused allocation blocks */
00672   char drVN[28];                /* volume name */
00673   uint32_t drVolBkUp;            /* date and time of last backup */
00674   uint16_t drVSeqNum;      /* backup sequence number */
00675   uint32_t drWrCnt;        /* fs write count */
00676   uint32_t drXTClpSiz;     /* clumpsize for the extents B-tree */
00677   uint32_t drCTClpSiz;     /* clumpsize for the catalog B-tree */
00678   uint16_t drNmRtDirs;     /* number of directories in the root directory */
00679   uint32_t drFilCnt;       /* number of files in the fs */
00680   uint32_t drDirCnt;       /* number of directories in the fs */
00681   uint32_t drFndrInfo[8]; /* data used by the Finder */
00682   uint16_t drEmbedSigWord; /* embedded volume signature */
00683   uint32_t drEmbedExtent;  /* starting block number (xdrStABN) 
00684                                    and number of allocation blocks 
00685                                    (xdrNumABlks) occupied by embedded
00686                                    volume */
00687   uint32_t drXTFlSize;     /* bytes in the extents B-tree */
00688   uint8_t  drXTExtRec[12]; /* extents B-tree's first 3 extents */
00689   uint32_t drCTFlSize;     /* bytes in the catalog B-tree */
00690   uint8_t  drCTExtRec[12]; /* catalog B-tree's first 3 extents */
00691 };
00692 
00693 #define HFS_ATTR_HW_LOCKED         (1 << 7) // Set if the volume is locked by hardware
00694 #define HFS_ATTR_CLEAN                    (1 << 8) // Set if the volume was successfully unmounted
00695 #define HFS_ATTR_BAD_BLOCKS_SPARED (1 << 9) // Set if the volume has had its bad blocks spared
00696 #define HFS_ATTR_LOCKED                   (1 << 15) // Set if the volume is locked by software
00697 
00698 static bool detect_hfs(hwNode & n, source & s)
00699 {
00700   static char buffer[HFSBLOCKSIZE];
00701   source hfsvolume;
00702   string magic;
00703   HFSMasterDirectoryBlock *vol = (HFSMasterDirectoryBlock*)buffer;
00704   uint16_t attributes = 0;
00705   time_t mkfstime, dumptime, wtime;
00706 
00707   hfsvolume = s;
00708   hfsvolume.blocksize = HFSBLOCKSIZE;
00709 
00710                                                   // read the second block
00711   if(readlogicalblocks(hfsvolume, buffer, 1, 1)!=1)
00712     return false;
00713 
00714   magic = hw::strip(string(buffer, 2));
00715   if((magic != "BD"))              // wrong signature
00716     return false;
00717 
00718   n.setSize((unsigned long long)be_short(&vol->drNmAlBlks) * (unsigned long long)be_long(&vol->drAlBlkSiz));
00719   n.setConfig("label", hw::strip(string(vol->drVN, sizeof(vol->drVN))));
00720 
00721   attributes = be_short(&vol->drAtrb);
00722   if(attributes & (HFS_ATTR_LOCKED | HFS_ATTR_HW_LOCKED))
00723     n.addCapability("ro");
00724   if(attributes & HFS_ATTR_CLEAN)
00725     n.setConfig("state", "clean");
00726   else
00727     n.setConfig("state", "unclean");
00728   if(vol->drFndrInfo[0])
00729     n.addCapability("bootable");
00730 
00731   mkfstime = (time_t)be_long(&vol->drCrDate);
00732   dumptime = (time_t)be_long(&vol->drVolBkUp);
00733   wtime = (time_t)be_long(&vol->drLsMod);
00734   if(mkfstime)
00735     n.setConfig("created", datetime(mkfstime - HFSTIMEOFFSET, false));       // all dates use local time
00736   if(dumptime)
00737     n.setConfig("backup", datetime(dumptime - HFSTIMEOFFSET, false));
00738   if(wtime)
00739     n.setConfig("modified", datetime(wtime - HFSTIMEOFFSET, false));
00740 
00741   return true;
00742 }
00743 
00744 struct mft_entry
00745 {
00746   char magic[4];
00747   uint16_t usa_ofs;
00748   uint16_t usa_count;
00749 
00750   uint64_t logseqnumber;
00751   uint16_t seq;
00752   uint16_t links;
00753   uint16_t attr_ofs;
00754   uint16_t flags;
00755   uint32_t bytes_used;
00756   uint32_t bytes_allocated;
00757   
00758   /* some garbage */
00759 };
00760 
00761 // for flags
00762 #define MFT_RECORD_IN_USE 1
00763 #define MFT_RECORD_IS_DIRECTORY 2
00764 
00765 struct attr_entry
00766 {
00767 /*Ofs*/ 
00768 /*  0*/ uint32_t type;        /* The (32-bit) type of the attribute. */
00769 /*  4*/ uint32_t length;             /* Byte size of the resident part of the
00770                                    attribute (aligned to 8-byte boundary).
00771                                    Used to get to the next attribute. */
00772 /*  8*/ uint8_t non_resident;        /* If 0, attribute is resident.
00773                                    If 1, attribute is non-resident. */
00774 /*  9*/ uint8_t name_length;         /* Unicode character size of name of attribute.
00775                                    0 if unnamed. */
00776 /* 10*/ uint16_t name_offset;        /* If name_length != 0, the byte offset to the
00777                                    beginning of the name from the attribute
00778                                    record. Note that the name is stored as a
00779                                    Unicode string. When creating, place offset
00780                                    just at the end of the record header. Then,
00781                                    follow with attribute value or mapping pairs
00782                                    array, resident and non-resident attributes
00783                                    respectively, aligning to an 8-byte
00784                                    boundary. */
00785 /* 12*/ uint16_t flags;       /* Flags describing the attribute. */
00786 /* 14*/ uint16_t instance;           /* The instance of this attribute record. This
00787                                    number is unique within this mft record (see
00788                                    MFT_RECORD/next_attribute_instance notes
00789                                    above for more details). */
00790                 /* CAUTION: we only use resident attributes. */
00791                 struct {
00792 /* 16 */                uint32_t value_length; /* Byte size of attribute value. */
00793 /* 20 */                uint16_t value_offset; /* Byte offset of the attribute
00794                                                value from the start of the
00795                                                attribute record. When creating,
00796                                                align to 8-byte boundary if we
00797                                                have a name present as this might
00798                                                not have a length of a multiple
00799                                                of 8-bytes. */
00800 /* 22 */                uint8_t resident_flags; /* See above. */
00801 /* 23 */                int8_t reservedR;       /* Reserved/alignment to 8-byte
00802                                                boundary. */
00803                 };
00804 };
00805 
00806 typedef enum {
00807         AT_UNUSED                       = 0,
00808         AT_STANDARD_INFORMATION         = 0x10,
00809         AT_ATTRIBUTE_LIST               = 0x20,
00810         AT_FILE_NAME                    = 0x30,
00811         AT_OBJECT_ID                    = 0x40,
00812         AT_VOLUME_NAME                  = 0x60,
00813         AT_VOLUME_INFORMATION           = 0x70,
00814         AT_END                          = 0xffffffff,
00815        // other values are defined but we don't use them
00816 } ATTR_TYPES;
00817 
00818 typedef enum { 
00819         VOLUME_IS_DIRTY                 = 0x0001,
00820         VOLUME_RESIZE_LOG_FILE          = 0x0002,
00821         VOLUME_UPGRADE_ON_MOUNT         = 0x0004,
00822         VOLUME_MOUNTED_ON_NT4           = 0x0008,
00823         VOLUME_DELETE_USN_UNDERWAY      = 0x0010,
00824         VOLUME_REPAIR_OBJECT_ID         = 0x0020,
00825         VOLUME_MODIFIED_BY_CHKDSK       = 0x8000,
00826         VOLUME_FLAGS_MASK               = 0x803f,
00827 } VOLUME_FLAGS;
00828 
00829 struct volinfo {
00830         uint64_t reserved;           /* Not used (yet?). */
00831         uint8_t major_ver;           /* Major version of the ntfs format. */
00832         uint8_t minor_ver;           /* Minor version of the ntfs format. */
00833         uint16_t flags;     /* Bit array of VOLUME_* flags. */
00834 };
00835 
00836 struct stdinfo
00837 {
00838 /*Ofs*/
00839 /*  0*/ int64_t creation_time;              /* Time file was created. Updated when
00840                                            a filename is changed(?). */
00841 /*  8*/ int64_t last_data_change_time;      /* Time the data attribute was last
00842                                            modified. */
00843 /* 16*/ int64_t last_mft_change_time;       /* Time this mft record was last
00844                                            modified. */
00845 /* 24*/ int64_t last_access_time;           /* Approximate time when the file was
00846                                            last accessed (obviously this is not
00847                                            updated on read-only volumes). In
00848                                            Windows this is only updated when
00849                                            accessed if some time delta has
00850                                            passed since the last update. Also,
00851                                            last access times updates can be
00852                                            disabled altogether for speed. */
00853 /* 32*/ uint32_t file_attributes; /* Flags describing the file. */
00854        /* CAUTION: this structure is incomplete */
00855 };
00856 
00857 #define MFT_RECORD_SIZE 1024
00858 #define MFT_MFT             0      // inode for $MFT
00859 #define MFT_MFTMirr  1      // inode for $MFTMirr
00860 #define MFT_LogFile  2      // inode for $LogFile
00861 #define MFT_VOLUME   3      // inode for $Volume
00862 
00863 #define NTFSTIMEOFFSET ((int64_t)(369 * 365 + 89) * 24 * 3600 * 10000000)
00864 
00865 static time_t ntfs2utc(int64_t ntfs_time)
00866 {
00867   return (ntfs_time - (NTFSTIMEOFFSET)) / 10000000;
00868 }
00869 
00870 static bool detect_ntfs(hwNode & n, source & s)
00871 {
00872   static char buffer[BLOCKSIZE];
00873   source ntfsvolume;
00874   string magic, serial, name, version;
00875   unsigned long long bytes_per_sector = 512;
00876   unsigned long long sectors_per_cluster = 8;
00877   signed char clusters_per_mft_record = 1;
00878   signed char clusters_per_index_record = 1;
00879   unsigned long long reserved_sectors = 0;
00880   unsigned long long hidden_sectors = 0;
00881   unsigned long long size = 0;
00882   unsigned long long mft = 0;
00883   mft_entry *entry = NULL;
00884   attr_entry *attr = NULL;
00885   unsigned long long mft_record_size = MFT_RECORD_SIZE;
00886   volinfo *vi = NULL;
00887   stdinfo *info = NULL;
00888   string guid = "";
00889 
00890   ntfsvolume = s;
00891   ntfsvolume.blocksize = BLOCKSIZE;
00892 
00893                                                   // read the first block
00894   if(readlogicalblocks(ntfsvolume, buffer, 0, 1)!=1)
00895     return false;
00896 
00897   if(be_short(buffer+0x1fe) != 0x55aa)           // no "boot" signature
00898     return false;
00899 
00900   magic = hw::strip(string(buffer+3, 8));
00901   if(magic != "NTFS")                            // wrong magic
00902     return false;
00903 
00904   bytes_per_sector = le_short(buffer+0xb);
00905   sectors_per_cluster = le_short(buffer+0xd);
00906   reserved_sectors = le_short(buffer+0xe);
00907   hidden_sectors = le_short(buffer+0x1c);
00908   size = le_long(buffer+0x28);
00909   size -= reserved_sectors + hidden_sectors;
00910   size *= bytes_per_sector;
00911 
00912   serial = dos_serial(le_long(buffer+0x48));
00913 
00914   mft = le_longlong(buffer+0x30);
00915   clusters_per_mft_record = (char)buffer[0x40];
00916   if(clusters_per_mft_record < 0)
00917     mft_record_size = 1 << -clusters_per_mft_record;
00918   else
00919   {
00920     mft_record_size = clusters_per_mft_record;
00921     mft_record_size *= bytes_per_sector * sectors_per_cluster;
00922   }
00923   clusters_per_index_record = (char)buffer[0x44];
00924 
00925   ntfsvolume = s;
00926   ntfsvolume.offset += mft * bytes_per_sector * sectors_per_cluster; // point to $MFT
00927   ntfsvolume.blocksize = mft_record_size;
00928   // FIXME mft_record_size<=sizeof(buffer)
00929   if(readlogicalblocks(ntfsvolume, buffer, MFT_VOLUME, 1)!=1)  // read $Volume
00930     return false;
00931 
00932   entry = (mft_entry*)buffer;
00933   if(strncmp(entry->magic, "FILE", 4) != 0)
00934     return false;
00935 
00936   if(le_short(&entry->flags) && MFT_RECORD_IN_USE)      // $Volume is valid
00937   {
00938     unsigned offset = le_short(&entry->attr_ofs);
00939 
00940     while(offset < mft_record_size)
00941     {
00942        attr = (attr_entry*)(buffer+offset);
00943 
00944        if(attr->type == AT_END)
00945          break;
00946 
00947        if(!attr->non_resident)            // ignore non-resident attributes
00948          switch(le_long(&attr->type))
00949          {
00950            case AT_STANDARD_INFORMATION:
00951              info = (stdinfo*)(buffer+offset+le_short(&attr->value_offset));
00952              break;
00953            case AT_VOLUME_INFORMATION:
00954              vi = (volinfo*)(buffer+offset+le_short(&attr->value_offset));
00955              vi->flags = le_short(&vi->flags);
00956              version = tostring(vi->major_ver) + "." + tostring(vi->minor_ver);
00957              break;
00958            case AT_OBJECT_ID:
00959              guid = uuid((uint8_t*)buffer+offset+le_short(&attr->value_offset));
00960              break;
00961            case AT_VOLUME_NAME:
00962              name = utf8((uint16_t*)(buffer+offset+le_short(&attr->value_offset)), le_short(&attr->value_length)/2, true);
00963              break;
00964            }
00965        offset += le_short(&attr->length);
00966     }
00967   }
00968 
00969   n.setSize(size);
00970   n.setCapacity(ntfsvolume.size);
00971   n.setConfig("clustersize", bytes_per_sector * sectors_per_cluster);
00972   n.setConfig("label", name);
00973   n.setSerial(serial);
00974   if(guid != "")
00975     n.setSerial(guid);
00976   n.setVersion(version);
00977   n.setDescription("");
00978   if(vi)
00979   {
00980     if(vi->flags && VOLUME_IS_DIRTY)
00981       n.setConfig("state", "dirty");
00982     else
00983       n.setConfig("state", "clean");
00984 
00985     if(vi->flags && VOLUME_MODIFIED_BY_CHKDSK)
00986       n.setConfig("modified_by_chkdsk", "true");
00987     if(vi->flags && VOLUME_MOUNTED_ON_NT4)
00988       n.setConfig("mounted_on_nt4", "true");
00989     if(vi->flags && VOLUME_UPGRADE_ON_MOUNT)
00990       n.setConfig("upgrade_on_mount", "true");
00991     if(vi->flags && VOLUME_RESIZE_LOG_FILE)
00992       n.setConfig("resize_log_file", "true");
00993   }
00994   if(info)
00995   {
00996     n.setConfig("created", datetime(ntfs2utc(le_longlong(&info->creation_time))));
00997     //n.setConfig("mounted", datetime(ntfs2utc(le_longlong(&info->last_access_time))));
00998     //n.setConfig("modified", datetime(ntfs2utc(le_longlong(&info->last_mft_change_time))));
00999   }
01000   return true;
01001 }
01002 
01003 #define SWAPBLOCKSIZE 1024
01004 
01005 static bool detect_swap(hwNode & n, source & s)
01006 {
01007   static char buffer[SWAPBLOCKSIZE];
01008   source swapvolume;
01009   unsigned long version = 0;
01010   unsigned long pages = 0;
01011   unsigned long badpages = 0;
01012   unsigned long long pagesize = 4096;
01013   bool bigendian = false;
01014   string signature = "";
01015 
01016   swapvolume = s;
01017   swapvolume.blocksize = SWAPBLOCKSIZE;
01018 
01019   if(readlogicalblocks(swapvolume, buffer, 3, 1)!=1) // look for a signature
01020     return false;
01021   signature = string(buffer+SWAPBLOCKSIZE-10, 10);
01022   if(signature != "SWAPSPACE2" && signature != "SWAP-SPACE")
01023     return false;
01024 
01025   swapvolume = s;
01026   swapvolume.blocksize = SWAPBLOCKSIZE;
01027   if(readlogicalblocks(swapvolume, buffer, 1, 1)!=1) // skip the first block
01028     return false;
01029 
01030   version = le_long(buffer);
01031   if(version == 0x01000000)
01032   {
01033     bigendian = true;
01034     version = 1;
01035   }
01036 
01037   if((version < 0) || (version > 1))      // unknown version
01038     return false;
01039 
01040   if(bigendian)
01041   {
01042     pages = be_long(buffer + 4) + 1;
01043     badpages = be_long(buffer + 8);
01044   }
01045   else
01046   {
01047     pages = le_long(buffer + 4) + 1;
01048     badpages = le_long(buffer + 8);
01049   }
01050   pagesize = swapvolume.size / (unsigned long long)pages;
01051 
01052   n.setSerial(uuid((uint8_t*)buffer+0xc));
01053   n.setConfig("label", hw::strip(std::string(buffer+0x1c, 16)));
01054   n.setConfig("pagesize", pagesize);
01055   n.setSize((unsigned long long)(pages - badpages) * pagesize);
01056   n.setCapacity(swapvolume.size);
01057   n.setVersion(tostring(version));
01058   n.setDescription("");
01059 
01060   return true;
01061 }
01062 
01063 bool scan_volume(hwNode & n, source & s)
01064 {
01065   int i = 0;
01066 
01067   while(fs_types[i].id)
01068   {
01069     if(fs_types[i].detect && fs_types[i].detect(n, s))
01070     {
01071       n.addCapability(fs_types[i].id, fs_types[i].description);
01072       if(n.getConfig("filesystem") == "")
01073         n.setConfig("filesystem", fs_types[i].id);
01074       n.addCapability("initialized", _("initialized volume"));
01075       if(n.getDescription()=="")
01076         n.setDescription(string(fs_types[i].description) + " "+string(_("volume")));
01077       return true;
01078     }
01079     i++;
01080   }
01081 
01082   return scan_lvm(n,s);
01083 }