Description: add support to Ext4 and brtfs. Update detection string
             for recent GRUB2 versions. This patch was pulled from
             upstream CVS (revision 1.32 for detect.c and 1.19 for
             linux.c). Thanks to Frederic Boiteux for suggest this
             solution in #563805.
Author: Christoph Pfisterer <chrisp@users.sf.net>
Origin: http://disktype.cvs.sourceforge.net/viewvc/disktype/disktype/
Reviewed-by: Joao Eriberto Mota Filho <eriberto@debian.org>
Last-Update: 2014-09-13

Index: disktype-9/detect.c
===================================================================
--- disktype-9.orig/detect.c
+++ disktype-9/detect.c
@@ -59,7 +59,8 @@ void detect_cdrom_misc(SECTION *section,
 void detect_udf(SECTION *section, int level);
 
 /* in linux.c */
-void detect_ext23(SECTION *section, int level);
+void detect_ext234(SECTION *section, int level);
+void detect_btrfs(SECTION *section, int level);
 void detect_reiser(SECTION *section, int level);
 void detect_reiser4(SECTION *section, int level);
 void detect_linux_raid(SECTION *section, int level);
@@ -136,7 +137,8 @@ DETECTOR detectors[] = {
   detect_udf,
   detect_cdrom_misc,
   detect_iso,
-  detect_ext23,
+  detect_ext234,
+  detect_btrfs,
   detect_reiser,
   detect_reiser4,
   detect_linux_raid,
@@ -153,7 +155,7 @@ DETECTOR detectors[] = {
   detect_bfs,
   /* 5: file formats */
   detect_archive,
-  detect_compressed,  /* this is here because of boot disks */
+  detect_compressed,  /* this is down here because of boot disks */
   /* 6: blank formatted disk */
   detect_blank,
 
Index: disktype-9/linux.c
===================================================================
--- disktype-9.orig/linux.c
+++ disktype-9/linux.c
@@ -28,13 +28,14 @@
 #include "global.h"
 
 /*
- * ext2/ext3 file system
+ * ext2/ext3/ext4 file system
  */
 
-void detect_ext23(SECTION *section, int level)
+void detect_ext234(SECTION *section, int level)
 {
   unsigned char *buf;
   char s[256];
+  int fslevel, is_journal, is_dev;
   u4 blocksize;
   u8 blockcount;
 
@@ -42,12 +43,32 @@ void detect_ext23(SECTION *section, int
     return;
 
   if (get_le_short(buf + 56) == 0xEF53) {
-    if (get_le_long(buf + 96) & 0x0008)       /* JOURNAL_DEV flag */
-      print_line(level, "Ext3 external journal");
-    else if (get_le_long(buf + 92) & 0x0004)  /* HAS_JOURNAL flag */
-      print_line(level, "Ext3 file system");
-    else
-      print_line(level, "Ext2 file system");
+    fslevel = 2;
+    is_journal = 0;
+    is_dev = 0;
+    /* Ext3/4 external journal: INCOMPAT feature JOURNAL_DEV */
+    if (get_le_long(buf + 96) & 0x0008) {
+      is_journal = 1;
+      fslevel = 3;  /* at least ext3, ext2 has no journalling */
+    }
+    /* Ext3/4 COMPAT feature: HAS_JOURNAL */
+    if (get_le_long(buf + 92) & 0x0004)
+      fslevel = 3;
+    /* Ext4 INCOMPAT features: EXTENTS, 64BIT, FLEX_BG */
+    if (get_le_long(buf + 96) & 0x02C0)
+      fslevel = 4;
+    /* Ext4 RO_COMPAT features: HUGE_FILE, GDT_CSUM, DIR_NLINK, EXTRA_ISIZE */
+    if (get_le_long(buf + 100) & 0x0078)
+      fslevel = 4;
+    /* Ext4 sets min_extra_isize even on external journals */
+    if (get_le_short(buf + 348) >= 0x1c)
+      fslevel = 4;
+    /* Ext4dev TEST_FILESYS flag */
+    if (get_le_long(buf + 352) & 0x0004)
+      is_dev = 1;
+
+    print_line(level, "Ext%d%s %s", fslevel, is_dev ? "dev" : "",
+               is_journal ? "external journal" : "file system");
 
     get_string(buf + 120, 16, s);
     if (s[0])
@@ -73,6 +94,33 @@ void detect_ext23(SECTION *section, int
 }
 
 /*
+ * btrfs file system
+ */
+
+void detect_btrfs(SECTION *section, int level)
+{
+  unsigned char *buf;
+  char s[258];
+
+  if (get_buffer(section, 64 * 1024, 1024, (void **)&buf) < 1024)
+    return;
+
+  if (memcmp(buf + 64, "_BHRfS_M", 8) == 0) {
+    print_line(level, "Btrfs file system");
+
+    get_string(buf + 299, 256, s);
+    if (s[0])
+      print_line(level + 1, "Volume name \"%s\"", s);
+
+    format_uuid(buf + 32, s);
+    print_line(level + 1, "UUID %s", s);
+
+    format_size(s, get_le_quad(buf + 0x70));
+    print_line(level + 1, "Volume size %s", s);
+  }
+}
+
+/*
  * ReiserFS file system
  */
 
@@ -100,13 +148,13 @@ void detect_reiser(SECTION *section, int
     } else if (memcmp(buf + 52, "ReIsEr3Fs", 9) == 0) {
       newformat = get_le_short(buf + 72);
       if (newformat == 0) {
-	print_line(level, "ReiserFS file system (old 3.5 format, non-standard journal, starts at %d KiB)", at);
+        print_line(level, "ReiserFS file system (old 3.5 format, non-standard journal, starts at %d KiB)", at);
       } else if (newformat == 2) {
-	print_line(level, "ReiserFS file system (new 3.6 format, non-standard journal, starts at %d KiB)", at);
-	newformat = 1;
+        print_line(level, "ReiserFS file system (new 3.6 format, non-standard journal, starts at %d KiB)", at);
+        newformat = 1;
       } else {
-	print_line(level, "ReiserFS file system (v3 magic, but unknown version %d, starts at %d KiB)", newformat, at);
-	continue;
+        print_line(level, "ReiserFS file system (v3 magic, but unknown version %d, starts at %d KiB)", newformat, at);
+        continue;
       }
     } else
       continue;
@@ -164,7 +212,7 @@ void detect_reiser4(SECTION *section, in
 
   format_size(s, blocksize);
   print_line(level, "Reiser4 file system (%s, block size %s)",
-	     layout_name, s);
+             layout_name, s);
 
   /* get label and UUID */
   get_string(buf + 36, 16, s);
@@ -231,8 +279,8 @@ void detect_linux_raid(SECTION *section,
     return;
 
   print_line(level, "Linux RAID disk, version %lu.%lu.%lu",
-	     get_le_long(buf + 4), get_le_long(buf + 8),
-	     get_le_long(buf + 12));
+             get_le_long(buf + 4), get_le_long(buf + 8),
+             get_le_long(buf + 12));
 
   /* get some data */
   rlevel = (int)(long)get_le_long(buf + 28);   /* is signed, actually */
@@ -243,10 +291,10 @@ void detect_linux_raid(SECTION *section,
   /* find the name for the personality in the table */
   if (rlevel < -4 || rlevel > 5 || levels[rlevel+4] == NULL) {
     print_line(level + 1, "Unknown RAID level %d using %d regular %d spare disks",
-	       rlevel, raid_disks, spare);
+               rlevel, raid_disks, spare);
   } else {
     print_line(level + 1, "%s set using %d regular %d spare disks",
-	       levels[rlevel+4], raid_disks, spare);
+               levels[rlevel+4], raid_disks, spare);
   }
 
   /* get the UUID */
@@ -279,8 +327,8 @@ void detect_linux_lvm(SECTION *section,
 
   minor_version = get_le_short(buf + 2);
   print_line(level, "Linux LVM1 volume, version %d%s",
-	     minor_version,
-	     (minor_version < 1 || minor_version > 2) ? " (unknown)" : "");
+             minor_version,
+             (minor_version < 1 || minor_version > 2) ? " (unknown)" : "");
 
   /* volume group name */
   get_string(buf + 172, 128, s);
@@ -315,7 +363,7 @@ void detect_linux_lvm(SECTION *section,
   /* try to detect from first PE */
   if (pe_start > 0) {
     analyze_recursive(section, level + 1,
-		      pe_start, 0, 0);
+                      pe_start, 0, 0);
     /* TODO: elaborate on this by reading the PE allocation map */
   }
 }
@@ -348,16 +396,16 @@ void detect_linux_lvm2(SECTION *section,
     if (memcmp(buf + 24, "LVM2 001", 8) != 0) {
       get_string(buf + 24, 8, s);
       print_line(level, "LABELONE label at sector %d, unknown type \"%s\"",
-		 at, s);
+                 at, s);
       return;
     }
 
     print_line(level, "Linux LVM2 volume, version 001");
     print_line(level + 1, "LABELONE label at sector %d",
-	       at);
+               at);
 
     if (labeloffset >= 512 || labelsector > 256 ||
-	labelsector != at) {
+        labelsector != at) {
       print_line(level + 1, "LABELONE data inconsistent, aborting analysis");
       return;
     }
@@ -375,10 +423,10 @@ void detect_linux_lvm2(SECTION *section,
     mdoffset = 0;
     for (i = 0; i < 16; i++)
       if (get_le_quad(buf + labeloffset + 40 + i * 16) == 0) {
-	i++;
-	mdoffset = get_le_quad(buf + labeloffset + 40 + i * 16);
-	mdsize = get_le_quad(buf + labeloffset + 40 + i * 16 + 8);
-	break;
+        i++;
+        mdoffset = get_le_quad(buf + labeloffset + 40 + i * 16);
+        mdsize = get_le_quad(buf + labeloffset + 40 + i * 16 + 8);
+        break;
       }
     if (mdoffset == 0)
       return;
@@ -418,28 +466,28 @@ void detect_linux_swap(SECTION *section,
 
     if (memcmp((char *)buf + 512 - 10, "SWAP-SPACE", 10) == 0) {
       print_line(level, "Linux swap, version 1, %d KiB pages",
-		 pagesize >> 10);
+                 pagesize >> 10);
     }
     if (memcmp((char *)buf + 512 - 10, "SWAPSPACE2", 10) == 0) {
       if (get_buffer(section, 1024, 512, (void **)&buf) != 512)
-	break;  /* really shouldn't happen */
+        break;  /* really shouldn't happen */
 
       for (en = 0; en < 2; en++) {
-	version = get_ve_long(en, buf);
-	if (version >= 1 && version < 10)
-	  break;
+        version = get_ve_long(en, buf);
+        if (version >= 1 && version < 10)
+          break;
       }
       if (en < 2) {
-	print_line(level, "Linux swap, version 2, subversion %d, %d KiB pages, %s",
-		   (int)version, pagesize >> 10, get_ve_name(en));
-	if (version == 1) {
-	  pages = get_ve_long(en, buf + 4) - 1;
-	  format_blocky_size(s, pages, pagesize, "pages", NULL);
-	  print_line(level + 1, "Swap size %s", s);
-	}
+        print_line(level, "Linux swap, version 2, subversion %d, %d KiB pages, %s",
+                   (int)version, pagesize >> 10, get_ve_name(en));
+        if (version == 1) {
+          pages = get_ve_long(en, buf + 4) - 1;
+          format_blocky_size(s, pages, pagesize, "pages", NULL);
+          print_line(level + 1, "Swap size %s", s);
+        }
       } else {
-	print_line(level, "Linux swap, version 2, illegal subversion, %d KiB pages",
-		   pagesize >> 10);
+        print_line(level, "Linux swap, version 2, illegal subversion, %d KiB pages",
+                   pagesize >> 10);
       }
     }
   }
@@ -479,13 +527,13 @@ void detect_linux_misc(SECTION *section,
     }
     if (version) {
       print_line(level, "Minix file system (v%d, %d chars)",
-		 version, namesize);
+                 version, namesize);
       if (version == 1)
-	blocks = get_le_short(buf + 1024 + 2);
+        blocks = get_le_short(buf + 1024 + 2);
       else
-	blocks = get_le_long(buf + 1024 + 20);
+        blocks = get_le_long(buf + 1024 + 20);
       blocks = (blocks - get_le_short(buf + 1024 + 8))
-	<< get_le_short(buf + 1024 + 10);
+        << get_le_short(buf + 1024 + 10);
       format_blocky_size(s, blocks, 1024, "blocks", NULL);
       print_line(level + 1, "Volume size %s", s);
     }
@@ -506,18 +554,18 @@ void detect_linux_misc(SECTION *section,
       break;
     for (en = 0; en < 2; en++) {
       if (get_ve_long(en, buf + off) == 0x28cd3d45) {
-	print_line(level, "Linux cramfs, starts sector %d, %s",
-		   off >> 9, get_ve_name(en));
+        print_line(level, "Linux cramfs, starts sector %d, %s",
+                   off >> 9, get_ve_name(en));
 
-	get_string(buf + off + 48, 16, s);
-	print_line(level + 1, "Volume name \"%s\"", s);
+        get_string(buf + off + 48, 16, s);
+        print_line(level + 1, "Volume name \"%s\"", s);
 
-	size = get_ve_long(en, buf + off + 4);
-	blocks = get_ve_long(en, buf + off + 40);
-	format_size_verbose(s, size);
-	print_line(level + 1, "Compressed size %s", s);
-	format_blocky_size(s, blocks, 4096, "blocks", " -assumed-");
-	print_line(level + 1, "Data size %s", s);
+        size = get_ve_long(en, buf + off + 4);
+        blocks = get_ve_long(en, buf + off + 40);
+        format_size_verbose(s, size);
+        print_line(level + 1, "Compressed size %s", s);
+        format_blocky_size(s, blocks, 4096, "blocks", " -assumed-");
+        print_line(level + 1, "Data size %s", s);
       }
     }
   }
@@ -530,16 +578,16 @@ void detect_linux_misc(SECTION *section,
       major = get_ve_short(en, buf + 28);
       minor = get_ve_short(en, buf + 30);
       print_line(level, "Linux squashfs, version %d.%d, %s",
-		 major, minor, get_ve_name(en));
+                 major, minor, get_ve_name(en));
 
       if (major > 2)
-	size = get_ve_quad(en, buf + 63);
+        size = get_ve_quad(en, buf + 63);
       else
-	size = get_ve_long(en, buf + 8);
+        size = get_ve_long(en, buf + 8);
       if (major > 1)
-	blocksize = get_ve_long(en, buf + 51);
+        blocksize = get_ve_long(en, buf + 51);
       else
-	blocksize = get_ve_short(en, buf + 32);
+        blocksize = get_ve_short(en, buf + 32);
 
       format_size_verbose(s, size);
       print_line(level + 1, "Compressed size %s", s);
@@ -569,7 +617,7 @@ void detect_linux_loader(SECTION *sectio
 
   /* boot sector stuff */
   if (executable && (memcmp(buf + 2, "LILO", 4) == 0 ||
-		     memcmp(buf + 6, "LILO", 4) == 0))
+                     memcmp(buf + 6, "LILO", 4) == 0))
     print_line(level, "LILO boot loader");
   if (executable && memcmp(buf + 3, "SYSLINUX", 8) == 0)
     print_line(level, "SYSLINUX boot loader");
@@ -578,25 +626,25 @@ void detect_linux_loader(SECTION *sectio
 
   /* we know GRUB a little better... */
   if (executable &&
-      find_memory(buf, 512, "Geom\0Hard Disk\0Read\0 Error\0", 27) >= 0) {
+      find_memory(buf, 512, "Geom\0Hard Disk\0Read\0 Error", 26) >= 0) {
     if (buf[0x3e] == 3) {
       print_line(level, "GRUB boot loader, compat version %d.%d, boot drive 0x%02x",
-		 (int)buf[0x3e], (int)buf[0x3f], (int)buf[0x40]);
+                 (int)buf[0x3e], (int)buf[0x3f], (int)buf[0x40]);
     } else if (executable && buf[0x1bc] == 2 && buf[0x1bd] <= 2) {
       id = buf[0x3e];
       if (id == 0x10) {
-	print_line(level, "GRUB boot loader, compat version %d.%d, normal version",
-		   (int)buf[0x1bc], (int)buf[0x1bd]);
+        print_line(level, "GRUB boot loader, compat version %d.%d, normal version",
+                   (int)buf[0x1bc], (int)buf[0x1bd]);
       } else if (id == 0x20) {
-	print_line(level, "GRUB boot loader, compat version %d.%d, LBA version",
-		   (int)buf[0x1bc], (int)buf[0x1bd]);
+        print_line(level, "GRUB boot loader, compat version %d.%d, LBA version",
+                   (int)buf[0x1bc], (int)buf[0x1bd]);
       } else {
-	print_line(level, "GRUB boot loader, compat version %d.%d",
-		   (int)buf[0x1bc], (int)buf[0x1bd]);
+        print_line(level, "GRUB boot loader, compat version %d.%d",
+                   (int)buf[0x1bc], (int)buf[0x1bd]);
       }
     } else {
       print_line(level, "GRUB boot loader, unknown compat version %d",
-		 buf[0x3e]);
+                 buf[0x3e]);
     }
   }
 
@@ -612,7 +660,7 @@ void detect_linux_loader(SECTION *sectio
     char *number = (char *)buf + 164;
     char *total = (char *)buf + 172;
     print_line(level, "Debian floppy split, name \"%s\", disk %s of %s",
-	       name, number, total);
+               name, number, total);
   }
 }
 
