/* * 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 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 * added Native Language Support * * 2000-12-01 Sepp Wijnands * added probes for cramfs, hfs, hpfs and adfs. * * 2001-10-26 Tim Launchbury * added sysv magic. * * aeb - many changes. * */ #include #include #include #include #include #include #include #include "linux_fs.h" #include "mount_guess_fstype.h" #include "sundries.h" /* for xstrdup */ #include "nls.h" #define ETC_FILESYSTEMS "/etc/filesystems" #define PROC_FILESYSTEMS "/proc/filesystems" #define SIZE(a) (sizeof(a)/sizeof(a[0])) /* 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 ; made into a function for mount(8) by Mike Grupenhoff . Corrected the test for xiafs - aeb Read the superblock only once - aeb Added a very weak heuristic for vfat - aeb Added 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 Added cramfs, hfs, hpfs, adfs - Sepp Wijnands Added ext3 - Andrew Morton Added jfs - Christoph Hellwig Added sysv - Tim Launchbury Added udf - Bryce Nesbitt */ static char *magic_known[] = { "adfs", "bfs", "cramfs", "ext", "ext2", "ext3", "hfs", "hpfs", "iso9660", "jfs", "minix", "ntfs", "qnx4", "reiserfs", "romfs", "swap", "sysv", "udf", "ufs", "vxfs", "xfs", "xiafs" }; static struct tried { struct tried *next; char *type; } *tried = NULL; static int was_tested(const char *fstype) { char **m; struct tried *t; for (m = magic_known; m - magic_known < SIZE(magic_known); m++) if (!strcmp(*m, fstype)) return 1; for (t = tried; t; t = t->next) { if (!strcmp(t->type, fstype)) return 1; } return 0; } static void set_tested(const char *fstype) { struct tried *t = xmalloc(sizeof(struct tried)); t->next = tried; t->type = xstrdup(fstype); tried = t; } static void free_tested(void) { struct tried *t, *tt; t = tried; while(t) { free(t->type); tt = t->next; free(t); t = tt; } tried = NULL; } /* * 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" }; static int may_be_udf(const char *id) { char **m; for (m = udf_magic; m - udf_magic < SIZE(udf_magic); m++) if (!strncmp(*m, id, 5)) return 1; return 0; } /* 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 0; 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 0; } /* 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 1; if (!may_be_udf(isosb.id)) return 0; } return 0; } static int may_be_swap(const char *s) { return (strncmp(s-10, "SWAP-SPACE", 10) == 0 || strncmp(s-10, "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]); } static int is_reiserfs_magic_string (struct reiserfs_super_block * rs) { return (!strncmp (rs->s_magic, REISERFS_SUPER_MAGIC_STRING, strlen ( REISERFS_SUPER_MAGIC_STRING)) || !strncmp (rs->s_magic, REISER2FS_SUPER_MAGIC_STRING, strlen ( REISER2FS_SUPER_MAGIC_STRING))); } char * do_guess_fstype(const char *device) { int fd; char *type = NULL; 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 */ 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; } xsb; struct ufs_super_block ufssb; union { struct iso_volume_descriptor iso; struct hs_volume_descriptor hs; } isosb; 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 stat statbuf; /* 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 0; fd = open(device, O_RDONLY); if (fd < 0) return 0; /* 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)) goto try_iso9660; /* Gyorgy Kovesdi: none of my photocds has a readable block 0 */ if (xiafsmagic(xsb.xiasb) == _XIAFS_SUPER_MAGIC) type = "xiafs"; else if(!strncmp(xsb.romfs_magic, "-rom1fs-", 8)) type = "romfs"; else if(!strncmp(xsb.xfsb.s_magic, XFS_SUPER_MAGIC, 4)) type = "xfs"; else if(!strncmp(xsb.qnx4fs_magic+4, "QNX4FS", 6)) type = "qnx4"; else if(xsb.bfs_magic == 0x1badface) type = "bfs"; else if(!strncmp(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 ((!strncmp(xsb.fatsb.s_os, "MSDOS", 5) || !strncmp(xsb.fatsb.s_os, "MSWIN", 5) || !strncmp(xsb.fatsb.s_os, "MTOOL", 5) || !strncmp(xsb.fatsb.s_os, "IBM", 3) || !strncmp(xsb.fatsb.s_os, "DRDOS", 5) || !strncmp(xsb.fatsb.s_os, "mkdosfs", 7) || !strncmp(xsb.fatsb.s_os, "kmkdosfs", 8) || /* Michal Svec: created by fdformat, old msdos utility for formatting large (1.7) floppy disks. */ !strncmp(xsb.fatsb.s_os, "CH-FOR18", 8)) && (!strncmp(xsb.fatsb.s_fs, "FAT12 ", 8) || !strncmp(xsb.fatsb.s_fs, "FAT16 ", 8) || !strncmp(xsb.fatsb.s_fs2, "FAT32 ", 8))) 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)) goto io_error; if (sysvmagic(svsb) == SYSV_SUPER_MAGIC ) type = "sysv"; } if (!type) { /* block 1 */ if (lseek(fd, 1024, SEEK_SET) != 1024 || read(fd, (char *) &sb, sizeof(sb)) != sizeof(sb)) goto io_error; /* 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" */ } 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 (vxfsmagic(sb.vs) == VXFS_SUPER_MAGIC) type = "vxfs"; } if (!type) { /* block 1 */ if (lseek(fd, 0x400, SEEK_SET) != 0x400 || read(fd, (char *) &hfssb, sizeof(hfssb)) != sizeof(hfssb)) goto io_error; /* 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)) goto io_error; /* 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)) goto io_error; 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)) goto io_error; if (is_reiserfs_magic_string(&reiserfssb)) type = "reiserfs"; } if (!type) { /* block 8 */ if (lseek(fd, 0x2000, SEEK_SET) != 0x2000 || read(fd, (char *) &hpfssb, sizeof(hpfssb)) != sizeof(hpfssb)) goto io_error; if (hpfsmagic(hpfssb) == 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)) goto io_error; if (!strncmp(jfssb.s_magic, JFS_MAGIC, 4)) type = "jfs"; } if (!type) { /* block 32 */ try_iso9660: if (lseek(fd, 0x8000, SEEK_SET) != 0x8000 || read(fd, (char *) &isosb, sizeof(isosb)) != sizeof(isosb)) goto io_error; if (strncmp(isosb.hs.id, HS_STANDARD_ID, sizeof(isosb.hs.id)) == 0) { /* "CDROM" */ type = "iso9660"; } else if (strncmp(isosb.iso.id, ISO_STANDARD_ID, sizeof(isosb.iso.id)) == 0) { /* CD001 */ type = "iso9660"; if (is_really_udf(fd)) type = "udf"; } else if (may_be_udf(isosb.iso.id)) type = "udf"; } 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)) goto io_error; if (is_reiserfs_magic_string(&reiserfssb)) type = "reiserfs"; } if (!type) { /* perhaps the user tries to mount the swap space on a new disk; warn her before she does mke2fs on it */ int pagesize = getpagesize(); int rd; char buf[32768]; rd = pagesize; if (rd < 8192) rd = 8192; if (rd > sizeof(buf)) rd = sizeof(buf); if (lseek(fd, 0, SEEK_SET) != 0 || read(fd, buf, rd) != rd) goto io_error; if (may_be_swap(buf+pagesize) || may_be_swap(buf+4096) || may_be_swap(buf+8192)) type = "swap"; } close (fd); return(type); io_error: if (errno) perror(device); else fprintf(stderr, _("mount: error while guessing filesystem type\n")); close(fd); return 0; } char * guess_fstype(const char *spec) { char *type = do_guess_fstype(spec); if (verbose) { printf (_("mount: you didn't specify a filesystem type for %s\n"), spec); if (!type) printf (_(" I will try all types mentioned in %s or %s\n"), ETC_FILESYSTEMS, PROC_FILESYSTEMS); else if (!strcmp(type, "swap")) printf (_(" and it looks like this is swapspace\n")); else printf (_(" I will try type %s\n"), type); } return type; } static char * procfsnext(FILE *procfs) { char line[100]; char fsname[100]; while (fgets(line, sizeof(line), procfs)) { if (sscanf (line, "nodev %[^\n]\n", fsname) == 1) continue; if (sscanf (line, " %[^ \n]\n", fsname) != 1) continue; return strdup(fsname); } return 0; } /* Only use /proc/filesystems here, this is meant to test what the kernel knows about, so /etc/filesystems is irrelevant. Return: 1: yes, 0: no, -1: cannot open procfs */ int is_in_procfs(const char *type) { FILE *procfs; char *fsname; int ret = -1; procfs = fopen(PROC_FILESYSTEMS, "r"); if (procfs) { ret = 0; while ((fsname = procfsnext(procfs)) != NULL) if (!strcmp(fsname, type)) { ret = 1; break; } fclose(procfs); procfs = NULL; } return ret; } /* Try all types in FILESYSTEMS, except those in *types, in case *types starts with "no" */ /* return: 0: OK, -1: error in errno, 1: type not found */ /* when 0 or -1 is returned, *types contains the type used */ /* when 1 is returned, *types is NULL */ int procfsloop(int (*mount_fn)(struct mountargs *), struct mountargs *args, char **types) { char *files[2] = { ETC_FILESYSTEMS, PROC_FILESYSTEMS }; FILE *procfs; char *fsname; char *notypes = NULL; int no = 0; int ret = 1; int errsv = 0; int i; if (*types && !strncmp(*types, "no", 2)) { no = 1; notypes = (*types) + 2; } *types = NULL; /* Use PROC_FILESYSTEMS only when ETC_FILESYSTEMS does not exist. In some cases trying a filesystem that the kernel knows about on the wrong data will crash the kernel; in such cases ETC_FILESYSTEMS can be used to list the filesystems that we are allowed to try, and in the order they should be tried. End ETC_FILESYSTEMS with a line containing a single '*' only, if PROC_FILESYSTEMS should be tried afterwards. */ for (i=0; i<2; i++) { procfs = fopen(files[i], "r"); if (!procfs) continue; while ((fsname = procfsnext(procfs)) != NULL) { if (!strcmp(fsname, "*")) { fclose(procfs); goto nexti; } if (was_tested (fsname)) continue; if (no && matching_type(fsname, notypes)) continue; set_tested (fsname); args->type = fsname; if (verbose) { printf(_("Trying %s\n"), fsname); fflush(stdout); } if ((*mount_fn) (args) == 0) { *types = fsname; ret = 0; break; } else if (errno != EINVAL && is_in_procfs(fsname) == 1) { *types = "guess"; ret = -1; errsv = errno; break; } } free_tested(); fclose(procfs); errno = errsv; return ret; nexti:; } return 1; }