/**

Ok, this is a hack of the 'mount' program from bin-utils, just extracted the file system guessing function and made it into a seperate proggie, witha few dozen tweaks.

original commets from thsi file below :)

**/


/*
 * Thu Jul 14 07:32:40 1994: faith@cs.unc.edu added changes from Adam
 * J. Richter (adam@adam.yggdrasil.com) so that /proc/filesystems is used
 * if no -t option is given.  I modified his patches so that, if
 * /proc/filesystems is not available, the behavior of mount is the same as
 * it was previously.
 *
 * Wed Feb 8 09:23:18 1995: Mike Grupenhoff <kashmir@umiacs.UMD.EDU> added
 * a probe of the superblock for the type before /proc/filesystems is
 * checked.
 *
 * Fri Apr  5 01:13:33 1996: quinlan@bucknell.edu, fixed up iso9660 autodetect
 *
 * Wed Nov  11 11:33:55 1998: K.Garloff@ping.de, try /etc/filesystems before
 * /proc/filesystems
 * [This was mainly in order to specify vfat before fat; these days we often
 *  detect *fat and then assume vfat, so perhaps /etc/filesystems isnt
 *  so useful anymore.]
 *
 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@pld.ORG.PL>
 * added Native Language Support
 *
 * 2000-12-01 Sepp Wijnands <mrrazz@garbage-coderz.net>
 * added probes for cramfs, hfs, hpfs and adfs.
 *
 * 2001-10-26 Tim Launchbury
 * added sysv magic.
 *
 * aeb - many changes.
 *

* 2005 Jesse ripped it appart somewhat and put it back together... well just the guess_fs part

 */

typedef unsigned int uint; /*BK*/

#include <stdio.h>
#include <stdlib.h>
//#include <stderr.h>
#include <error.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "linux_fs.h"
#include "guess_fstype.h"
//#include "nls.h"

//#define ETC_FILESYSTEMS		"/etc/filesystems"
//#define PROC_FILESYSTEMS	"/proc/filesystems"

#ifndef FALSE
#	define FALSE 0
#	define TRUE !FALSE
#endif
#ifndef NULL
#	define NULL 0
#endif

#include "dosfsck.h"


#ifdef __le16_to_cpu
/* ++roman: 2.1 kernel headers define these function, they're probably more
 * efficient then coding the swaps machine-independently. */
#define CF_LE_W	__le16_to_cpu
#define CF_LE_L	__le32_to_cpu
#define CT_LE_W	__cpu_to_le16
#define CT_LE_L	__cpu_to_le32
#else
#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff))
#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \
               (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24))
#define CT_LE_W(v) CF_LE_W(v)
#define CT_LE_L(v) CF_LE_L(v)
#endif /* defined(__le16_to_cpu) */
 
#ifdef __alpha
/* Unaligned fields must first be copied byte-wise */
#define GET_UNALIGNED_W(f)			\
    ({						\
	unsigned short __v;			\
	memcpy( &__v, &f, sizeof(__v) );	\
	CF_LE_W( *(unsigned short *)&f );	\
    })
#else
#define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f )
#endif

// from /usr/include/linux/msdos_fs.h
#define MSDOS_FAT12 4084 /* maximum number of clusters in a 12 bit FAT */
#define SECTOR_SIZE     512             /* sector size (bytes) */


int fs_read(int fd, loff_t pos, int size, void *data){
	if (lseek(fd,pos,0) != pos) 
		return FALSE;
	if (size != read(fd, data, size)) 
		return FALSE;
	return TRUE;
}

int check_backup_boot(int fd, DOS_FS *fs, struct boot_sector *b){
	struct boot_sector b2;
	if (!fs->backupboot_start) {
		if (CF_LE_W(b->reserved) < 3) {
			return TRUE; // must be ok still.
		}
	}
	fs_read(fd, fs->backupboot_start, sizeof(b2), &b2);
	if (memcmp(b, &b2, sizeof(b2)) != 0) {
		/* there are many differences */
		__u8 *p, *q;
		__u32 i;
		for( p = (__u8 *)b, q = (__u8 *)&b2, i = 0; i < sizeof(b2); ++p, ++q, ++i )
			if (*p != *q)
				return TRUE;
		return FALSE;
	}
	return TRUE;
}

int read_fsinfo(int fd, DOS_FS *fs, struct boot_sector *b,int lss){
	struct info_sector i;
	__u32 magic, sig, boot;
	if (!b->info_sector)
		return TRUE; // its ok to not have one.
	fs->fsinfo_start = CF_LE_W(b->info_sector)*lss;
	fs_read(fd, fs->fsinfo_start,sizeof(i),&i);
	
	magic = 0x41615252;
	sig = 0x61417272;
	boot = 0xaa55;
	
	if (i.magic != CT_LE_L(magic) || i.signature != CT_LE_L(sig) || i.boot_sign != CT_LE_W(boot))
		return FALSE; // invalid magic number
	if (fs->fsinfo_start)
		fs->free_clusters = CF_LE_L(i.free_clusters);
	return TRUE;
}


int read_boot(DOS_FS *fs, int fd){
	struct boot_sector b;
	unsigned fat_length;
	unsigned short logical_sector_size, sectors, total_sectors;
	loff_t data_size;
	
	fs_read(fd,0,sizeof(b),&b);
	logical_sector_size = GET_UNALIGNED_W(b.sector_size);
	if (!logical_sector_size) return FALSE;
	fs->cluster_size = b.cluster_size*logical_sector_size;
	if (!fs->cluster_size) return FALSE;
	if (b.fats != 2)
		return FALSE;
	sectors = GET_UNALIGNED_W(b.sectors);
	total_sectors = sectors;
	if (total_sectors ==	0) total_sectors = CF_LE_L(b.total_sect);
	fat_length = CF_LE_W(b.fat_length);
	if (fat_length == 0) fat_length = CF_LE_L(b.fat32_length);
	fs->fat_start = (loff_t)CF_LE_W(b.reserved)*logical_sector_size;
	fs->root_start = ((loff_t)CF_LE_W(b.reserved)+b.fats*fat_length)*logical_sector_size;
	fs->root_entries = GET_UNALIGNED_W(b.dir_entries);
	data_size = (loff_t)total_sectors*(loff_t)fs->cluster_size- (loff_t)fs->root_start;
	fs->clusters = data_size/fs->cluster_size;
	fs->root_cluster = 0; /* indicates standard, pre-FAT32 root dir */
	fs->fsinfo_start = 0; /* no FSINFO structure */
	fs->free_clusters = -1; /* unknown */
	
	//if (!read_fsinfo(fd,fs,&b,logical_sector_size))
//		return FALSE;
	if (!b.fat_length && b.fat32_length) {
		fs->fat_bits = 32;
		fs->root_cluster = CF_LE_L(b.root_cluster);
		if (!fs->root_cluster && !fs->root_entries)
			return FALSE;
		fs->backupboot_start = CF_LE_W(b.backup_boot)*logical_sector_size;
		if (!check_backup_boot(fd,fs,&b))
			return FALSE;
	}else{
		fs->fat_bits = 16; /* assume 16 bit FAT for now */
		/* On real MS-DOS, a 16 bit FAT is used whenever there would be too
		* much clusers otherwise. */
		fs->fat_bits = (fs->clusters > MSDOS_FAT12) ? 16 : 12;
		/* On Atari, things are more difficult: GEMDOS always uses 12bit FATs
		* on floppies, and always 16 bit on harddisks. */
		/* If more clusters than fat entries in 16-bit fat, we assume
		* it's a real MSDOS FS with 12-bit fat. */
		if ((unsigned)(fs->clusters+2) > (unsigned)(fat_length*logical_sector_size*8/16) ||
		/* if it's a floppy disk --> 12bit fat */
		//device_no == 2 ||
		/* if it's a ramdisk or loopback device and has one of the usual
		* floppy sizes -> 12bit FAT  */
		//((device_no == 1 || device_no == 7) &&
		(total_sectors == 720 || total_sectors == 1440 ||
		total_sectors == 2880))
			fs->fat_bits = 12;
	}
	/* On FAT32, the high 4 bits of a FAT entry are reserved */
	fs->fat_size = fat_length*logical_sector_size;
	
	if (!fs->root_entries && !fs->root_cluster)
		return FALSE;
	if ((logical_sector_size & (SECTOR_SIZE-1)) == 0)
		return FALSE;
	if (!b.secs_track || !b.heads)
		return FALSE;
	return TRUE;
}




int fatcheck(int fd){
	DOS_FS fs;
	if (!read_boot(&fs, fd))
		return FALSE;
	return TRUE;
}

/* Most file system types can be recognized by a `magic' number
   in the superblock.  Note that the order of the tests is
   significant: by coincidence a filesystem can have the
   magic numbers for several file system types simultaneously.
   For example, the romfs magic lives in the 1st sector;
   xiafs does not touch the 1st sector and has its magic in
   the 2nd sector; ext2 does not touch the first two sectors. */

static inline unsigned short swapped(unsigned short a) {
     return (a>>8) | (a<<8);
}

/*
    char *guess_fstype(const char *device);

    Probes the device and attempts to determine the type of filesystem
    contained within.

    Original routine by <jmorriso@bogomips.ww.ubc.ca>; made into a function
    for mount(8) by Mike Grupenhoff <kashmir@umiacs.umd.edu>.
    Corrected the test for xiafs - aeb
    Read the superblock only once - aeb
    Added a very weak heuristic for vfat - aeb
    Added efs, iso9660, minix-v2, romfs, qnx4, udf, vxfs, swap - aeb
    Added a test for high sierra (iso9660) - quinlan@bucknell.edu
    Added ufs from a patch by jj. But maybe there are several types of ufs?
    Added ntfs from a patch by Richard Russon.
    Added xfs - 2000-03-21 Martin K. Petersen <mkp@linuxcare.com>
    Added cramfs, hfs, hpfs, adfs - Sepp Wijnands <mrrazz@garbage-coderz.net>
    Added ext3 - Andrew Morton
    Added jfs - Christoph Hellwig
    Added sysv - Tim Launchbury
    Added udf - Bryce Nesbitt
    Added ocfs, ocfs2 - Manish Singh - http://oss.oracle.com/projects/ocfs2/
*/

/*
static char *magic_known[] = {
	"adfs", "bfs", "cramfs", "efs", "ext", "ext2", "ext3",
	"hfs", "hpfs", "iso9660", "jfs", "minix", "ntfs", "ocfs", "ocfs2",
	"qnx4", "reiserfs", "romfs", "swap", "sysv", "udf", "ufs",
	"vxfs", "xfs", "xiafs",
	NULL
};

static int known_fstype(const char *fstype) {
	int i;
	for (i=0;magic_known[i]!= NULL;i++)
		if (!strcmp(magic_known[i], fstype))
			return TRUE;
	return FALSE;
}*/

/*
 * udf magic - I find that trying to mount garbage as an udf fs
 * causes a very large kernel delay, almost killing the machine.
 * So, we do not try udf unless there is positive evidence that it
 * might work. Strings below taken from ECMA 167.
 */
/*
 * It seems that before udf 2.00 the volume descriptor was not well
 * defined.  For 2.00 you're supposed to keep scanning records until
 * you find one NOT in this list.  (See ECMA 2/8.3.1).
 */
static char *udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02", "NSR03", "TEA01", NULL };

static int may_be_udf(const char *id) {
	int i;
	for (i=0; udf_magic[i]!=NULL; i++)
		if (!strncmp(udf_magic[i], id, 5))
			return TRUE;
	return FALSE;
}

/* we saw "CD001" - may be iso9660 or udf - Bryce Nesbitt */
static int is_really_udf(int fd) {
	int j, bs;
	struct iso_volume_descriptor isosb;

	/* determine the block size by scanning in 2K increments
	   (block sizes larger than 2K will be null padded) */
	for (bs = 1; bs < 16; bs++) {
		lseek(fd, bs*2048+32768, SEEK_SET);
		if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb))
			return FALSE;
		if (isosb.id[0])
			break;
	}

	/* Scan up to another 64 blocks looking for additional VSD's */
	for (j = 1; j < 64; j++) {
		if (j > 1) {
			lseek(fd, j*bs*2048+32768, SEEK_SET);
			if (read(fd, (char *)&isosb, sizeof(isosb)) != sizeof(isosb))
				return FALSE;
		}
		/* If we find NSR0x then call it udf:
		   NSR01 for UDF 1.00
		   NSR02 for UDF 1.50
		   NSR03 for UDF 2.00 */
		if (!strncmp(isosb.id, "NSR0", 4))
			return TRUE;
		if (!may_be_udf(isosb.id))
			return FALSE;
	}
	return FALSE;
}

static int may_be_swap(const char *s) {
	return (strncmp(s, "SWAP-SPACE", 10) == 0 || strncmp(s, "SWAPSPACE2", 10) == 0);
}

/* rather weak necessary condition */
static int may_be_adfs(const u_char *s) {
	u_char *p;
	int sum;

	p = (u_char *) s + 511;
	sum = 0;
	while (--p != s)
		sum = (sum >> 8) + (sum & 0xff) + *p;
	return (sum == p[511]);
}

char * io_error(const char *device){
	if (errno)
		perror(device);
	else
		fprintf(stderr, "error while guessing filesystem type: %s\n", device);
	return NULL;
}

int is_it_iso9660(int fd, const char *device, int allow_alternatives, char **type){
	union {
		struct iso_volume_descriptor iso;
		struct hs_volume_descriptor hs;
	} isosb;
	if (lseek(fd, 0x8000, SEEK_SET) != 0x8000 || read(fd, (char *) &isosb, sizeof(isosb)) != sizeof(isosb)){
		(*type) =io_error(device);
		return FALSE;
	}
	if (strncmp(isosb.hs.id, HS_STANDARD_ID, sizeof(isosb.hs.id)) == 0) {
		/* "CDROM" */
		(*type) = "iso9660";
		return TRUE;
	} else if (strncmp(isosb.iso.id, ISO_STANDARD_ID, sizeof(isosb.iso.id)) == 0) {
		/* CD001 */
		(*type) = "iso9660";
		if (allow_alternatives && is_really_udf(fd))
			(*type) = "udf";
		return TRUE;
	} else if (allow_alternatives && may_be_udf(isosb.iso.id)){
		(*type) = "udf";
		return TRUE;
	}
	return FALSE;
}

int reiserfs_magic_version(const unsigned char *magic) {
	int rc = 0;

	if (!strncmp((char *)magic, REISERFS_SUPER_MAGIC_STRING,
		     strlen(REISERFS_SUPER_MAGIC_STRING)))
		rc = 1;
	if (!strncmp((char *)magic, REISER2FS_SUPER_MAGIC_STRING, 
		     strlen(REISER2FS_SUPER_MAGIC_STRING)))
		rc = 2;
	if (!strncmp((char *)magic, REISER3FS_SUPER_MAGIC_STRING, 
		     strlen(REISER3FS_SUPER_MAGIC_STRING)))
		rc = 3;
	return rc;
}


char * do_guess_fstype(int fd, const char *device) {
	char *type = NULL;
	union {
		struct xiafs_super_block xiasb;
		char romfs_magic[8];
		char qnx4fs_magic[10];	/* ignore first 4 bytes */
		long bfs_magic;
		struct ntfs_super_block ntfssb;
		struct fat_super_block fatsb;
		struct xfs_super_block xfsb;
		struct cramfs_super_block cramfssb;
		struct ocfs_volume_header ovh;
		struct efs_volume_header efsvh;
		struct efs_super efssb;
	} xsb;			/* stuff at 0 */
	union {
		struct minix_super_block ms;
		struct ext_super_block es;
		struct ext2_super_block e2s;
		struct vxfs_super_block vs;
	} sb;			/* stuff at 1024 */
	struct ufs_super_block ufssb;
	struct reiserfs_super_block reiserfssb;	/* block 64 or 8 */
	struct jfs_super_block jfssb;		/* block 32 */
	struct hfs_super_block hfssb;
	struct hpfs_super_block hpfssb;
	struct adfs_super_block adfssb;
	struct sysv_super_block svsb;
	struct ocfs2_super_block osb;



	/* do seeks and reads in disk order, otherwise a very short
	partition may cause a failure because of read error */

	if (!type) {
	/* block 0 */
		if (lseek(fd, 0, SEEK_SET) != 0 || read(fd, (char *) &xsb, sizeof(xsb)) != sizeof(xsb)){
	 /* Gyorgy Kovesdi: none of my photocds has a readable block 0 */
			if (is_it_iso9660(fd, device, FALSE, &type))
				return type;
			return NULL;
		}

//printf("xsb.fatsb.s_os = '%.8s'\n", xsb.fatsb.s_os);
//printf("xsb.fatsb.s_fs2 = '%.8s'\n", xsb.fatsb.s_fs2);
//printf("xsb.fatsb.s_fs = '%.8s'\n", xsb.fatsb.s_fs);
//printf("xsb.fatsb.s_label = '%.8s'\n", xsb.fatsb.s_label);
//printf("xsb.fatsb.s_label2 = '%.8s'\n", xsb.fatsb.s_label2);

//printf("int fatcheck(int fd) = '%s'\n", fatcheck(fd) ? "OK":"BAD");

		
		if (xiafsmagic(xsb.xiasb) == _XIAFS_SUPER_MAGIC)
			type = "xiafs";
		else if(!strncmp(xsb.romfs_magic, "-rom1fs-", 8))
			type = "romfs";
		else if(!strncmp((char *)xsb.xfsb.s_magic, XFS_SUPER_MAGIC, 4))
			type = "xfs";
		else if(!strncmp((char *)xsb.ovh.signature, OCFS_MAGIC, sizeof(OCFS_MAGIC)))
			type = "ocfs";
		else if(!strncmp(xsb.qnx4fs_magic+4, "QNX4FS", 6))
			type = "qnx4";
		else if(xsb.bfs_magic == 0x1badface)
			type = "bfs";
		else if(!strncmp((char *)xsb.ntfssb.s_magic, NTFS_SUPER_MAGIC, sizeof(xsb.ntfssb.s_magic)))
			type = "ntfs";
		else if(cramfsmagic(xsb.cramfssb) == CRAMFS_SUPER_MAGIC ||
			cramfsmagic(xsb.cramfssb) == CRAMFS_SUPER_MAGIC_BE)
				type = "cramfs";
		else if (assemble4be((unsigned char *)xsb.efsvh.vh_magic) == EFS_VHMAGIC)
			type = "efs";		/* EFS volume header */
						/* might check checksum here */
		else if (	assemble4be((unsigned char *)xsb.efssb.fs_magic) == EFS_SBMAGIC || 
					assemble4be((unsigned char *)xsb.efssb.fs_magic) == EFS_SBMAGIC2)
			type = "efs";		/* EFS partition */
		else if (((
				!strncmp((char *)xsb.fatsb.s_os, "XTF-BOOT", 8) ||
				!strncmp((char *)xsb.fatsb.s_os, "MSDOS", 5) ||
				!strncmp((char *)xsb.fatsb.s_os, "MSWIN", 5) ||
				!strncmp((char *)xsb.fatsb.s_os, "MTOOL", 5) ||
				!strncmp((char *)xsb.fatsb.s_os, "IBM", 3) ||
				!strncmp((char *)xsb.fatsb.s_os, "DRDOS", 5) ||
				!strncmp((char *)xsb.fatsb.s_os, "mkdosfs", 7) ||
				!strncmp((char *)xsb.fatsb.s_os, "kmkdosfs", 8) ||
		/* Michal Svec: created by fdformat, old msdos utility for formatting large (1.7) floppy disks. */
				!strncmp((char *)xsb.fatsb.s_os, "CH-FOR18", 8) ||
				!strncmp((char *)xsb.fatsb.s_os, "        ", 8) // this is valid too.
			)&&(
				!strncmp((char *)xsb.fatsb.s_fs, "IBM", 3) ||
				!strncmp((char *)xsb.fatsb.s_fs, "FAT12   ", 8) ||
				!strncmp((char *)xsb.fatsb.s_fs, "FAT16   ", 8) ||
				!strncmp((char *)xsb.fatsb.s_fs2, "FAT32   ", 8)
			)) || fatcheck(fd))
				type = "vfat";	/* only guessing - might as well be fat or umsdos */
	}

	if (!type) {
		/* sector 1 */
		if (lseek(fd, 512 , SEEK_SET) != 512 || read(fd, (char *) &svsb, sizeof(svsb)) != sizeof(svsb))
			return io_error(device);
		if ((unsigned)sysvmagic(svsb) == (unsigned)SYSV_SUPER_MAGIC)
			type = "sysv";
	}

	if (!type) {
		/* block 1 */
		if (lseek(fd, 1024, SEEK_SET) != 1024 || read(fd, (char *) &sb, sizeof(sb)) != sizeof(sb))
			return io_error(device);
	
		/* ext2 has magic in little-endian on disk, so "swapped" is
		superfluous; however, there have existed strange byteswapped
		PPC ext2 systems */
		if (ext2magic(sb.e2s) == EXT2_SUPER_MAGIC ||
			ext2magic(sb.e2s) == EXT2_PRE_02B_MAGIC ||
			ext2magic(sb.e2s) == swapped(EXT2_SUPER_MAGIC)) {
				type = "ext2";
	
			/* maybe even ext3? */
			if ((assemble4le(sb.e2s.s_feature_compat)
				& EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
				assemble4le(sb.e2s.s_journal_inum) != 0){
				type = "ext3";	/* "ext3,ext2" */
				

#define EXT3_FEATURE_INCOMPAT_EXTENTS		0x0040 /* actually ext4 */

//printf("The feature   compatible 32 bit flag is %08X\n", assemble4le(sb.e2s.s_feature_compat) );
//printf("The feature incompatible 32 bit flag is %08X (0x0040==ext4)\n", assemble4le(sb.e2s.s_feature_incompat) );
				/* Distinguish between ext4 and ext3 */
				if (assemble4le(sb.e2s.s_feature_incompat) &
					EXT3_FEATURE_INCOMPAT_EXTENTS)
						type = "ext4";	/* "ext3 / ext4 test" */
			}				
				
		}else if (minixmagic(sb.ms) == MINIX_SUPER_MAGIC ||
			minixmagic(sb.ms) == MINIX_SUPER_MAGIC2 ||
			minixmagic(sb.ms) == swapped(MINIX_SUPER_MAGIC2) ||
			minixmagic(sb.ms) == MINIX2_SUPER_MAGIC ||
			minixmagic(sb.ms) == MINIX2_SUPER_MAGIC2)
				type = "minix";
		else if (extmagic(sb.es) == EXT_SUPER_MAGIC)
			type = "ext";
		else if ((unsigned)vxfsmagic(sb.vs) == (unsigned)VXFS_SUPER_MAGIC)
			type = "vxfs";
	}

	if (!type) {
		/* block 1 */
		if (lseek(fd, 0x400, SEEK_SET) != 0x400 || read(fd, (char *) &hfssb, sizeof(hfssb)) != sizeof(hfssb))
			return io_error(device);
		/* also check if block size is equal to 512 bytes, or a multiple. (I see 1536 here.) */
		if (hfsmagic(hfssb) == HFS_SUPER_MAGIC &&	/* always BE */
			hfsblksize(hfssb) != 0 && (hfsblksize(hfssb) & 0x1ff) == 0)
				type = "hfs";
	}

	if (!type) {
		/* block 3 */
		if (lseek(fd, 0xc00, SEEK_SET) != 0xc00 || read(fd, (char *) &adfssb, sizeof(adfssb)) != sizeof(adfssb))
			return io_error(device);
		/* only a weak test */
		if (may_be_adfs((u_char *) &adfssb) && (adfsblksize(adfssb) >= 8 && adfsblksize(adfssb) <= 10))
			type = "adfs";
	}

	if (!type) {
		int mag;
		/* block 8 */
		if (lseek(fd, 8192, SEEK_SET) != 8192 || read(fd, (char *) &ufssb, sizeof(ufssb)) != sizeof(ufssb))
			return io_error(device);
		mag = ufsmagic(ufssb);
		if (mag == UFS_SUPER_MAGIC_LE || mag == UFS_SUPER_MAGIC_BE)
			type = "ufs";
	}

	if (!type) {
		/* block 8 */
		if (lseek(fd, REISERFS_OLD_DISK_OFFSET_IN_BYTES, SEEK_SET) != REISERFS_OLD_DISK_OFFSET_IN_BYTES
		|| read(fd, (char *) &reiserfssb, sizeof(reiserfssb)) != sizeof(reiserfssb))
			return io_error(device);
		if (reiserfs_magic_version(reiserfssb.s_magic))
			type = "reiserfs";
	}

	if (!type) {
		/* block 8 */
		if (lseek(fd, 0x2000, SEEK_SET) != 0x2000 || read(fd, (char *) &hpfssb, sizeof(hpfssb)) != sizeof(hpfssb))
			return io_error(device);
		if ((unsigned)hpfsmagic(hpfssb) == (unsigned)HPFS_SUPER_MAGIC)
			type = "hpfs";
	}

	if (!type) {
		/* block 32 */
		if (lseek(fd, JFS_SUPER1_OFF, SEEK_SET) != JFS_SUPER1_OFF
		|| read(fd, (char *) &jfssb, sizeof(jfssb)) != sizeof(jfssb))
			return io_error(device);
		if (!strncmp(jfssb.s_magic, JFS_MAGIC, 4))
			type = "jfs";
	}

	if (!type) {
		/* block 32 */
		is_it_iso9660(fd, device, TRUE, &type);
	}

	if (!type) {
		/* block 64 */
		if (lseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET) != REISERFS_DISK_OFFSET_IN_BYTES
		|| read(fd, (char *) &reiserfssb, sizeof(reiserfssb)) != sizeof(reiserfssb))
			return io_error(device);
		if (reiserfs_magic_version(reiserfssb.s_magic))
			type = "reiserfs";
	}

	if (!type) {
		int blksize, blkoff;
		for (blksize = OCFS2_MIN_BLOCKSIZE; blksize <= OCFS2_MAX_BLOCKSIZE; blksize <<= 1) {
			blkoff = blksize * OCFS2_SUPER_BLOCK_BLKNO;
			if (lseek(fd, blkoff, SEEK_SET) != blkoff || read(fd, (char *) &osb, sizeof(osb)) != sizeof(osb))
				return io_error(device);
			if (strncmp((char *)osb.signature, OCFS2_SUPER_BLOCK_SIGNATURE, sizeof(OCFS2_SUPER_BLOCK_SIGNATURE)) == 0)
				type = "ocfs2";
		}
	}

	if (!type) {
		/* perhaps the user tries to mount the swap space on a new disk; warn her before she does mke2fs on it */
		int rd, pagesize;
		char buf[32768];
		
		rd = pagesize = getpagesize();
		if (rd < 8192)
			rd = 8192;
		if (rd > (signed)sizeof(buf))
			rd = (signed)sizeof(buf);
		if (lseek(fd, 0, SEEK_SET) != 0 || read(fd, buf, rd) != rd)
			return io_error(device);
		if (may_be_swap(buf+pagesize-10) || may_be_swap(buf+4096-10) || may_be_swap(buf+8192-10))
			type = "swap";
	}
	return type;
}


char * guess_fstype(const char *device){ //, int verbose) {
	int fd;
	struct stat statbuf;
	char *type = NULL;

	/* opening and reading an arbitrary unknown path can have
	undesired side effects - first check that `device' refers
	to a block device or ordinary file */
	if (stat (device, &statbuf) || !(S_ISBLK(statbuf.st_mode) || S_ISREG(statbuf.st_mode)))
		return NULL;

	fd = open(device, O_RDONLY);
	if (fd < 0)
		return NULL;
	type = do_guess_fstype(fd, device);
	close(fd);

/*	if (verbose) {
		if (!type)
		printf ("I will try all types mentioned in %s or %s\n", ETC_FILESYSTEMS, PROC_FILESYSTEMS);
		if (type)
		printf ("Looks like type %s\n", type);
	}*/
	return type;
}


