summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristophe Grenier <grenier@cgsecurity.org>2018-06-28 22:15:01 +0200
committerChristophe Grenier <grenier@cgsecurity.org>2018-06-28 22:15:01 +0200
commitaad79e476614079aa300cc4abb838690e9ee163e (patch)
treeb930d7bf2259166288023bb4c98506e10094e84a
parent2a4497ef6b0259f077370d06a47a67505f32940e (diff)
Detect f2fs filesystem, a filesystem created in 2012 by Samsung Electronics Co., Ltd.
-rw-r--r--configure.ac2
-rw-r--r--src/Makefile.am4
-rw-r--r--src/analyse.c20
-rw-r--r--src/common.h1
-rw-r--r--src/f2fs.c101
-rw-r--r--src/f2fs.h25
-rw-r--r--src/f2fs_fs.h107
-rw-r--r--src/partnone.c6
8 files changed, 257 insertions, 9 deletions
diff --git a/configure.ac b/configure.ac
index ee216ec..de20ad9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ AC_INIT([testdisk],[7.1-WIP],[grenier@cgsecurity.org])
AC_LANG(C)
sinclude(acx_pthread.m4)
sinclude(mkdir.m4)
-TESTDISKDATE="April 2018"
+TESTDISKDATE="June 2018"
AC_SUBST(TESTDISKDATE)
AC_DEFINE_UNQUOTED([TESTDISKDATE],"$TESTDISKDATE",[Date of release])
AC_CONFIG_AUX_DIR(config)
diff --git a/src/Makefile.am b/src/Makefile.am
index 7baacb9..f4b5c7b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,8 +17,8 @@ EXTRA_PROGRAMS = photorecf
base_C = autoset.c common.c crc.c ewf.c fnctdsk.c hdaccess.c hdcache.c hdwin32.c hidden.c hpa_dco.c intrf.c iso.c list_sort.c log.c log_part.c misc.c msdos.c parti386.c partgpt.c parthumax.c partmac.c partsun.c partnone.c partxbox.c io_redir.c ntfs_io.c ntfs_utl.c partauto.c sudo.c unicode.c win32.c
base_H = alignio.h autoset.h common.h crc.h ewf.h fnctdsk.h hdaccess.h hdwin32.h hidden.h guid_cmp.h guid_cpy.h hdcache.h hpa_dco.h intrf.h iso.h iso9660.h lang.h list.h list_sort.h log.h log_part.h misc.h types.h io_redir.h msdos.h ntfs_utl.h parti386.h partgpt.h parthumax.h partmac.h partsun.h partxbox.h partauto.h sudo.h unicode.h win32.h
-fs_C = analyse.c bfs.c bsd.c btrfs.c cramfs.c exfat.c fat.c fat_common.c fatx.c ext2.c ext2_common.c jfs.c gfs2.c hfs.c hfsp.c hpfs.c luks.c lvm.c md.c netware.c ntfs.c refs.c rfs.c savehdr.c sun.c swap.c sysv.c ufs.c vmfs.c wbfs.c xfs.c zfs.c
-fs_H = analyse.h bfs.h bsd.h btrfs.h cramfs.h exfat.h fat.h fat_common.h fatx.h ext2.h ext2_common.h jfs_superblock.h jfs.h gfs2.h hfs.h hfsp.h hpfs.h luks.h lvm.h md.h netware.h ntfs.h refs.h rfs.h savehdr.h sun.h swap.h sysv.h ufs.h vmfs.h wbfs.h xfs.h zfs.h
+fs_C = analyse.c bfs.c bsd.c btrfs.c cramfs.c exfat.c ext2.c ext2_common.c fat.c fat_common.c fatx.c f2fs.c jfs.c gfs2.c hfs.c hfsp.c hpfs.c luks.c lvm.c md.c netware.c ntfs.c refs.c rfs.c savehdr.c sun.c swap.c sysv.c ufs.c vmfs.c wbfs.c xfs.c zfs.c
+fs_H = analyse.h bfs.h bsd.h btrfs.h cramfs.h exfat.h ext2.h ext2_common.h fat.h fat_common.h fatx.h f2fs.h f2fs_fs.h jfs_superblock.h jfs.h gfs2.h hfs.h hfsp.h hpfs.h luks.h lvm.h md.h netware.h ntfs.h refs.h rfs.h savehdr.h sun.h swap.h sysv.h ufs.h vmfs.h wbfs.h xfs.h zfs.h
testdisk_ncurses_C = addpart.c addpartn.c adv.c askloc.c chgarch.c chgarchn.c chgtype.c chgtypen.c dimage.c dirn.c dirpart.c diskacc.c diskcapa.c edit.c ext2_sb.c ext2_sbn.c fat1x.c fat32.c fat_adv.c fat_cluster.c fatn.c geometry.c geometryn.c godmode.c hiddenn.c intrface.c intrfn.c nodisk.c ntfs_adv.c ntfs_fix.c ntfs_udl.c parti386n.c partgptn.c partmacn.c partsunn.c partxboxn.c tanalyse.c tbanner.c tdelete.c tdiskop.c tdisksel.c testdisk.c texfat.c thfs.c tload.c tlog.c tmbrcode.c tntfs.c toptions.c tpartwr.c
testdisk_ncurses_H = addpart.h addpartn.h adv.h askloc.h chgarch.h chgarchn.h chgtype.h chgtypen.h dimage.h dirn.h dirpart.h diskacc.h diskcapa.h edit.h ext2_sb.h ext2_sbn.h fat1x.h fat32.h fat_adv.h fat_cluster.h fatn.h geometry.h geometryn.h godmode.h hiddenn.h intrface.h intrfn.h nodisk.h ntfs_fix.h ntfs_udl.h partgptn.h parti386n.h partmacn.h partsunn.h partxboxn.h tanalyse.h tdelete.h tdiskop.h tdisksel.h texfat.h thfs.h tload.h tlog.h tmbrcode.h tntfs.h toptions.h tpartwr.h
diff --git a/src/analyse.c b/src/analyse.c
index 3e9d356..b121986 100644
--- a/src/analyse.c
+++ b/src/analyse.c
@@ -38,6 +38,8 @@
#include "ext2.h"
#include "fat.h"
#include "fatx.h"
+#include "f2fs_fs.h"
+#include "f2fs.h"
#include "gfs2.h"
#include "hfs.h"
#include "hfsp.h"
@@ -262,9 +264,11 @@ int search_type_2(const unsigned char *buffer, disk_t *disk, partition_t *partit
const hfs_mdb_t *hfs_mdb=(const hfs_mdb_t *)(buffer+0x400);
const struct hfsp_vh *vh=(const struct hfsp_vh *)(buffer+0x400);
const struct ext2_super_block *sb=(const struct ext2_super_block*)(buffer+0x400);
+ const struct f2fs_super_block *sb_f2fs=(const struct f2fs_super_block *)(buffer+0x400);
// assert(sizeof(struct ext2_super_block)<=1024);
// assert(sizeof(hfs_mdb_t)<=1024);
// assert(sizeof(struct hfsp_vh)<=1024);
+// assert(sizeof(struct f2fs_super_block)==3072);
if(verbose>2)
{
log_trace("search_type_2 lba=%lu\n",
@@ -279,6 +283,9 @@ int search_type_2(const unsigned char *buffer, disk_t *disk, partition_t *partit
if((be16(vh->version)==4 || be16(vh->version)==5) &&
recover_HFSP(disk, vh, partition, verbose, dump_ind, 0)==0)
return 1;
+ if(sb_f2fs->magic == le32(F2FS_SUPER_MAGIC) &&
+ recover_f2fs(disk, sb_f2fs, partition)==0)
+ return 1;
return 0;
}
@@ -357,13 +364,13 @@ int search_type_128(unsigned char *buffer, disk_t *disk, partition_t *partition,
}
if(disk->pread(disk, buffer, 11 * DEFAULT_SECTOR_SIZE, partition->part_offset + 126 * 512) != 11 * DEFAULT_SECTOR_SIZE)
return -1;
- buffer=buffer+0x400;
{
- const struct reiserfs_super_block *rfs=(const struct reiserfs_super_block *)buffer;
- const struct reiser4_master_sb *rfs4=(const struct reiser4_master_sb *)buffer;
- const struct ufs_super_block *ufs=(const struct ufs_super_block *)buffer;
- const struct btrfs_super_block *btrfs=(const struct btrfs_super_block*)buffer;
- const struct gfs2_sb *gfs2=(const struct gfs2_sb *)buffer;
+ const unsigned char *buffer_1024=buffer+0x400;
+ const struct reiserfs_super_block *rfs=(const struct reiserfs_super_block *)buffer_1024;
+ const struct reiser4_master_sb *rfs4=(const struct reiser4_master_sb *)buffer_1024;
+ const struct ufs_super_block *ufs=(const struct ufs_super_block *)buffer_1024;
+ const struct btrfs_super_block *btrfs=(const struct btrfs_super_block*)buffer_1024;
+ const struct gfs2_sb *gfs2=(const struct gfs2_sb *)buffer_1024;
/* 64k offset */
/* Test ReiserFS */
if((memcmp(rfs->s_magic,"ReIs",4) == 0 ||
@@ -412,6 +419,7 @@ int check_linux(disk_t *disk, partition_t *partition, const int verbose)
check_xfs(disk, partition, verbose)==0 ||
check_LUKS(disk, partition)==0 ||
check_btrfs(disk, partition)==0 ||
+ check_f2fs(disk, partition)==0 ||
check_gfs2(disk, partition)==0 ||
check_ZFS(disk, partition)==0)
return 0;
diff --git a/src/common.h b/src/common.h
index a10107e..c031222 100644
--- a/src/common.h
+++ b/src/common.h
@@ -239,6 +239,7 @@ enum upart_type {
UP_FAT32,
UP_FATX,
UP_FREEBSD,
+ UP_F2FS,
UP_GFS2,
UP_HFS,
UP_HFSP,
diff --git a/src/f2fs.c b/src/f2fs.c
new file mode 100644
index 0000000..f4c7822
--- /dev/null
+++ b/src/f2fs.c
@@ -0,0 +1,101 @@
+/*
+
+ File: f2fs.c
+
+ Copyright (C) 2018 Christophe GRENIER <grenier@cgsecurity.org>
+
+ This software is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write the Free Software Foundation, Inc., 51
+ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#include <stdio.h>
+#include "types.h"
+#include "common.h"
+#include "f2fs_fs.h"
+#include "f2fs.h"
+#include "log.h"
+
+extern const arch_fnct_t arch_none;
+
+static void set_f2fs_info(partition_t *partition, const struct f2fs_super_block*hdr)
+{
+ partition->upart_type=UP_F2FS;
+ partition->blocksize=1<<le32(hdr->log_blocksize);
+ partition->fsname[0]='\0';
+ if(partition->sb_offset==0)
+ snprintf(partition->info, sizeof(partition->info), "F2FS, blocksize=%u", partition->blocksize);
+ else
+ snprintf(partition->info, sizeof(partition->info), "F2FS found using backup sector, blocksize=%u", partition->blocksize);
+}
+
+int check_f2fs(disk_t *disk, partition_t *partition)
+{
+ unsigned char *buffer=(unsigned char*)MALLOC(F2FS_BLKSIZE);
+ if(disk->pread(disk, buffer, F2FS_BLKSIZE, partition->part_offset + F2FS_SUPER_OFFSET) != F2FS_BLKSIZE)
+ {
+ free(buffer);
+ return 1;
+ }
+ if(test_f2fs((struct f2fs_super_block*)buffer)!=0)
+ {
+ free(buffer);
+ return 1;
+ }
+ set_f2fs_info(partition, (struct f2fs_super_block*)buffer);
+ free(buffer);
+ return 0;
+}
+
+int test_f2fs(const struct f2fs_super_block *hdr)
+{
+ if(le32(hdr->magic) != F2FS_SUPER_MAGIC)
+ return 1;
+ /* Currently, support 512/1024/2048/4096 bytes sector size */
+ if(le32(hdr->log_sectorsize) < 9 || le32(hdr->log_sectorsize) > 12)
+ return 1;
+ /* Currently, support only 4KB block size */
+ if(le32(hdr->log_blocksize) != F2FS_BLKSIZE_BITS)
+ return 1;
+ if(le32(hdr->log_sectorsize) + le32(hdr->log_sectors_per_block) != le32(hdr->log_blocksize))
+ return 1;
+ /* check log blocks per segment */
+ if(le32(hdr->log_blocks_per_seg) != 9)
+ return 1;
+ if(le64(hdr->block_count) == 0)
+ return 1;
+ return 0;
+}
+
+int recover_f2fs(const disk_t *disk, const struct f2fs_super_block *hdr, partition_t *partition)
+{
+ if(test_f2fs(hdr)!=0)
+ return 1;
+ partition->sborg_offset=0;
+ partition->sb_size=F2FS_BLKSIZE;
+ partition->part_type_i386=P_LINUX;
+ partition->part_type_gpt=GPT_ENT_TYPE_MS_BASIC_DATA;
+ partition->part_size=(uint64_t)le64(hdr->block_count) << le32(hdr->log_blocksize);
+ set_f2fs_info(partition, hdr);
+ return 0;
+}
diff --git a/src/f2fs.h b/src/f2fs.h
new file mode 100644
index 0000000..e1d7086
--- /dev/null
+++ b/src/f2fs.h
@@ -0,0 +1,25 @@
+/*
+
+ File: f2fs.h
+
+ Copyright (C) 2018 Christophe GRENIER <grenier@cgsecurity.org>
+
+ This software is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write the Free Software Foundation, Inc., 51
+ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ */
+
+int check_f2fs(disk_t *disk, partition_t *partition);
+int test_f2fs(const struct f2fs_super_block *hdr);
+int recover_f2fs(const disk_t *disk, const struct f2fs_super_block *hdr, partition_t *partition);
diff --git a/src/f2fs_fs.h b/src/f2fs_fs.h
new file mode 100644
index 0000000..07d0a89
--- /dev/null
+++ b/src/f2fs_fs.h
@@ -0,0 +1,107 @@
+/*
+ File: f2fs_fs.h
+
+ Copyright (C) 2018 Christophe GRENIER <grenier@cgsecurity.org>
+
+ Superblock information from Samsung Electronics Co., Ltd. http://www.samsung.com/
+
+ This software is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write the Free Software Foundation, Inc., 51
+ Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+#ifndef F2FS_FS_H
+#define F2FS_FS_H
+
+#define F2FS_SUPER_MAGIC 0xF2F52010 /* F2FS Magic Number */
+
+#define F2FS_SUPER_OFFSET 1024 /* byte-size offset */
+#define F2FS_MIN_LOG_SECTOR_SIZE 9 /* 9 bits for 512 bytes */
+#define F2FS_MAX_LOG_SECTOR_SIZE 12 /* 12 bits for 4096 bytes */
+#define F2FS_LOG_SECTORS_PER_BLOCK 3 /* log number for sector/blk */
+#define F2FS_BLKSIZE 4096 /* support only 4KB block */
+#define F2FS_BLKSIZE_BITS 12 /* bits for F2FS_BLKSIZE */
+#define F2FS_MAX_EXTENSION 64 /* # of extension entries */
+#define F2FS_EXTENSION_LEN 8 /* max size of extension */
+#define F2FS_BLK_ALIGN(x) (((x) + F2FS_BLKSIZE - 1) >> F2FS_BLKSIZE_BITS)
+
+/*
+ * For further optimization on multi-head logs, on-disk layout supports maximum
+ * 16 logs by default. The number, 16, is expected to cover all the cases
+ * enoughly. The implementaion currently uses no more than 6 logs.
+ * Half the logs are used for nodes, and the other half are used for data.
+ */
+#define MAX_ACTIVE_LOGS 16
+#define MAX_ACTIVE_NODE_LOGS 8
+#define MAX_ACTIVE_DATA_LOGS 8
+
+#define VERSION_LEN 256
+#define MAX_VOLUME_NAME 512
+#define MAX_PATH_LEN 64
+#define MAX_DEVICES 8
+
+#define F2FS_MAX_QUOTAS 3
+
+/*
+ * For superblock
+ */
+struct f2fs_device {
+ uint8_t path[MAX_PATH_LEN];
+ uint32_t total_segments;
+} __attribute__ ((gcc_struct, __packed__));
+
+struct f2fs_super_block {
+ uint32_t magic; /* Magic Number */
+ uint16_t major_ver; /* Major Version */
+ uint16_t minor_ver; /* Minor Version */
+ uint32_t log_sectorsize; /* log2 sector size in bytes */
+ uint32_t log_sectors_per_block; /* log2 # of sectors per block */
+ uint32_t log_blocksize; /* log2 block size in bytes */
+ uint32_t log_blocks_per_seg; /* log2 # of blocks per segment */
+ uint32_t segs_per_sec; /* # of segments per section */
+ uint32_t secs_per_zone; /* # of sections per zone */
+ uint32_t checksum_offset; /* checksum offset inside super block */
+ uint64_t block_count; /* total # of user blocks */
+ uint32_t section_count; /* total # of sections */
+ uint32_t segment_count; /* total # of segments */
+ uint32_t segment_count_ckpt; /* # of segments for checkpoint */
+ uint32_t segment_count_sit; /* # of segments for SIT */
+ uint32_t segment_count_nat; /* # of segments for NAT */
+ uint32_t segment_count_ssa; /* # of segments for SSA */
+ uint32_t segment_count_main; /* # of segments for main area */
+ uint32_t segment0_blkaddr; /* start block address of segment 0 */
+ uint32_t cp_blkaddr; /* start block address of checkpoint */
+ uint32_t sit_blkaddr; /* start block address of SIT */
+ uint32_t nat_blkaddr; /* start block address of NAT */
+ uint32_t ssa_blkaddr; /* start block address of SSA */
+ uint32_t main_blkaddr; /* start block address of main area */
+ uint32_t root_ino; /* root inode number */
+ uint32_t node_ino; /* node inode number */
+ uint32_t meta_ino; /* meta inode number */
+ uint8_t uuid[16]; /* 128-bit uuid for volume */
+ uint16_t volume_name[MAX_VOLUME_NAME]; /* volume name */
+ uint32_t extension_count; /* # of extensions below */
+ uint8_t extension_list[F2FS_MAX_EXTENSION][F2FS_EXTENSION_LEN];/* extension array */
+ uint32_t cp_payload;
+ uint8_t version[VERSION_LEN]; /* the kernel version */
+ uint8_t init_version[VERSION_LEN]; /* the initial kernel version */
+ uint32_t feature; /* defined features */
+ uint8_t encryption_level; /* versioning level for encryption */
+ uint8_t encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
+ struct f2fs_device devs[MAX_DEVICES]; /* device list */
+ uint32_t qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */
+ uint8_t hot_ext_count; /* # of hot file extension */
+ uint8_t reserved[314]; /* valid reserved region */
+} __attribute__ ((gcc_struct, __packed__));
+
+#endif /* F2FS_FS_H */
diff --git a/src/partnone.c b/src/partnone.c
index ff20954..a2e25d9 100644
--- a/src/partnone.c
+++ b/src/partnone.c
@@ -48,6 +48,8 @@
#include "fat.h"
#include "fat_common.h"
#include "fatx.h"
+#include "f2fs_fs.h"
+#include "f2fs.h"
#include "iso9660.h"
#include "iso.h"
#include "gfs2.h"
@@ -99,6 +101,7 @@ static const struct systypes none_sys_types[] = {
{UP_FAT16, "FAT16"},
{UP_FAT32, "FAT32"},
{UP_FREEBSD, "FreeBSD"},
+ {UP_F2FS, "f2fs"},
{UP_GFS2, "GFS2"},
{UP_HFS, "HFS"},
{UP_HFSP, "HFS+"},
@@ -370,6 +373,9 @@ static int check_part_none(disk_t *disk_car,const int verbose,partition_t *parti
case UP_FREEBSD:
ret=check_BSD(disk_car,partition,verbose,BSD_MAXPARTITIONS);
break;
+ case UP_F2FS:
+ ret=check_f2fs(disk_car, partition);
+ break;
case UP_GFS2:
ret=check_gfs2(disk_car, partition);
break;