diff options
author | JinWang An <jinwang.an@samsung.com> | 2022-12-26 13:14:07 +0900 |
---|---|---|
committer | JinWang An <jinwang.an@samsung.com> | 2022-12-26 13:14:07 +0900 |
commit | 9a7966555cb8146da9a47e2270b3e45bda06ea1c (patch) | |
tree | 4722e160acc621183e9a3ad4601a1a6d45518b36 | |
parent | f2f0e690db008447a230628762679108d1d9e72b (diff) | |
download | dosfstools-9a7966555cb8146da9a47e2270b3e45bda06ea1c.tar.gz dosfstools-9a7966555cb8146da9a47e2270b3e45bda06ea1c.tar.bz2 dosfstools-9a7966555cb8146da9a47e2270b3e45bda06ea1c.zip |
Imported Upstream version 4.0upstream/4.0
-rw-r--r-- | .gitignore | 27 | ||||
-rw-r--r-- | ChangeLog | 765 | ||||
-rw-r--r-- | Makefile | 170 | ||||
-rw-r--r-- | Makefile.am | 10 | ||||
-rw-r--r-- | NEWS | 76 | ||||
-rw-r--r-- | README | 39 | ||||
-rw-r--r-- | README.md | 42 | ||||
-rw-r--r-- | VERSION | 1 | ||||
-rw-r--r-- | configure.ac | 67 | ||||
-rw-r--r-- | manpages/Makefile | 50 | ||||
-rw-r--r-- | manpages/Makefile.am | 38 | ||||
-rwxr-xr-x | manpages/bin/update-version.sh | 55 | ||||
-rw-r--r-- | manpages/fatlabel.8.in (renamed from manpages/en/fatlabel.8) | 2 | ||||
-rw-r--r-- | manpages/fsck.fat.8.in (renamed from manpages/en/fsck.fat.8) | 5 | ||||
-rw-r--r-- | manpages/mkfs.fat.8.in (renamed from manpages/en/mkfs.fat.8) | 5 | ||||
-rw-r--r-- | src/Makefile.am | 63 | ||||
-rw-r--r-- | src/blkdev/README | 3 | ||||
-rw-r--r-- | src/blkdev/blkdev.c | 376 | ||||
-rw-r--r-- | src/blkdev/blkdev.h | 149 | ||||
-rw-r--r-- | src/blkdev/linux_version.c | 25 | ||||
-rw-r--r-- | src/blkdev/linux_version.h | 14 | ||||
-rw-r--r-- | src/boot.c | 77 | ||||
-rw-r--r-- | src/boot.h | 2 | ||||
-rw-r--r-- | src/check.c | 82 | ||||
-rw-r--r-- | src/check.h | 2 | ||||
-rw-r--r-- | src/device_info.c | 332 | ||||
-rw-r--r-- | src/device_info.h | 56 | ||||
-rw-r--r-- | src/endian_compat.h | 29 | ||||
-rw-r--r-- | src/fat.c | 56 | ||||
-rw-r--r-- | src/fat.h | 2 | ||||
-rw-r--r-- | src/fatlabel.c | 5 | ||||
-rw-r--r-- | src/fsck.fat.c | 3 | ||||
-rw-r--r-- | src/fsck.fat.h | 24 | ||||
-rw-r--r-- | src/io.c | 70 | ||||
-rw-r--r-- | src/io.h | 16 | ||||
-rw-r--r-- | src/lfn.c | 23 | ||||
-rw-r--r-- | src/lfn.h | 6 | ||||
-rw-r--r-- | src/mkfs.fat.c | 540 | ||||
-rw-r--r-- | src/msdos_fs.h | 2 | ||||
-rw-r--r-- | src/testdevinfo.c | 125 | ||||
-rw-r--r-- | src/version.h.in (renamed from src/version.h) | 6 |
41 files changed, 2650 insertions, 790 deletions
@@ -1,11 +1,32 @@ *.o -/fatlabel -/fsck.fat -/mkfs.fat +src/fatlabel +src/fsck.fat +src/mkfs.fat +src/testdevinfo tags +TAGS *~ .*.sw[a-p] *.orig *.rej *.DS_Store + +aclocal.m4 +autom4te.cache +config.log +config.status +configure +compile +depcomp +install-sh +missing +.deps +.dirstamp + +Makefile +Makefile.in +src/version.h +manpages/fatlabel.8 +manpages/mkfs.fat.8 +manpages/fsck.fat.8 @@ -1,3 +1,768 @@ +commit a79ff90 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri May 6 02:34:42 2016 +0200 + + src/Makefile.am: Fix CPPFLAGS for VPATH builds + + The -I flag to add the blkdev subdir to the include search path is now + relative to $(srcdir) to allow VPATH builds to work. Additionally move + the -I flag from the mkfs_fat and testdevinfo CFLAGS to CPPFLAGS where + it actually belongs. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit e8eff14 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Apr 27 21:38:37 2016 +0200 + + read_boot(): Handle excessive FAT size specifications + + The variable used for storing the FAT size (in bytes) was an unsigned + int. Since the size in sectors read from the BPB was not sufficiently + checked, this could end up being zero after multiplying it with the + sector size while some offsets still stayed excessive. Ultimately it + would cause segfaults when accessing FAT entries for which no memory + was allocated. + + Make it more robust by changing the types used to store FAT size to + off_t and abort if there is no room for data clusters. Additionally + check that FAT size is not specified as zero. + + Fixes #25 and fixes #26. + + Reported-by: Hanno Böck + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 016800e +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Apr 27 14:16:53 2016 +0200 + + Use variable total_fat_entries in read_boot() for readability + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit a6478d8 +Author: Álvaro Fernández Rojas <noltari@gmail.com> +Date: Fri Apr 8 12:20:46 2016 +0200 + + Add missing iconv library for OS X + + Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit ce67dc6 +Author: Álvaro Fernández Rojas <noltari@gmail.com> +Date: Fri Apr 8 12:20:27 2016 +0200 + + Add endian support for OS X + + Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 08f3869 +Author: Joel Holdsworth <joel.holdsworth@vcatechnology.com> +Date: Thu Mar 10 00:53:07 2016 +0000 + + Configure option to disable building with libudev + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit ed4e47b +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Feb 22 03:47:14 2016 +0100 + + Remove use of PATH_MAX in path_name() + + The length of a file path on the checked filesystem has no relation to + the maximum path length of the system fsck is running on. So replace it + with a constant of our own. + + As a bonus this will not fail compilation on a system without PATH_MAX. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit b1a38ab +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 17 21:04:35 2016 +0100 + + Add preliminary entry for release 4.0 to NEWS + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 4ad3e9e +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 17 21:02:06 2016 +0100 + + Adjust ridiculous source indentation in io.c + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit b96acb2 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 17 20:51:53 2016 +0100 + + Document ./configure --enable-compat-symlinks in README + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit de39c5c +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 17 15:16:27 2016 +0100 + + Add include paths.h in the HAVE_DECL_GETMNTENT case + + _PATH_MOUNTED is now used for getmntent() in place of MOUNTED because + the latter was marked as a deprecated alias in glibc's mntent.h. The + mntent.h of musl libc does not include the _PATH_MOUNTED however. Fix + this by including paths.h alongside mntent.h + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 86c7acd +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 17 15:06:56 2016 +0100 + + man fsck: Document the -c option + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit ef9a73c +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Feb 15 02:10:57 2016 +0100 + + Add NEWS file with changes of the last two releases + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit bda6551 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Feb 12 03:56:16 2016 +0100 + + Make filesystem mounted check portable + + A new function is_device_mounted() in device_info.c is now used by + check_mount() in mkfs.fat.c. It contains the getmntent() using code + used before in check_mount() and now an alternative using getmntinfo() + as found on the BSDs. + + In case neither function is available, is_device_mounted() defaults to + reporting that the device isn't mounted. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 4b8c9cc +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Feb 12 01:57:33 2016 +0100 + + Make use of endian.h portable to BSD + + The endian.h found on Linux and the BSDs appear to be compatible, but + they are found in different locations. Add tests in configure.ac and a + new endian_compat.h file that has the logic to include the correct + files. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit d7665f2 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 10 21:30:06 2016 +0100 + + Fix format string in check_file() (%lu → %llu) + + The cluster chain length printing needs a 64 bit calculation, so we can + just use unsigned long long instead of uint64_t and use the format + string %llu. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 6225e59 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Feb 5 14:39:00 2016 +0100 + + blkdev.c: Prevent unused parameter warnings in fallback code + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 34cdded +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Feb 5 14:36:14 2016 +0100 + + blkdev_get_size(): Remove unused variable ch + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 5571d29 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 10 03:22:19 2016 +0100 + + Reinstate alignment of FAT32 structures to cluster size + + This reverts commits 17c956cb9 and d63e0d627 where the alignment was + removed because it created problems with a device that refused to read + the aligned filesystem. The option -a is already provided to disable + alignment in order to handle such cases. + + This change brings it back in line with FAT12/16 where alignment wasn't + disabled and brings consistency with the current command line options, + where only the option to disable alignment exists but no opposite option + to enable it. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 2c71ace +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 3 03:38:33 2016 +0100 + + Makefile.am: Add historic documentation to distribution + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit bcbae63 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 3 02:38:24 2016 +0100 + + src/Makefile.am: Add forgotten msdos_fs.h to mkfs_fat_SOURCES + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 19d1a13 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 3 02:34:51 2016 +0100 + + Remove sys/ioctl.h and linux/fd.h include from io.c + + These weren't used anymore and the linux/fd.h include would + gratuitously cause compilation to fail on non-Linux environments. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit f691660 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Feb 3 02:31:00 2016 +0100 + + Reinstate some #include <sys/types.h> + + These shouldn't have been removed in commit 245d0cce5. Put them back for + correctness even though the definitions were pulled in implicitly. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 51afd41 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Jan 27 21:38:29 2016 +0100 + + src/Makefile.am: Put all header files in appropriate _SOURCES variables + + With the headers missing the dist targets of the automake generated + Makefiles would not include them and make the resulting dist + unbuildable. + + Also combine sources collections into common variables for + deduplication. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit bf6f142 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Jan 27 15:28:42 2016 +0100 + + mkfs man: Note that sector sizes > 4096 are non-standard + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit ea96c32 +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Jan 25 21:30:23 2016 +0100 + + mkfs: Improve parsing of bad blocks file + + The bad blocks file that can be given to mkfs via the -l option had a + very simplistic design. It failed to notice it was parsing an empty + line and would report errors for that. + + Replace it with a more robust version that ignores empty lines as well + as leading and trailing white space. Additionally it produces meaningful + error messages. + + GitHub: Fixes #17 + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit dfb5bea +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Jan 25 02:47:12 2016 +0100 + + mkfs: Limit filesystem size on targets that are too large + + For FAT filesystems, the number of sectors has to fit into a 32 bit + variable. Previously this was not checked possibly causing invalid + filesystems to be generated. + + Now there is a check for that case which will limit the number of + sectors if needed and print a warning that disk space will be left + unused in that case. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit fc0343f +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Jan 22 21:29:20 2016 +0100 + + mkfs: Improved bounds checking in mark_FAT_sector()/mark_FAT_cluster() + + In mark_FAT_sector() the sector number itself is now checked against + limits instead of the computed cluster number. Even with sector number + before the start of the data area, the cluster number may be valid for + the first cluster due to dividing by the cluster size. + + Both functions now check for upper limits and should prevent writing + past the valid end of the FAT. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 9211c8a +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Jan 15 02:17:16 2016 +0100 + + mkfs: Fix offset error in FAT12/16 bad cluster marking + + The root directory wasn't factored in to the calculation of the data + area start sector. On FAT32 the root directory is in the data area, but + for FAT12 and FAT16 it is a reserved space before the start of the data + area. + + On FAT12 and FAT16, this resulted in the wrong clusters being marked + during bad blocks mapping, whether from check_blocks() or from reading + the user supplied bad blocks file. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 0627a62 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Jan 15 01:59:59 2016 +0100 + + mkfs: Fix off-by-2 error in bad cluster marking + + mark_FAT_sector(), which has the mark_sector_bad() macro as its sole + user, computed the cluster number corresponding to the sector by taking + its offset from the first data sector and dividing by sectors per + cluster. + + What it missed was that the first data cluster is number 2 and not 0. + This meant all marks were off by 2 and when the first two clusters are + supposed to be marked, it would overwrite the reserved cluster values + and create an invalid filesystem. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 2dca9aa +Author: Andreas Bombe <aeb@debian.org> +Date: Thu Jan 14 14:43:00 2016 +0100 + + .gitignore: Add .dirstamp + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 6a966fb +Author: Andreas Bombe <aeb@debian.org> +Date: Thu Jan 14 14:38:53 2016 +0100 + + mkfs: Reword non-standard sector size warning + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit bebc9ac +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Dec 30 15:10:35 2015 +0100 + + Clean up includes in mkfs.fat.c + + Moving the device probing out into device_info.c removed the need for a + number of includes in mkfs.fat.c. Remove them and add a define for + BLOCK_SIZE, which was the only thing used from linux/fs.h. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 12a1d46 +Author: Andreas Bombe <aeb@debian.org> +Date: Sun Nov 29 01:59:10 2015 +0100 + + Don't use pointer to first member when more of the struct gets copied + + Where a fs_write() of "first 13 bytes of directory entry" is intended, + actually use pointer to directory entry structure instead of the 11 + byte name field at the beginning. + + This does not change how the code works, it is just a clean up. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit d38bd2d +Author: Andreas Bombe <aeb@debian.org> +Date: Sun Nov 29 00:44:48 2015 +0100 + + Remove name/extension split in directory entry structures + + Both the DIR_ENT structure in fsck.fat.h and the msdos_dir_entry in + msdos_fs.h - these represent the on disk format of directory entries - + had the name field split into name[8] followed by ext[3]. + + By far the most operations on name are on the full name including + extension and they treated the name field as an 11 byte array. This is + an array overflow that worked because the structs have the attribute + packed and the extension field is following right after. + + Nevertheless, this is not clean C and the merging of both fields + actually simplified the code in a few places. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 0847e4c +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Nov 27 21:29:49 2015 +0100 + + Free allocated strings after use + + There are multiple calls to cnv_unicode() in lfn.c which returns an + allocated string. Most had the appropriate free() calls after printing + the strings. Add the missing two calls where memory was leaked. + + Found by Coverity. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 1b7d91e +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Nov 27 03:34:51 2015 +0100 + + Add test for and include linux/hdreg.h in blkdev.c + + Before, blkdev did not include it and depended on its own fallback + definition of the ioctl and struct hd_geometry. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 5024372 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Nov 27 03:28:14 2015 +0100 + + Add FDGETPRM attempt to blkdev_get_geometry() + + If HDIO_GETGEO isn't available or has failed, try FDGETPRM. This should + get the geometry from floppy drivers where HDIO_GETGEO isn't supported. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 4a146d7 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Nov 27 03:25:55 2015 +0100 + + Add blkdev_get_start() for getting partition start offset + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 254f8ab +Author: Andreas Bombe <aeb@debian.org> +Date: Thu Nov 26 23:33:02 2015 +0100 + + Remove use of libblkid again + + It appears libblkid is not as widely available as presumed, since some + platforms only have the original libblkid included in e2fsprogs which + lacks the needed functionality. This commit removes the requirement and + use of libblkid. + + As a replacement, blkdev.c from util-linux is included, which offers the + required basic functionality in a portable way. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit c9fb33c +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Oct 21 21:32:30 2015 +0200 + + Use just device size not major number in Atari mode + + In Atari mode, read_boot() in boot.c used the device major number to + determine whether to use FAT12. It would always use FAT12 for a floppy, + otherwise only if it is a RAM disk or loopback device and has a size + corresponding to standard floppy formats. + + Since this check was already broken for a long time (another place that + assumed 8 bit major numbers) and there is no real point to make the + distinction based on device, this commit reduces the check to just + compare against standard floppy sizes. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 64486ad +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Oct 21 00:18:22 2015 +0200 + + Remove loff_t and llseek() + + There appear to have been multiple conversions to 64 bit file offsets on + 32 bit architectures in dosfstools over the years, but today with the + proper setup off_t is 64 bits and simple lseek() can be used. The + AC_SYS_LARGEFILE macro in configure.ac does what is required to make + that happen. + + Given this, convert all uses of loff_t to off_t, remove llseek() + definitions and change llseek() calls to plain lseek(). + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit e03a5f4 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Oct 16 21:47:04 2015 +0200 + + Remove DJGPP support + + These macros have been added back when dosfstools was around version 2. + It is difficult to say whether these are still working correctly or + whether they are in use at all. FreeDOS appears to still show version + 2.11 of dosfstools in their software directory. + + Supporting actual MS-DOS or compatible may need more work in the + current state of things and this DJGPP support can be removed until + then. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 5b9a88d +Author: Andreas Bombe <aeb@debian.org> +Date: Thu Oct 8 16:17:22 2015 +0200 + + mkfs.fat: Complete overhaul of device probing + + The device probing in mkfs.fat is used to get device parameters where + needed and also to decide whether to refuse overwriting a device due to + possible user error. This code has suffered severe bitrot and is highly + Linux specific. Highlights include using hardcoded major/minor device + numbers to classify a device, and using 8 bits major/minor numbers that + have become obsolete a long time ago and thus often misidentifying a + device. + + The overhauled implementation is now in src/device_info.c and makes use + of libudev (optional, recommended) and libblkid (required) to probe the + device and where Linux ioctls are required it provides fallbacks and + does not attempt to call these on non-Linux systems. The FAT parameter + selection has been unified and simplified in the process. + + A new executable testdevinfo has been added that gets built but not + automatically installed. It takes one file name, uses the same probing + as mkfs.fat would with verbose messages enabled and displays the + results. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 53eddfc +Author: Andreas Bombe <aeb@debian.org> +Date: Sat Sep 12 02:54:33 2015 +0200 + + Die on out of range cluster values in set_fat()/get_fat() + + To prevent bugs caused by FAT corruption inside fsck to go unnoticed, + add a check against out of range requested cluster values in get_fat() + and against out of range cluster to change and new cluster value in + set_fat(). + + When an invalid cluster value is detected, these functions now die() + with an "internal error" message. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 456767b +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Sep 14 00:33:49 2015 +0200 + + configure.ac: Use AS_HELP_STRING to format option help + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 3cfb479 +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Sep 14 00:30:40 2015 +0200 + + version.h: Use @configure_input@ autoconf variable in boilerplate + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 41ef834 +Author: Andreas Bombe <aeb@debian.org> +Date: Mon Sep 14 00:29:27 2015 +0200 + + .gitignore: Add TAGS in addition to tags + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit bdc3d2a +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Sep 11 20:28:12 2015 +0200 + + Rename clusters field in DOS_FS struct + + Rename it to data_clusters to prevent mistaking the clusters field of + the DOS_FS struct as the total number of FAT entries instead of the + number of data clusters (two less than the number of entries). + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 0790812 +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Sep 11 19:47:29 2015 +0200 + + set_fat(): Fix off-by-2 error leading to corruption in FAT12 + + In FAT12 two 12 bit entries are combined to a 24 bit value (three + bytes). Therefore, when an even numbered FAT entry is set in FAT12, it + must be be combined with the following entry. To prevent accessing + beyond the end of the FAT array, it must be checked that the cluster is + not the last one. + + Previously, the check tested that the requested cluster was equal to + fs->clusters - 1. However, fs->clusters is the number of data clusters + not including the two reserved FAT entries at the start so the test + triggered two clusters early. + + If the third to last entry was written on a FAT12 filesystem with an + odd number of clusters, the second to last entry would be corrupted. + This corruption may also lead to invalid memory accesses when the + corrupted entry becomes out of bounds and is used later. + + Change the test to fs->clusters + 1 to fix. + + Reported-by: Hanno Böck + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 39ce90f +Author: Andreas Bombe <aeb@debian.org> +Date: Fri Sep 11 19:34:10 2015 +0200 + + set_fat(): Move FAT12 next cluster check up + + In FAT12 two 12 bit entries are combined to a 24 bit value (three + bytes). Therefore, when an even numbered FAT entry is set in FAT12, it + must be be combined with the following entry. To prevent accessing + beyond the end of the FAT array, it must be checked that the cluster is + not the last one. + + This check was broken in ff1b24e9 (first included in 3.0.3) as the + lookup was done unconditionally and the check influenced only using the + looked up value. + + Move the check up to fix. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 2aad1c8 +Author: Andreas Bombe <aeb@debian.org> +Date: Tue Sep 8 03:58:29 2015 +0200 + + Prevent out of bound array read in date_dos2unix() + + The function date_dos2unix() is called during fsck while showing + information about duplicate file names. In case the date field of a + directory entry contains the invalid value 0 for the month, + date_dos2unix would read index -1 of the day_n array. + + Add a check to prevent that and also make the day_n array const on this + occasion. + + Reported-by: Hanno Böck + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 3b95786 +Author: Yann E. MORIN <yann.morin.1998@free.fr> +Date: Sun Aug 16 15:55:43 2015 +0200 + + mkfs.fat: fix incorrect int type + + u_int32_t is not a stanard type, while uint32_t is. This fixes builds + with the musl C library, which only defines so-called "clean" headers; + build failures are like (back-quotes and elision manually added for + readability): + + http://autobuild.buildroot.org/results/a09/a0923d7f6d4dbae02eba4c5024bbdae3a52aa85a/build-end.log + + /home/peko/autobuild/instance-1/output/host/usr/bin/x86_64-linux-gcc -D_LARGEFILE_SOURCE \ + -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os -D_GNU_SOURCE -D_LARGEFILE_SOURCE \ + -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -c -o mkfs.fat.o src/mkfs.fat.c + src/mkfs.fat.c: In function 'main': + src/mkfs.fat.c:1415:18: error: 'u_int32_t' undeclared (first use in this function) + volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); [...] + ^ + src/mkfs.fat.c:1415:18: note: each undeclared identifier is reported only once for each + function it appears in + + Signed-off-by: "Yann E. MORIN" <yann.morin.1998@free.fr> + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 2b1c4d1 +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Jun 3 03:33:10 2015 +0200 + + Add README.md, remove Markdown formatting from README + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit b720acc +Author: Andreas Bombe <aeb@debian.org> +Date: Wed Jun 3 03:27:24 2015 +0200 + + Add simple README in Markdown format + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 2b255e6 +Author: Andreas Bombe <aeb@debian.org> +Date: Tue Jun 2 18:25:06 2015 +0200 + + Configure option for legacy names symlinks + + The symlinks from the old names (mkdosfs, dosfsck, etc.) are now only + created on "make install" when the --enable-compat-symlinks option was + given to configure. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 0643db7 +Author: Andreas Bombe <aeb@debian.org> +Date: Sun May 31 02:40:19 2015 +0200 + + Convert build system to autoconf/automake + + In preparation for fixing the horribly outdated and broken device + checking - which will likely involve using additional libraries like + libblkid - as well as making this package portable to other operating + systems, the build system is now the tried and true autoconf/automake + combination which should make both goals a little more straightforward. + + The release version number and date are now in configure.ac and + substituted by configure where they are needed. Now it is no longer + necessary to change the number in multiple places for a release and the + man pages get the number substituted directly into them, making the + update-version.sh script and the VERSION file obsolete. + + The English man pages are moved back up one directory to mark their + status as the master copy for all translations. At the moment the po4a + translation infrastructure is defunct since it isn't integrated into + the automake environment yet. So far it hasn't been used, so that is + not an actual regression. + + The date in the man pages is not automatically updated anymore. This is + as it should be, since the date is supposed to signify the time of the + last nontrivial change and not the release date of the software. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + +commit 85022fe (tag: v3.0.28) +Author: Andreas Bombe <aeb@debian.org> +Date: Sat May 16 02:56:17 2015 +0200 + + Releasing version 3.0.28. + + Signed-off-by: Andreas Bombe <aeb@debian.org> + commit ad1342e Author: Andreas Bombe <aeb@debian.org> Date: Sat May 16 02:10:18 2015 +0200 diff --git a/Makefile b/Makefile deleted file mode 100644 index 1593f3d..0000000 --- a/Makefile +++ /dev/null @@ -1,170 +0,0 @@ -# Makefile -# -# Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> -# -# This program 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 3 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, see <http://www.gnu.org/licenses/>. -# -# The complete text of the GNU General Public License -# can be found in /usr/share/common-licenses/GPL-3 file. - -SHELL := sh -e -LANGUAGES = $(shell cd manpages/po && ls) - -DESTDIR = -PREFIX = /usr/local -SBINDIR = $(PREFIX)/sbin -DOCDIR = $(PREFIX)/share/doc -MANDIR = $(PREFIX)/share/man - -#OPTFLAGS = -O2 -fomit-frame-pointer -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -OPTFLAGS = -O2 -fomit-frame-pointer -D_GNU_SOURCE $(shell getconf LFS_CFLAGS) -#WARNFLAGS = -Wall -pedantic -std=c99 -WARNFLAGS = -Wall -Wextra -Wno-sign-compare -Wno-missing-field-initializers -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings -DEBUGFLAGS = -g -CFLAGS += $(OPTFLAGS) $(WARNFLAGS) $(DEBUGFLAGS) - -VPATH = src - -all: build - -build: fatlabel fsck.fat mkfs.fat - -fatlabel: boot.o check.o common.o fat.o file.o io.o lfn.o charconv.o fatlabel.o - -fsck.fat: boot.o check.o common.o fat.o file.o io.o lfn.o charconv.o fsck.fat.o - -mkfs.fat: mkfs.fat.o - -rebuild: distclean build - -install: install-bin install-doc install-man install-symlinks - -install-bin: build - install -d -m 0755 $(DESTDIR)/$(SBINDIR) - install -m 0755 fatlabel fsck.fat mkfs.fat $(DESTDIR)/$(SBINDIR) - -install-doc: - install -d -m 0755 $(DESTDIR)/$(DOCDIR)/dosfstools - install -p -m 0644 ChangeLog doc/* $(DESTDIR)/$(DOCDIR)/dosfstools - -install-man: - for MANPAGE in manpages/en/*; \ - do \ - SECTION="8"; \ - mkdir -p $(DESTDIR)/$(MANDIR)/man$${SECTION}/; \ - install -m 0644 $${MANPAGE} $(DESTDIR)/$(MANDIR)/man$${SECTION}/$$(basename $${MANPAGE}); \ - done - - for LANGUAGE in $(LANGUAGES); \ - do \ - for MANPAGE in manpages/$${LANGUAGE}/*; \ - do \ - SECTION="8"; \ - mkdir -p $(DESTDIR)/$(MANDIR)/$${LANGUAGE}/man$${SECTION}/; \ - install -m 0644 $${MANPAGE} $(DESTDIR)/$(MANDIR)/$${LANGUAGE}/man$${SECTION}/$$(basename $${MANPAGE} .$${LANGUAGE}.$${SECTION}).$${SECTION}; \ - done; \ - done -install-symlinks: install-bin install-man - if [ -e $(DESTDIR)/$(SBINDIR)/fatlabel ]; \ - then \ - ln -sf fatlabel $(DESTDIR)/$(SBINDIR)/dosfslabel; \ - if [ -e $(DESTDIR)/$(MANDIR)/man8/fatlabel.8 ]; \ - then \ - ln -sf fatlabel.8 $(DESTDIR)/$(MANDIR)/man8/dosfslabel.8; \ - fi; \ - fi - - if [ -e $(DESTDIR)/$(SBINDIR)/fsck.fat ]; \ - then \ - ln -sf fsck.fat $(DESTDIR)/$(SBINDIR)/dosfsck; \ - ln -sf fsck.fat $(DESTDIR)/$(SBINDIR)/fsck.msdos; \ - ln -sf fsck.fat $(DESTDIR)/$(SBINDIR)/fsck.vfat; \ - if [ -e $(DESTDIR)/$(MANDIR)/man8/fsck.fat.8 ]; \ - then \ - ln -sf fsck.fat.8 $(DESTDIR)/$(MANDIR)/man8/dosfsck.8; \ - ln -sf fsck.fat.8 $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8; \ - ln -sf fsck.fat.8 $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8; \ - fi; \ - fi - - if [ -e $(DESTDIR)/$(SBINDIR)/mkfs.fat ]; \ - then \ - ln -sf mkfs.fat $(DESTDIR)/$(SBINDIR)/mkdosfs; \ - ln -sf mkfs.fat $(DESTDIR)/$(SBINDIR)/mkfs.msdos; \ - ln -sf mkfs.fat $(DESTDIR)/$(SBINDIR)/mkfs.vfat; \ - if [ -e $(DESTDIR)/$(MANDIR)/man8/mkfs.fat.8 ]; \ - then \ - ln -sf mkfs.fat.8 $(DESTDIR)/$(MANDIR)/man8/mkdosfs.8; \ - ln -sf mkfs.fat.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8; \ - ln -sf mkfs.fat.8 $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8; \ - fi; \ - fi - -uninstall: uninstall-symlinks uninstall-man uninstall-doc uninstall-bin - -uninstall-bin: - rm -f $(DESTDIR)/$(SBINDIR)/fatlabel - rm -f $(DESTDIR)/$(SBINDIR)/fsck.fat - rm -f $(DESTDIR)/$(SBINDIR)/mkfs.fat - - rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(SBINDIR) - -uninstall-doc: - rm -rf $(DESTDIR)/$(DOCDIR)/dosfstools - - rmdir --ignore-fail-on-non-empty $(DESTDIR)/$(DOCDIR) - -uninstall-man: - for MANPAGE in manpages/en/*; \ - do \ - SECTION="8"; \ - rm -f $(DESTDIR)/$(MANDIR)/man$${SECTION}/$$(basename $${MANPAGE} .en.$${SECTION}).$${SECTION}; \ - done - - for LANGUAGE in $(LANGUAGES); \ - do \ - for MANPAGE in manpages/$${LANGUAGE}/*; \ - do \ - SECTION="8"; \ - rm -f $(DESTDIR)/$(MANDIR)/$${LANGUAGE}/man$${SECTION}/$$(basename $${MANPAGE} .$${LANGUAGE}.$${SECTION}).$${SECTION}; \ - done; \ - done - -uninstall-symlinks: - rm -f $(DESTDIR)/$(SBINDIR)/dosfslabel - rm -f $(DESTDIR)/$(MANDIR)/man8/dosfslabel.8 - - rm -f $(DESTDIR)/$(SBINDIR)/dosfsck - rm -f $(DESTDIR)/$(MANDIR)/man8/dosfsck.8 - rm -f $(DESTDIR)/$(SBINDIR)/fsck.msdos - rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.msdos.8 - rm -f $(DESTDIR)/$(SBINDIR)/fsck.vfat - rm -f $(DESTDIR)/$(MANDIR)/man8/fsck.vfat.8 - - rm -f $(DESTDIR)/$(SBINDIR)/mkdosfs - rm -f $(DESTDIR)/$(MANDIR)/man8/mkdosfs.8 - rm -f $(DESTDIR)/$(SBINDIR)/mkfs.msdos - rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.msdos.8 - rm -f $(DESTDIR)/$(SBINDIR)/mkfs.vfat - rm -f $(DESTDIR)/$(MANDIR)/man8/mkfs.vfat.8 - -reinstall: distclean install - -clean: - rm -f *.o - -distclean: clean - rm -f fatlabel fsck.fat mkfs.fat - -.PHONY: build rebuild install install-bin install-doc install-man install-symlinks uninstall uninstall-bin uninstall-doc uninstall-man uninstall-symlinks reinstall clean distclean diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..e2ec024 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,10 @@ +SUBDIRS = src manpages + +dist_doc_DATA = doc/ANNOUNCE.mkdosfs \ + doc/ChangeLog.dosfsck \ + doc/ChangeLog.dosfstools-2.x \ + doc/ChangeLog.mkdosfs \ + doc/README.dosfsck \ + doc/README.dosfstools-2.x \ + doc/README.mkdosfs \ + doc/TODO.dosfstools-2.x @@ -0,0 +1,76 @@ +dosfstools 4.0 - released 2016-05-06 +==================================== + +The programs are now portable to non-Linux operating systems. To that end, the +build system has been converted to use autotools. There have been Linux +specifics in a lot of places which have been either eliminated or should have +equivalents so that it should now work in other Unix-like environments. It has +been tested on FreeBSD and OS X. + +As part of making it portable all the code that assumed 8 bit major/minor +numbers - and in fact masked out all other bits - has been cleaned up. Now +mkfs.vfat should not misidentify devices anymore and require the -I option to +override. The new device probing uses libudev (if available) to collect more +information. + +Fixed data corruption errors in fsck.fat: Writing to the third to last cluster +on FAT12 with an odd number of clusters would corrupt the following cluster. In +mkfs.fat, long existing bugs in bad cluster marking (from scanning or user +supplied bad blocks list) were fixed so that it actually marks the correct +clusters. + +The automatic alignment of data clusters that was added in 3.0.8 and broken for +FAT32 starting with 3.0.20 has been reinstated. If you need to create file +systems for finicky devices that have broken FAT implementations use the option +-a to disable alignment. + + +dosfstools 3.0.28 - released 2015-05-16 +======================================= + +The major user visible change in this release is that fsck.fat now defaults to +interactive repair mode which previously had to be selected with -r. The +previous default of a read only check mode was confusing to users who had to +repeat a potentially lengthy fsck.fat run with the right option in order to +actually fix their file system. It was also pointless – the interactive repair +mode already won't write anything without asking for confirmation. + +mkfs.fat now allows choosing 0xF0 as the media byte which was previously +rejected. + +mkfs.fat now supports the --invariant option to facilitate testing mkfs.fat +itself. It will reproducibly generate filesystems without random or time based +differences between them when all else is identical. + +Bugs fixed in fsck.fat are a read one byte beyond the end of an allocated array +when checking some FAT12 filesystems, and checking that the first cluster of a +file as specified in the directory entry is not 1. Previously it could attempt +to follow a block chain starting on cluster 1 and segfault when the conditions +are right. + + +dosfstools 3.0.27 - released 2014-11-12 +======================================= + +This is a pure bug fix release. The major bugs fixed: + +* fatlabel did not recognize long file names and mistook long file name + segments in the root directory for labels. This caused output of garbage when + asked to print the label and damage to the root directory (loss of long file + name after fsck) when used to set the label. + +* A fsck.fat check introduced in 3.0.26 triggered use of uninitialized fields + in the constructed root directory entry, which randomly caused the code + checking file names to consider the empty "file name" of the root directory + to be bad: + + $ /sbin/fsck.fat -y bad.img + fsck.fat 3.0.26 (2014-03-07) + / + Bad short file name (). + Auto-renaming it. + Renamed to + bad.img: 14 files, 19388/403266 clusters + +* And finally fsck.fat will not print the version string anymore every time the + -v option is encountered. @@ -0,0 +1,39 @@ +dosfstools consists of the programs mkfs.fat, fsck.fat and fatlabel to create, +check and label file systems of the FAT family. The dosfstools are licensed +under the GNU GPL version 3 or later. See the file COPYING for details. + + +### Build Requirements + +dosfstools recommends libudev. It is used in mkfs.fat to collect additional +information about the device to format in order to refuse potentially unsafe +operations without additional confirmation. + + +### Installing + +dosfstools are built using an autoconf/automake system, so the standard method +applies: + + ./configure + make + make install + +You need to have superuser privileges in order to install into the standard +system wide locations. + +The ./configure script has an option --enable-compat-symlinks that will +configure the build to symlink older names of the tools to the current ones on +installation. These are dosfsck, fsck.msdos and fsck.vfat for fsck.fat, mkdosfs, +mkfs.msdos and mkfs.vfat for mkfs.fat and dosfslabel for fatlabel. + + +### Building from the VCS repository + +If you are working directly from a git clone of the official dosfstools +repository, you will find that you can not run "./configure" straight away +because it, like other autogenerated files for the build system, is not included +in the repository. + +First, autoconf and automake have to be installed. Then you can run +"autoreconf -i" to generate all the required files. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f397895 --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +dosfstools consists of the programs mkfs.fat, fsck.fat and fatlabel to create, +check and label file systems of the FAT family. The dosfstools are licensed +under the GNU GPL version 3 or later. See the file `COPYING` for details. + + +### Build Requirements + +dosfstools recommends libudev. It is used in mkfs.fat to collect additional +information about the device to format in order to refuse potentially unsafe +operations without additional confirmation. + + +### Installing + +dosfstools are built using an autoconf/automake system, so the standard method +applies: + +``` +./configure +make +make install +``` + +You need to have superuser privileges in order to install into the standard +system wide locations. + +The `./configure` script has an option `--enable-compat-symlinks` that will +configure the build to symlink older names of the tools to the current ones on +installation. These are `dosfsck`, `fsck.msdos` and `fsck.vfat` for `fsck.fat`, +`mkdosfs`, `mkfs.msdos` and `mkfs.vfat` for `mkfs.fat` and `dosfslabel` for +`fatlabel`. + + +### Building from the VCS repository + +If you are working directly from a git clone of the official dosfstools +repository, you will find that you can not run `./configure` straight away +because it, like other autogenerated files for the build system, is not included +in the repository. + +First, autoconf and automake have to be installed. Then you can run `autoreconf +-i` to generate all the required files. diff --git a/VERSION b/VERSION deleted file mode 100644 index 0baec4d..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -3.0.28 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..4d4e522 --- /dev/null +++ b/configure.ac @@ -0,0 +1,67 @@ +# configure.ac for dosfstools +# Copyright (C) 2015 Andreas Bombe <aeb@debian.org> +# +# This program 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 3 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, see <http://www.gnu.org/licenses/>. + +AC_INIT([dosfstools], [4.0]) +AC_SUBST([RELEASE_DATE], [2016-05-06]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) + +AC_ARG_ENABLE([compat-symlinks], + [AS_HELP_STRING([--enable-compat-symlinks], + [install symlinks for legacy names of the tools])], + [case "${enableval}" in + yes) symlinks=true ;; + no) symlinks=false ;; + *) AC_MSG_ERROR([bad value ${enableval} for --enable-compat-symlinks]) ;; + esac], + [symlinks=false]) +AM_CONDITIONAL([COMPAT_SYMLINKS], [test x$symlinks = xtrue]) + + +AC_PROG_CC +AC_PROG_LN_S + +AC_SYS_LARGEFILE + +AC_CHECK_HEADERS([\ + err.h \ + linux/fd.h \ + linux/hdreg.h \ + linux/version.h \ + sys/disk.h \ + sys/disklabel.h \ + sys/ioccom.h \ + sys/mkdev.h \ + sys/queue.h \ + ]) + +AC_CHECK_HEADERS([endian.h sys/endian.h]) + +AC_CHECK_DECLS([getmntent], [], [], [[#include <mntent.h>]]) +AC_CHECK_DECLS([getmntinfo], [], [], [[#include <sys/mount.h>]]) + +AC_ARG_WITH([udev], AS_HELP_STRING([--without-udev], [build without libudev support])) +if test "x$with_udev" != "xno"; then + PKG_CHECK_MODULES([UDEV], [libudev], + [AC_DEFINE([HAVE_UDEV], [1])], + [true]) +fi + +AC_SEARCH_LIBS(iconv_open, iconv) + +AC_CONFIG_FILES([Makefile src/Makefile src/version.h + manpages/Makefile manpages/mkfs.fat.8 + manpages/fsck.fat.8 manpages/fatlabel.8]) +AC_OUTPUT diff --git a/manpages/Makefile b/manpages/Makefile deleted file mode 100644 index ee8a478..0000000 --- a/manpages/Makefile +++ /dev/null @@ -1,50 +0,0 @@ -# Makefile - -## dosfstools(7) -## Copyright (C) 2006-2014 Daniel Baumann <mail@daniel-baumann.ch> -## -## This program comes with ABSOLUTELY NO WARRANTY; for details see COPYING. -## This is free software, and you are welcome to redistribute it -## under certain conditions; see COPYING for details. - - -SHELL := sh -e - -LANGUAGES = $(shell cd po && ls) - -all: build - -po4a.cfg: - echo "[po4a_langs] $(LANGUAGES)" > po4a.cfg - echo "[po4a_paths] pot/\$$master.pot \$$lang:po/\$$lang/\$$master.po" >> po4a.cfg - - for MANPAGE in en/*; \ - do \ - SECTION="$$(basename $${MANPAGE} | sed -e 's|\.|\n|g' | tail -n1)"; \ - echo "[type: man] $${MANPAGE} \$$lang:\$$lang/$$(basename $${MANPAGE} .$${SECTION}).\$$lang.$${SECTION}" >> po4a.cfg; \ - done - -update: - ./bin/update-version.sh - -build: po4a.cfg - @if [ ! -x "$$(which po4a 2>/dev/null)" ]; \ - then \ - echo "E: po4a - command not found"; \ - echo "I: po4a can be obtained from:"; \ - echo "I: http://po4a.alioth.debian.org/"; \ - echo "I: On Debian based systems, po4a can be installed with:"; \ - echo "I: apt-get install po4a"; \ - exit 1; \ - fi - - po4a --keep 0 --no-backups -o untranslated=MT,ME \ - --package-name dosfstools po4a.cfg - -clean: - rm -rf $(LANGUAGES) - -distclean: clean - rm -f po4a.cfg - -rebuild: distclean update build diff --git a/manpages/Makefile.am b/manpages/Makefile.am new file mode 100644 index 0000000..5473a7f --- /dev/null +++ b/manpages/Makefile.am @@ -0,0 +1,38 @@ +# dosfstools manpages/Makefile.am +# Copyright (C) 2015 Andreas Bombe <aeb@debian.org> +# +# This program 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 3 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, see <http://www.gnu.org/licenses/>. + +man_MANS = fsck.fat.8 mkfs.fat.8 fatlabel.8 + + +if COMPAT_SYMLINKS +install-data-hook: + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f fatlabel.8 dosfslabel.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f fsck.fat.8 dosfsck.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f fsck.fat.8 fsck.msdos.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f fsck.fat.8 fsck.vfat.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f mkfs.fat.8 mkdosfs.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f mkfs.fat.8 mkfs.msdos.8 + cd $(DESTDIR)$(mandir)/man8 && $(LN_S) -f mkfs.fat.8 mkfs.vfat.8 + +uninstall-hook: + $(RM) $(DESTDIR)$(mandir)/man8/dosfslabel.8 + $(RM) $(DESTDIR)$(mandir)/man8/dosfsck.8 + $(RM) $(DESTDIR)$(mandir)/man8/fsck.msdos.8 + $(RM) $(DESTDIR)$(mandir)/man8/fsck.vfat.8 + $(RM) $(DESTDIR)$(mandir)/man8/mkdosfs.8 + $(RM) $(DESTDIR)$(mandir)/man8/mkfs.msdos.8 + $(RM) $(DESTDIR)$(mandir)/man8/mkfs.vfat.8 +endif diff --git a/manpages/bin/update-version.sh b/manpages/bin/update-version.sh deleted file mode 100755 index 48e9c08..0000000 --- a/manpages/bin/update-version.sh +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh - -## dosfstools(7) -## Copyright (C) 2006-2014 Daniel Baumann <mail@daniel-baumann.ch> -## -## This program comes with ABSOLUTELY NO WARRANTY; for details see COPYING. -## This is free software, and you are welcome to redistribute it -## under certain conditions; see COPYING for details. - - -set -e - -PROJECT="dosfstools" -VERSION="$(cat ../VERSION)" - -DATE="$(LC_ALL=C date +%Y\\\\-%m\\\\-%d)" - -DAY="$(LC_ALL=C date +%d)" -MONTH="$(LC_ALL=C date +%m)" -YEAR="$(LC_ALL=C date +%Y)" - -echo "Updating version headers..." - -for MANPAGE in en/* -do - PROGRAM="$(basename ${MANPAGE} | sed -e 's|\(.*\).[0-9]$|\1|' | tr [a-z] [A-Z])" - SECTION="$(basename ${MANPAGE} | sed -e 's|.*.\([0-9]\)$|\1|')" - - sed -i -e "s|^.TH.*$|.TH ${PROGRAM} ${SECTION} ${DATE} ${VERSION} \"${PROJECT}\"|" ${MANPAGE} -done - -# European date format -for _LANGUAGE in de es fr it -do - if ls po/${_LANGUAGE}/*.po > /dev/null 2>&1 - then - for _FILE in po/${_LANGUAGE}/*.po - do - sed -i -e "s|^msgstr .*.2014-.*$|msgstr \"${DAY}.${MONTH}.${YEAR}\"|g" \ - -e "s|^msgstr .*.2014\"$|msgstr \"${DAY}.${MONTH}.${YEAR}\"|g" \ - "${_FILE}" - done - fi -done - -# Brazilian date format -if ls po/pt_BR/*.po > /dev/null 2>&1 -then - for _FILE in po/pt_BR/*.po - do - sed -i -e "s|^msgstr .*.2014-.*$|msgstr \"${DAY}-${MONTH}-${YEAR}\"|g" \ - -e "s|^msgstr .*-2014\"$|msgstr \"${DAY}-${MONTH}-${YEAR}\"|g" \ - "${_FILE}" - done -fi diff --git a/manpages/en/fatlabel.8 b/manpages/fatlabel.8.in index c00a795..bf5ebbd 100644 --- a/manpages/en/fatlabel.8 +++ b/manpages/fatlabel.8.in @@ -19,7 +19,7 @@ .\" can be found in /usr/share/common-licenses/GPL-3 file. .\" .\" -.TH FATLABEL 8 2015\-05\-16 3.0.28 "dosfstools" +.TH FATLABEL 8 2015\-04\-16 "dosfstools @PACKAGE_VERSION@" .SH NAME \fBfatlabel\fR \- set or get MS\-DOS filesystem label .\" ---------------------------------------------------------------------------- diff --git a/manpages/en/fsck.fat.8 b/manpages/fsck.fat.8.in index f2d44d0..06057a8 100644 --- a/manpages/en/fsck.fat.8 +++ b/manpages/fsck.fat.8.in @@ -19,7 +19,7 @@ .\" can be found in /usr/share/common-licenses/GPL-3 file. .\" .\" -.TH FSCK.FAT 8 2015\-05\-16 3.0.28 "dosfstools" +.TH FSCK.FAT 8 2015\-04\-16 "dosfstools @PACKAGE_VERSION@" .SH NAME \fBfsck.fat\fR \- check and repair MS\-DOS filesystems .\" ---------------------------------------------------------------------------- @@ -115,6 +115,9 @@ MS\-DOS uses only 0xfff7 for bad clusters, where on Atari values 0xfff0...0xfff7 are for this purpose (but the standard value is still 0xfff7). .IP "\fB-b\fR" 4 Make read-only boot sector check. +.IP "\fB-c\fR \fIPAGE\fR" 4 +Use DOS codepage \fIPAGE\fR to decode short file names. +By default codepage 437 is used. .IP "\fB\-d\fR \fIPATH\fR" 4 Delete the specified file. If more than one file with that name exist, the first one is deleted. diff --git a/manpages/en/mkfs.fat.8 b/manpages/mkfs.fat.8.in index 5a5086e..2d19daf 100644 --- a/manpages/en/mkfs.fat.8 +++ b/manpages/mkfs.fat.8.in @@ -1,6 +1,7 @@ .\" mkfs.fat.8 - manpage for fs.fatck .\" .\" Copyright (C) 2006-2014 Daniel Baumann <daniel@debian.org> +.\" Copyright (C) 2016 Andreas Bombe <aeb@debian.org> .\" .\" This program is free software: you can redistribute it and/or modify .\" it under the terms of the GNU General Public License as published by @@ -19,7 +20,7 @@ .\" can be found in /usr/share/common-licenses/GPL-3 file. .\" .\" -.TH MKFS.FAT 8 2015\-05\-16 3.0.28 "dosfstools" +.TH MKFS.FAT 8 2016\-01\-25 "dosfstools @PACKAGE_VERSION@" .SH NAME \fBmkfs.fat\fR \- create an MS-DOS filesystem under Linux .\" ---------------------------------------------------------------------------- @@ -135,6 +136,8 @@ Must be a power of 2, i.e. 1, 2, 4, 8, ... 128. Specify the number of bytes per logical sector. Must be a power of 2 and greater than or equal to 512, i.e. 512, 1024, 2048, 4096, 8192, 16384, or 32768. +Values larger than 4096 are not conforming to the FAT file system specification +and may not work everywhere. .IP "\fB\-v\fR" 4 Verbose execution. .IP "\fB\-\-invariant\fR" 4 diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..3d22ba7 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,63 @@ +# dosfstools src/Makefile.am +# Copyright (C) 2015 Andreas Bombe <aeb@debian.org> +# +# This program 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 3 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, see <http://www.gnu.org/licenses/>. + +AM_CFLAGS = -Wall -Wextra -Wno-sign-compare -Wno-missing-field-initializers \ + -Wmissing-prototypes -Wstrict-prototypes -Wwrite-strings + +sbin_PROGRAMS = fsck.fat mkfs.fat fatlabel +noinst_PROGRAMS = testdevinfo + +fscklabel_common_sources = boot.c boot.h check.c check.h common.c common.h \ + fat.c fat.h file.c file.h io.c io.h lfn.c lfn.h \ + charconv.c charconv.h msdos_fs.h \ + fsck.fat.h endian_compat.h +fsck_fat_SOURCES = fsck.fat.c $(fscklabel_common_sources) +fatlabel_SOURCES = fatlabel.c $(fscklabel_common_sources) + +mkfs_common_sources = device_info.c device_info.h \ + blkdev/blkdev.c blkdev/blkdev.h \ + endian_compat.h \ + blkdev/linux_version.c blkdev/linux_version.h +mkfs_fat_SOURCES = mkfs.fat.c msdos_fs.h $(mkfs_common_sources) +mkfs_fat_CPPFLAGS = -I$(srcdir)/blkdev +mkfs_fat_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS) +mkfs_fat_LDFLAGS = $(UDEV_LIBS) + +testdevinfo_SOURCES = testdevinfo.c $(mkfs_common_sources) +testdevinfo_CPPFLAGS = -I$(srcdir)/blkdev +testdevinfo_CFLAGS = $(AM_CFLAGS) $(UDEV_CFLAGS) +testdevinfo_LDFLAGS = $(UDEV_LIBS) + + +if COMPAT_SYMLINKS +install-exec-hook: + cd $(DESTDIR)$(sbindir) && $(LN_S) -f fatlabel dosfslabel + cd $(DESTDIR)$(sbindir) && $(LN_S) -f fsck.fat dosfsck + cd $(DESTDIR)$(sbindir) && $(LN_S) -f fsck.fat fsck.msdos + cd $(DESTDIR)$(sbindir) && $(LN_S) -f fsck.fat fsck.vfat + cd $(DESTDIR)$(sbindir) && $(LN_S) -f mkfs.fat mkdosfs + cd $(DESTDIR)$(sbindir) && $(LN_S) -f mkfs.fat mkfs.msdos + cd $(DESTDIR)$(sbindir) && $(LN_S) -f mkfs.fat mkfs.vfat + +uninstall-hook: + $(RM) $(DESTDIR)$(sbindir)/dosfslabel + $(RM) $(DESTDIR)$(sbindir)/dosfsck + $(RM) $(DESTDIR)$(sbindir)/fsck.msdos + $(RM) $(DESTDIR)$(sbindir)/fsck.vfat + $(RM) $(DESTDIR)$(sbindir)/mkdosfs + $(RM) $(DESTDIR)$(sbindir)/mkfs.msdos + $(RM) $(DESTDIR)$(sbindir)/mkfs.vfat +endif diff --git a/src/blkdev/README b/src/blkdev/README new file mode 100644 index 0000000..af74eb7 --- /dev/null +++ b/src/blkdev/README @@ -0,0 +1,3 @@ +The source files blkdev.[ch] and linux_version.[ch] have been taken from +util-linux (git://git.kernel.org/pub/scm/utils/util-linux/util-linux.git) +git revision 42f536ee8. diff --git a/src/blkdev/blkdev.c b/src/blkdev/blkdev.c new file mode 100644 index 0000000..ae9c8d1 --- /dev/null +++ b/src/blkdev/blkdev.c @@ -0,0 +1,376 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * + * Written by Karel Zak <kzak@redhat.com> + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <stdint.h> + +#ifdef HAVE_LINUX_HDREG_H +#include <linux/hdreg.h> +#endif + +#ifdef HAVE_LINUX_FD_H +#include <linux/fd.h> +#endif + +#ifdef HAVE_SYS_DISKLABEL_H +#include <sys/disklabel.h> +#endif + +#ifdef HAVE_SYS_DISK_H +# ifdef HAVE_SYS_QUEUE_H +# include <sys/queue.h> /* for LIST_HEAD */ +# endif +# include <sys/disk.h> +#endif + +#include "blkdev.h" +#include "linux_version.h" + +static long +blkdev_valid_offset (int fd, off_t offset) { + char ch; + + if (lseek (fd, offset, 0) < 0) + return 0; + if (read (fd, &ch, 1) < 1) + return 0; + return 1; +} + +int is_blkdev(int fd) +{ + struct stat st; + return (fstat(fd, &st) == 0 && S_ISBLK(st.st_mode)); +} + +off_t +blkdev_find_size (int fd) { + uintmax_t high, low = 0; + + for (high = 1024; blkdev_valid_offset (fd, high); ) { + if (high == UINTMAX_MAX) + return -1; + + low = high; + + if (high >= UINTMAX_MAX/2) + high = UINTMAX_MAX; + else + high *= 2; + } + + while (low < high - 1) + { + uintmax_t mid = (low + high) / 2; + + if (blkdev_valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + blkdev_valid_offset (fd, 0); + return (low + 1); +} + +/* get size in bytes */ +int +blkdev_get_size(int fd, unsigned long long *bytes) +{ +#ifdef DKIOCGETBLOCKCOUNT + /* Apple Darwin */ + if (ioctl(fd, DKIOCGETBLOCKCOUNT, bytes) >= 0) { + *bytes <<= 9; + return 0; + } +#endif + +#ifdef BLKGETSIZE64 + { +#ifdef __linux__ + int ver = get_linux_version(); + + /* kernels 2.4.15-2.4.17, had a broken BLKGETSIZE64 */ + if (ver >= KERNEL_VERSION (2,6,0) || + (ver >= KERNEL_VERSION (2,4,18) && ver < KERNEL_VERSION (2,5,0))) +#endif + if (ioctl(fd, BLKGETSIZE64, bytes) >= 0) + return 0; + } +#endif /* BLKGETSIZE64 */ + +#ifdef BLKGETSIZE + { + unsigned long size; + + if (ioctl(fd, BLKGETSIZE, &size) >= 0) { + *bytes = ((unsigned long long)size << 9); + return 0; + } + } + +#endif /* BLKGETSIZE */ + +#ifdef DIOCGMEDIASIZE + /* FreeBSD */ + if (ioctl(fd, DIOCGMEDIASIZE, bytes) >= 0) + return 0; +#endif + +#ifdef FDGETPRM + { + struct floppy_struct this_floppy; + + if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { + *bytes = ((unsigned long long) this_floppy.size) << 9; + return 0; + } + } +#endif /* FDGETPRM */ + +#ifdef HAVE_SYS_DISKLABEL_H + { + /* + * This code works for FreeBSD 4.11 i386, except for the full device + * (such as /dev/ad0). It doesn't work properly for newer FreeBSD + * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE + * above however. + * + * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw, + * character) devices, so we need to check for S_ISCHR, too. + */ + int part = -1; + struct disklabel lab; + struct partition *pp; + struct stat st; + + if ((fstat(fd, &st) >= 0) && + (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) + part = st.st_rdev & 7; + + if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { + pp = &lab.d_partitions[part]; + if (pp->p_size) { + *bytes = pp->p_size << 9; + return 0; + } + } + } +#endif /* HAVE_SYS_DISKLABEL_H */ + + { + struct stat st; + + if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { + *bytes = st.st_size; + return 0; + } + if (!S_ISBLK(st.st_mode)) + return -1; + } + + *bytes = blkdev_find_size(fd); + return 0; +} + +/* get 512-byte sector count */ +int +blkdev_get_sectors(int fd, unsigned long long *sectors) +{ + unsigned long long bytes; + + if (blkdev_get_size(fd, &bytes) == 0) { + *sectors = (bytes >> 9); + return 0; + } + + return -1; +} + +/* + * Get logical sector size. + * + * This is the smallest unit the storage device can + * address. It is typically 512 bytes. + */ +int blkdev_get_sector_size(int fd, int *sector_size) +{ +#ifdef BLKSSZGET + if (ioctl(fd, BLKSSZGET, sector_size) >= 0) + return 0; + return -1; +#else + (void)fd; /* prevent unused parameter warning */ + *sector_size = DEFAULT_SECTOR_SIZE; + return 0; +#endif +} + +/* + * Get physical block device size. The BLKPBSZGET is supported since Linux + * 2.6.32. For old kernels is probably the best to assume that physical sector + * size is the same as logical sector size. + * + * Example: + * + * rc = blkdev_get_physector_size(fd, &physec); + * if (rc || physec == 0) { + * rc = blkdev_get_sector_size(fd, &physec); + * if (rc) + * physec = DEFAULT_SECTOR_SIZE; + * } + */ +int blkdev_get_physector_size(int fd, int *sector_size) +{ +#ifdef BLKPBSZGET + if (ioctl(fd, BLKPBSZGET, §or_size) >= 0) + return 0; + return -1; +#else + (void)fd; /* prevent unused parameter warning */ + *sector_size = DEFAULT_SECTOR_SIZE; + return 0; +#endif +} + +/* + * Return the alignment status of a device + */ +int blkdev_is_misaligned(int fd) +{ +#ifdef BLKALIGNOFF + int aligned; + + if (ioctl(fd, BLKALIGNOFF, &aligned) < 0) + return 0; /* probably kernel < 2.6.32 */ + /* + * Note that kernel returns -1 as alignement offset if no compatible + * sizes and alignments exist for stacked devices + */ + return aligned != 0 ? 1 : 0; +#else + (void)fd; /* prevent unused parameter warning */ + return 0; +#endif +} + +int blkdev_is_cdrom(int fd) +{ +#ifdef CDROM_GET_CAPABILITY + int ret; + + if ((ret = ioctl(fd, CDROM_GET_CAPABILITY, NULL)) < 0) + return 0; + else + return ret; +#else + (void)fd; /* prevent unused parameter warning */ + return 0; +#endif +} + +/* + * Get kernel's interpretation of the device's geometry. + * + * Returns the heads and sectors - but not cylinders + * as it's truncated for disks with more than 65535 tracks. + * + * Note that this is deprecated in favor of LBA addressing. + */ +int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s) +{ +#ifdef HDIO_GETGEO + { + struct hd_geometry geometry; + + if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) { + *h = geometry.heads; + *s = geometry.sectors; + return 0; + } + } +#endif + +#ifdef FDGETPRM + { + struct floppy_struct fdparam; + + if (ioctl(fd, FDGETPRM, &fdparam) == 0) { + *h = fdparam.head; + *s = fdparam.sect; + return 0; + } + } +#endif + + (void)fd; /* prevent unused parameter warning */ + *h = 0; + *s = 0; + return -1; +} + +/* + * Get start offset of partition + */ +int blkdev_get_start(int fd, unsigned int *s) +{ +#ifdef HDIO_GETGEO + struct hd_geometry geometry; + + if (ioctl(fd, HDIO_GETGEO, &geometry) == 0) { + *s = geometry.start; + return 0; + } +#endif + + (void)fd; /* prevent unused parameter warning */ + *s = 0; + return -1; +} + +/* + * Convert scsi type to human readable string. + */ +const char *blkdev_scsi_type_to_name(int type) +{ + switch (type) { + case SCSI_TYPE_DISK: + return "disk"; + case SCSI_TYPE_TAPE: + return "tape"; + case SCSI_TYPE_PRINTER: + return "printer"; + case SCSI_TYPE_PROCESSOR: + return "processor"; + case SCSI_TYPE_WORM: + return "worm"; + case SCSI_TYPE_ROM: + return "rom"; + case SCSI_TYPE_SCANNER: + return "scanner"; + case SCSI_TYPE_MOD: + return "mo-disk"; + case SCSI_TYPE_MEDIUM_CHANGER: + return "changer"; + case SCSI_TYPE_COMM: + return "comm"; + case SCSI_TYPE_RAID: + return "raid"; + case SCSI_TYPE_ENCLOSURE: + return "enclosure"; + case SCSI_TYPE_RBC: + return "rbc"; + case SCSI_TYPE_OSD: + return "osd"; + case SCSI_TYPE_NO_LUN: + return "no-lun"; + default: + break; + } + return NULL; +} diff --git a/src/blkdev/blkdev.h b/src/blkdev/blkdev.h new file mode 100644 index 0000000..b9179ad --- /dev/null +++ b/src/blkdev/blkdev.h @@ -0,0 +1,149 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * + * Written by Karel Zak <kzak@redhat.com> + */ +#ifndef BLKDEV_H +#define BLKDEV_H + +#include <sys/types.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_IOCCOM_H +# include <sys/ioccom.h> /* for _IO macro on e.g. Solaris */ +#endif +#include <fcntl.h> +#include <unistd.h> + +#ifdef HAVE_SYS_MKDEV_H +# include <sys/mkdev.h> /* major and minor on Solaris */ +#endif + +#define DEFAULT_SECTOR_SIZE 512 + +#ifdef __linux__ +/* very basic ioctls, should be available everywhere */ +# ifndef BLKROSET +# define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */ +# define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */ +# define BLKRRPART _IO(0x12,95) /* re-read partition table */ +# define BLKGETSIZE _IO(0x12,96) /* return device size /512 (long *arg) */ +# define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ +# define BLKRASET _IO(0x12,98) /* set read ahead for block device */ +# define BLKRAGET _IO(0x12,99) /* get current read ahead setting */ +# define BLKFRASET _IO(0x12,100) /* set filesystem (mm/filemap.c) read-ahead */ +# define BLKFRAGET _IO(0x12,101) /* get filesystem (mm/filemap.c) read-ahead */ +# define BLKSECTSET _IO(0x12,102) /* set max sectors per request (ll_rw_blk.c) */ +# define BLKSECTGET _IO(0x12,103) /* get max sectors per request (ll_rw_blk.c) */ +# define BLKSSZGET _IO(0x12,104) /* get block device sector size */ + +/* ioctls introduced in 2.2.16, removed in 2.5.58 */ +# define BLKELVGET _IOR(0x12,106,size_t) /* elevator get */ +# define BLKELVSET _IOW(0x12,107,size_t) /* elevator set */ + +# define BLKBSZGET _IOR(0x12,112,size_t) +# define BLKBSZSET _IOW(0x12,113,size_t) +# endif /* !BLKROSET */ + +# ifndef BLKGETSIZE64 +# define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ +# endif + +/* block device topology ioctls, introduced in 2.6.32 (commit ac481c20) */ +# ifndef BLKIOMIN +# define BLKIOMIN _IO(0x12,120) +# define BLKIOOPT _IO(0x12,121) +# define BLKALIGNOFF _IO(0x12,122) +# define BLKPBSZGET _IO(0x12,123) +# endif + +/* discard zeroes support, introduced in 2.6.33 (commit 98262f27) */ +# ifndef BLKDISCARDZEROES +# define BLKDISCARDZEROES _IO(0x12,124) +# endif + +/* filesystem freeze, introduced in 2.6.29 (commit fcccf502) */ +# ifndef FIFREEZE +# define FIFREEZE _IOWR('X', 119, int) /* Freeze */ +# define FITHAW _IOWR('X', 120, int) /* Thaw */ +# endif + +/* uniform CD-ROM information */ +# ifndef CDROM_GET_CAPABILITY +# define CDROM_GET_CAPABILITY 0x5331 +# endif + +#endif /* __linux */ + + +#ifdef APPLE_DARWIN +# define BLKGETSIZE DKIOCGETBLOCKCOUNT32 +#endif + +#ifndef HDIO_GETGEO +# ifdef __linux__ +# define HDIO_GETGEO 0x0301 +# endif + +struct hd_geometry { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; /* truncated */ + unsigned long start; +}; +#endif /* HDIO_GETGEO */ + + +/* are we working with block device? */ +int is_blkdev(int fd); + +/* Determine size in bytes */ +off_t blkdev_find_size (int fd); + +/* get size in bytes */ +int blkdev_get_size(int fd, unsigned long long *bytes); + +/* get 512-byte sector count */ +int blkdev_get_sectors(int fd, unsigned long long *sectors); + +/* get hardware sector size */ +int blkdev_get_sector_size(int fd, int *sector_size); + +/* specifies whether or not the device is misaligned */ +int blkdev_is_misaligned(int fd); + +/* get physical block device size */ +int blkdev_get_physector_size(int fd, int *sector_size); + +/* is the device cdrom capable? */ +int blkdev_is_cdrom(int fd); + +/* get device's geometry - legacy */ +int blkdev_get_geometry(int fd, unsigned int *h, unsigned int *s); + +/* get partition devices start offset */ +int blkdev_get_start(int fd, unsigned int *s); + +/* SCSI device types. Copied almost as-is from kernel header. + * http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/scsi/scsi.h */ +#define SCSI_TYPE_DISK 0x00 +#define SCSI_TYPE_TAPE 0x01 +#define SCSI_TYPE_PRINTER 0x02 +#define SCSI_TYPE_PROCESSOR 0x03 /* HP scanners use this */ +#define SCSI_TYPE_WORM 0x04 /* Treated as ROM by our system */ +#define SCSI_TYPE_ROM 0x05 +#define SCSI_TYPE_SCANNER 0x06 +#define SCSI_TYPE_MOD 0x07 /* Magneto-optical disk - treated as SCSI_TYPE_DISK */ +#define SCSI_TYPE_MEDIUM_CHANGER 0x08 +#define SCSI_TYPE_COMM 0x09 /* Communications device */ +#define SCSI_TYPE_RAID 0x0c +#define SCSI_TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ +#define SCSI_TYPE_RBC 0x0e +#define SCSI_TYPE_OSD 0x11 +#define SCSI_TYPE_NO_LUN 0x7f + +/* convert scsi type code to name */ +const char *blkdev_scsi_type_to_name(int type); + + +#endif /* BLKDEV_H */ diff --git a/src/blkdev/linux_version.c b/src/blkdev/linux_version.c new file mode 100644 index 0000000..2bcc2cc --- /dev/null +++ b/src/blkdev/linux_version.c @@ -0,0 +1,25 @@ +#include <stdio.h> +#include <sys/utsname.h> + +#include "linux_version.h" + +int get_linux_version (void) +{ + static int kver = -1; + struct utsname uts; + int major = 0; + int minor = 0; + int teeny = 0; + int n; + + if (kver != -1) + return kver; + if (uname (&uts)) + return kver = 0; + + n = sscanf(uts.release, "%d.%d.%d", &major, &minor, &teeny); + if (n < 1 || n > 3) + return kver = 0; + + return kver = KERNEL_VERSION(major, minor, teeny); +} diff --git a/src/blkdev/linux_version.h b/src/blkdev/linux_version.h new file mode 100644 index 0000000..a6a1e99 --- /dev/null +++ b/src/blkdev/linux_version.h @@ -0,0 +1,14 @@ +#ifndef LINUX_VERSION_H +#define LINUX_VERSION_H + +#ifdef HAVE_LINUX_VERSION_H +# include <linux/version.h> +#endif + +#ifndef KERNEL_VERSION +# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +int get_linux_version(void); + +#endif /* LINUX_VERSION_H */ @@ -3,6 +3,7 @@ Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +29,7 @@ #include <stdint.h> #include <string.h> #include <stdlib.h> +#include <sys/types.h> #include <time.h> #include "common.h" @@ -101,8 +103,8 @@ static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss) (unsigned long long)fs->fat_start, (unsigned long long)fs->fat_start / lss); printf("%10d FATs, %d bit entries\n", b->fats, fs->fat_bits); - printf("%10d bytes per FAT (= %u sectors)\n", fs->fat_size, - fs->fat_size / lss); + printf("%10lld bytes per FAT (= %llu sectors)\n", (long long)fs->fat_size, + (long long)fs->fat_size / lss); if (!fs->root_cluster) { printf("Root directory starts at byte %llu (sector %llu)\n", (unsigned long long)fs->root_start, @@ -115,8 +117,9 @@ static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss) printf("Data area starts at byte %llu (sector %llu)\n", (unsigned long long)fs->data_start, (unsigned long long)fs->data_start / lss); - printf("%10lu data clusters (%llu bytes)\n", (unsigned long)fs->clusters, - (unsigned long long)fs->clusters * fs->cluster_size); + printf("%10lu data clusters (%llu bytes)\n", + (unsigned long)fs->data_clusters, + (unsigned long long)fs->data_clusters * fs->cluster_size); printf("%u sectors/track, %u heads\n", le16toh(b->secs_track), le16toh(b->heads)); printf("%10u hidden sectors\n", atari_format ? @@ -155,7 +158,7 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) fs->backupboot_start = bbs * lss; b->backup_boot = htole16(bbs); fs_write(fs->backupboot_start, sizeof(*b), b); - fs_write((loff_t) offsetof(struct boot_sector, backup_boot), + fs_write(offsetof(struct boot_sector, backup_boot), sizeof(b->backup_boot), &b->backup_boot); printf("Created backup of boot sector in sector %d\n", bbs); return; @@ -233,9 +236,9 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) break; if (s > 0 && s < le16toh(b->reserved)) { init_fsinfo(&i); - fs_write((loff_t) s * lss, sizeof(i), &i); + fs_write((off_t)s * lss, sizeof(i), &i); b->info_sector = htole16(s); - fs_write((loff_t) offsetof(struct boot_sector, info_sector), + fs_write(offsetof(struct boot_sector, info_sector), sizeof(b->info_sector), &b->info_sector); if (fs->backupboot_start) fs_write(fs->backupboot_start + @@ -326,8 +329,9 @@ void read_boot(DOS_FS * fs) struct boot_sector b; unsigned total_sectors; unsigned short logical_sector_size, sectors; - unsigned fat_length; - loff_t data_size; + off_t fat_length; + unsigned total_fat_entries; + off_t data_size; fs_read(0, sizeof(b), &b); logical_sector_size = GET_UNALIGNED_W(b.sector_size); @@ -352,19 +356,27 @@ void read_boot(DOS_FS * fs) if (verbose) printf("Checking we can access the last sector of the filesystem\n"); /* Can't access last odd sector anyway, so round down */ - fs_test((loff_t) ((total_sectors & ~1) - 1) * (loff_t) logical_sector_size, + fs_test((off_t)((total_sectors & ~1) - 1) * logical_sector_size, logical_sector_size); + fat_length = le16toh(b.fat_length) ? le16toh(b.fat_length) : le32toh(b.fat32_length); - fs->fat_start = (loff_t) le16toh(b.reserved) * logical_sector_size; - fs->root_start = ((loff_t) le16toh(b.reserved) + b.fats * fat_length) * + if (!fat_length) + die("FAT size is zero."); + + fs->fat_start = (off_t)le16toh(b.reserved) * logical_sector_size; + fs->root_start = ((off_t)le16toh(b.reserved) + b.fats * fat_length) * logical_sector_size; fs->root_entries = GET_UNALIGNED_W(b.dir_entries); fs->data_start = fs->root_start + ROUND_TO_MULTIPLE(fs->root_entries << MSDOS_DIR_BITS, logical_sector_size); - data_size = (loff_t) total_sectors *logical_sector_size - fs->data_start; - fs->clusters = data_size / fs->cluster_size; + + data_size = (off_t)total_sectors * logical_sector_size - fs->data_start; + if (data_size < fs->cluster_size) + die("Filesystem has no space for any data clusters"); + + fs->data_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 */ @@ -385,13 +397,13 @@ void read_boot(DOS_FS * fs) printf("Warning: FAT32 root dir is in a cluster chain, but " "a separate root dir\n" " area is defined. Cannot fix this easily.\n"); - if (fs->clusters < FAT16_THRESHOLD) + if (fs->data_clusters < FAT16_THRESHOLD) printf("Warning: Filesystem is FAT32 according to fat_length " "and fat32_length fields,\n" " but has only %lu clusters, less than the required " "minimum of %d.\n" " This may lead to problems on some systems.\n", - (unsigned long)fs->clusters, FAT16_THRESHOLD); + (unsigned long)fs->data_clusters, FAT16_THRESHOLD); check_fat_state_bit(fs, &b); fs->backupboot_start = le16toh(b.backup_boot) * logical_sector_size; @@ -401,9 +413,9 @@ void read_boot(DOS_FS * fs) } else if (!atari_format) { /* On real MS-DOS, a 16 bit FAT is used whenever there would be too * much clusers otherwise. */ - fs->fat_bits = (fs->clusters >= FAT12_THRESHOLD) ? 16 : 12; - if (fs->clusters >= FAT16_THRESHOLD) - die("Too many clusters (%lu) for FAT16 filesystem.", fs->clusters); + fs->fat_bits = (fs->data_clusters >= FAT12_THRESHOLD) ? 16 : 12; + if (fs->data_clusters >= FAT16_THRESHOLD) + die("Too many clusters (%lu) for FAT16 filesystem.", fs->data_clusters); check_fat_state_bit(fs, &b); } else { /* On Atari, things are more difficult: GEMDOS always uses 12bit FATs @@ -411,14 +423,10 @@ void read_boot(DOS_FS * fs) fs->fat_bits = 16; /* assume 16 bit FAT for now */ /* If more clusters than fat entries in 16-bit fat, we assume * it's a real MSDOS FS with 12-bit fat. */ - if (fs->clusters + 2 > 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))) + if (fs->data_clusters + 2 > fat_length * logical_sector_size * 8 / 16 || + /* if it has one of the usual floppy sizes -> 12bit FAT */ + (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 */ @@ -439,11 +447,10 @@ void read_boot(DOS_FS * fs) fs->label = NULL; } - if (fs->clusters > - ((uint64_t)fs->fat_size * 8 / fs->fat_bits) - 2) - die("Filesystem has %d clusters but only space for %d FAT entries.", - fs->clusters, - ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2); + total_fat_entries = (uint64_t)fs->fat_size * 8 / fs->fat_bits; + if (fs->data_clusters > total_fat_entries - 2) + die("Filesystem has %u clusters but only space for %u FAT entries.", + fs->data_clusters, total_fat_entries - 2); if (!fs->root_entries && !fs->root_cluster) die("Root directory has zero size."); if (fs->root_entries & (MSDOS_DPS - 1)) @@ -491,10 +498,10 @@ static void write_boot_label(DOS_FS * fs, char *label) } } -loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de) +off_t find_volume_de(DOS_FS * fs, DIR_ENT * de) { uint32_t cluster; - loff_t offset; + off_t offset; int i; if (fs->root_cluster) { @@ -525,7 +532,7 @@ static void write_volume_label(DOS_FS * fs, char *label) { time_t now = time(NULL); struct tm *mtime = localtime(&now); - loff_t offset; + off_t offset; int created; DIR_ENT de; @@ -25,7 +25,7 @@ void read_boot(DOS_FS * fs); void write_label(DOS_FS * fs, char *label); -loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de); +off_t find_volume_de(DOS_FS * fs, DIR_ENT * de); /* Reads the boot sector from the currently open device and initializes *FS */ diff --git a/src/check.c b/src/check.c index d8b9d72..59d6d27 100644 --- a/src/check.c +++ b/src/check.c @@ -3,6 +3,7 @@ Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,7 +28,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <limits.h> #include <time.h> #include "common.h" @@ -38,6 +38,10 @@ #include "lfn.h" #include "check.h" + +/* the longest path on the filesystem that can be handled by path_name() */ +#define PATH_NAME_MAX 1023 + static DOS_FILE *root; /* get start field of a dir entry */ @@ -56,7 +60,7 @@ static DOS_FILE *root; #define MODIFY_START(p,v,fs) \ do { \ - uint32_t __v = (v); \ + uint32_t __v = (v); \ if (!p->offset) { \ /* writing to fake entry for FAT32 root dir */ \ if (!__v) die("Oops, deleting FAT32 root dir!"); \ @@ -64,7 +68,7 @@ static DOS_FILE *root; p->dir_ent.start = htole16(__v&0xffff); \ p->dir_ent.starthi = htole16(__v>>16); \ __v = htole32(__v); \ - fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \ + fs_write(offsetof(struct boot_sector,root_cluster), \ sizeof(((struct boot_sector *)0)->root_cluster), \ &__v); \ } \ @@ -75,16 +79,16 @@ static DOS_FILE *root; } \ } while(0) -loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) +off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) { static int curr_num = 0; - loff_t offset; + off_t offset; if (fs->root_cluster) { DIR_ENT d2; int i = 0, got = 0; uint32_t clu_num, prev = 0; - loff_t offset2; + off_t offset2; clu_num = fs->root_cluster; offset = cluster_start(fs, clu_num); @@ -111,7 +115,7 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) for (clu_num = prev + 1; clu_num != prev; clu_num++) { FAT_ENTRY entry; - if (clu_num >= fs->clusters + 2) + if (clu_num >= fs->data_clusters + 2) clu_num = 2; get_fat(&entry, fs->fat, clu_num, fs); if (!entry.value) @@ -132,8 +136,7 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) while (1) { char expanded[12]; sprintf(expanded, pattern, curr_num); - memcpy(de->name, expanded, 8); - memcpy(de->ext, expanded + 8, 3); + memcpy(de->name, expanded, MSDOS_NAME); clu_num = fs->root_cluster; i = 0; offset2 = cluster_start(fs, clu_num); @@ -177,8 +180,7 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) while (1) { char expanded[12]; sprintf(expanded, pattern, curr_num); - memcpy(de->name, expanded, 8); - memcpy(de->ext, expanded + 8, 3); + memcpy(de->name, expanded, MSDOS_NAME); for (scan = 0; scan < fs->root_entries; scan++) if (scan != next_free && !strncmp((const char *)root[scan].name, @@ -205,12 +207,12 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) */ static char *path_name(DOS_FILE * file) { - static char path[PATH_MAX * 2]; + static char path[PATH_NAME_MAX * 2]; if (!file) *path = 0; /* Reached the root directory */ else { - if (strlen(path_name(file->parent)) > PATH_MAX) + if (strlen(path_name(file->parent)) > PATH_NAME_MAX) die("Path name too long."); if (strcmp(path, "/") != 0) strcat(path, "/"); @@ -224,9 +226,9 @@ static char *path_name(DOS_FILE * file) return path; } -static int day_n[] = - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ +static const int day_n[] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; +/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ @@ -236,6 +238,10 @@ static time_t date_dos2unix(unsigned short time, unsigned short date) time_t secs; month = ((date >> 5) & 15) - 1; + if (month < 0) { + /* make sure that nothing bad happens if the month bits were zero */ + month = 0; + } year = date >> 9; secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + @@ -265,7 +271,7 @@ static int bad_name(DOS_FILE * file) int i, spc, suspicious = 0; const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; const unsigned char *name = file->dir_ent.name; - const unsigned char *ext = file->dir_ent.ext; + const unsigned char *ext = name + 8; /* Do not complain about (and auto-correct) the extended attribute files * of OS/2. */ @@ -283,7 +289,7 @@ static int bad_name(DOS_FILE * file) if (file->dir_ent.lcase & FAT_NO_83NAME) return 0; - for (i = 0; i < 8; i++) { + for (i = 0; i < MSDOS_NAME; i++) { if (name[i] < ' ' || name[i] == 0x7f) return 1; if (name[i] > 0x7f) @@ -292,15 +298,6 @@ static int bad_name(DOS_FILE * file) return 1; } - for (i = 0; i < 3; i++) { - if (ext[i] < ' ' || ext[i] == 0x7f) - return 1; - if (ext[i] > 0x7f) - ++suspicious; - if (strchr(bad_chars, ext[i])) - return 1; - } - spc = 0; for (i = 0; i < 8; i++) { if (name[i] == ' ') @@ -332,7 +329,7 @@ static int bad_name(DOS_FILE * file) return 0; } -static void lfn_remove(loff_t from, loff_t to) +static void lfn_remove(off_t from, off_t to) { DIR_ENT empty; @@ -356,7 +353,7 @@ static void drop_file(DOS_FS * fs, DOS_FILE * file) if (file->lfn) lfn_remove(file->lfn_offset, file->offset); for (cluster = FSTART(file, fs); cluster > 0 && cluster < - fs->clusters + 2; cluster = next_cluster(fs, cluster)) + fs->data_clusters + 2; cluster = next_cluster(fs, cluster)) set_owner(fs, cluster, NULL); --n_files; } @@ -392,8 +389,7 @@ static void auto_rename(DOS_FILE * file) char num[8]; sprintf(num, "%07lu", (unsigned long)number); memcpy(file->dir_ent.name, "FSCK", 4); - memcpy(file->dir_ent.name + 4, num, 4); - memcpy(file->dir_ent.ext, num + 4, 3); + memcpy(file->dir_ent.name + 4, num, 7); for (walk = first; walk; walk = walk->next) if (walk != file && !strncmp((const char *)walk->dir_ent.name, @@ -406,7 +402,7 @@ static void auto_rename(DOS_FILE * file) file->dir_ent.lcase &= ~FAT_NO_83NAME; /* reset the attributes, only keep DIR and VOLUME */ file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); - fs_write(file->offset, MSDOS_NAME + 2, file->dir_ent.name); + fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent); } else { fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); } @@ -449,7 +445,7 @@ static void rename_file(DOS_FILE * file) file->dir_ent.lcase &= ~FAT_NO_83NAME; /* reset the attributes, only keep DIR and VOLUME */ file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); - fs_write(file->offset, MSDOS_NAME + 2, file->dir_ent.name); + fs_write(file->offset, MSDOS_NAME + 2, &file->dir_ent); } else { fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); } @@ -552,13 +548,15 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) die("Bad FAT32 root directory! (bad start cluster 1)\n"); MODIFY_START(file, 0, fs); } - if (FSTART(file, fs) >= fs->clusters + 2) { + if (FSTART(file, fs) >= fs->data_clusters + 2) { printf ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n", - path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)(fs->clusters + 1)); + path_name(file), (unsigned long)FSTART(file, fs), + (unsigned long)(fs->data_clusters + 1)); if (!file->offset) die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n", - (unsigned long)FSTART(file, fs), (unsigned long)(fs->clusters + 1)); + (unsigned long)FSTART(file, fs), + (unsigned long)(fs->data_clusters + 1)); MODIFY_START(file, 0, fs); } clusters = prev = 0; @@ -581,10 +579,10 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <= (uint64_t)clusters * fs->cluster_size) { printf - ("%s\n File size is %u bytes, cluster chain length is > %lu " + ("%s\n File size is %u bytes, cluster chain length is > %llu " "bytes.\n Truncating file to %u bytes.\n", path_name(file), le32toh(file->dir_ent.size), - (uint64_t)clusters * fs->cluster_size, + (unsigned long long)clusters * fs->cluster_size, le32toh(file->dir_ent.size)); truncate_file(fs, file, clusters); break; @@ -844,7 +842,7 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) uint32_t walk, prev, clusters, next_clu; prev = clusters = 0; - for (walk = FSTART(file, fs); walk > 1 && walk < fs->clusters + 2; + for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; walk = next_clu) { next_clu = next_cluster(fs, walk); @@ -885,7 +883,7 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) set_owner(fs, walk, file); } /* Revert ownership (for now) */ - for (walk = FSTART(file, fs); walk > 1 && walk < fs->clusters + 2; + for (walk = FSTART(file, fs); walk > 1 && walk < fs->data_clusters + 2; walk = next_cluster(fs, walk)) if (bad_cluster(fs, walk)) break; @@ -905,7 +903,7 @@ static void undelete(DOS_FS * fs, DOS_FILE * file) walk = FSTART(file, fs); - while (left && (walk >= 2) && (walk < fs->clusters + 2)) { + while (left && (walk >= 2) && (walk < fs->data_clusters + 2)) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, walk, fs); @@ -948,7 +946,7 @@ static void new_dir(void) * @param cp */ static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent, - loff_t offset, FDSC ** cp) + off_t offset, FDSC ** cp) { DOS_FILE *new; DIR_ENT de; diff --git a/src/check.h b/src/check.h index fcb6bea..933bbf4 100644 --- a/src/check.h +++ b/src/check.h @@ -23,7 +23,7 @@ #ifndef _CHECK_H #define _CHECK_H -loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern); +off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern); /* Allocate a free slot in the root directory for a new file. The file name is constructed after 'pattern', which must include a %d type format for printf diff --git a/src/device_info.c b/src/device_info.c new file mode 100644 index 0000000..f5d11ac --- /dev/null +++ b/src/device_info.c @@ -0,0 +1,332 @@ +/* device_info.c - Collect device information for mkfs.fat + + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> + + This program 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + + +#include <stdint.h> +#include <stdbool.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#ifdef HAVE_UDEV +#include <libudev.h> +#endif + +#if HAVE_DECL_GETMNTENT +#include <paths.h> +#include <mntent.h> +#endif + +#if HAVE_DECL_GETMNTINFO +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/mount.h> +#endif + +#include <unistd.h> +#include <dirent.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "blkdev.h" +#include "device_info.h" + + +static const struct device_info device_info_clueless = { + .type = TYPE_UNKNOWN, + .partition = -1, + .has_children = -1, + .geom_heads = -1, + .geom_sectors = -1, + .geom_start = -1, + .sector_size = -1, + .size = -1, +}; + + +int device_info_verbose; + + +static void get_block_device_size(struct device_info *info, int fd) +{ + unsigned long long bytes; + + if (!blkdev_get_size(fd, &bytes) && bytes != 0) + info->size = bytes; +} + + +static void get_block_geometry(struct device_info *info, int fd) +{ + unsigned int heads, sectors, start; + + if (!blkdev_get_geometry(fd, &heads, §ors) + && heads && sectors) { + info->geom_heads = heads; + info->geom_sectors = sectors; + } + + if (!blkdev_get_start(fd, &start)) + info->geom_start = start; +} + + +static void get_sector_size(struct device_info *info, int fd) +{ + int size; + + if (!blkdev_get_sector_size(fd, &size)) + info->sector_size = size; +} + + +static int udev_fill_info(struct device_info *info, struct stat *stat); + +#ifdef HAVE_UDEV +static int udev_fill_info(struct device_info *info, struct stat *stat) +{ + struct udev *ctx; + struct udev_device *dev, *parent; + struct udev_enumerate *uenum; + const char *attr; + char holders_path[PATH_MAX + 1]; + DIR *holders_dir; + struct dirent *dir_entry; + unsigned long number; + char *endptr; + + if (device_info_verbose >= 3) + printf("udev_fill_info()\n"); + + ctx = udev_new(); + if (!ctx) { + if (device_info_verbose) + printf("no udev library context\n"); + return -1; + } + + dev = udev_device_new_from_devnum(ctx, 'b', stat->st_rdev); + if (!dev) { + if (device_info_verbose) + printf("no udev context\n"); + udev_unref(ctx); + return -1; + } + + /* + * first, look for for dependent devices (partitions or virtual mappings on + * this device) + */ + if (device_info_verbose >= 3) + printf("looking for dependent devices\n"); + + uenum = udev_enumerate_new(ctx); + if (uenum) { + struct udev_list_entry *entry; + if (udev_enumerate_add_match_parent(uenum, dev) >= 0 && + udev_enumerate_scan_devices(uenum) >= 0) { + entry = udev_enumerate_get_list_entry(uenum); + if (entry) { + /* + * the list of children includes the parent device, so make + * sure that has_children is -1 to end up with the correct + * count + */ + info->has_children = -1; + + while (entry) { + if (device_info_verbose >= 2) + printf("child-or-self: %s\n", udev_list_entry_get_name(entry)); + entry = udev_list_entry_get_next(entry); + info->has_children++; + } + } else + info->has_children = 0; + } + udev_enumerate_unref(uenum); + } + + /* see if the holders directory in sysfs exists and has entries */ + if (device_info_verbose >= 2) + printf("syspath: %s\n", udev_device_get_syspath(dev)); + if (info->has_children < 1 || device_info_verbose >= 3) { + snprintf(holders_path, PATH_MAX, "%s/holders", + udev_device_get_syspath(dev)); + holders_path[PATH_MAX] = 0; + + if (info->has_children < 0) + info->has_children = 0; + + holders_dir = opendir(holders_path); + if (holders_dir) { + dir_entry = readdir(holders_dir); + while (dir_entry) { + if (dir_entry->d_reclen && dir_entry->d_name[0] != '.') { + if (device_info_verbose >= 2) + printf("holder: %s\n", dir_entry->d_name); + + info->has_children++; + + /* look up and print every holder when very verbose */ + if (device_info_verbose < 3) + break; + } + dir_entry = readdir(holders_dir); + } + + closedir(holders_dir); + } + } + + /* + * block devices on real hardware have either other block devices + * (in the case of partitions) or the actual hardware as parent + */ + parent = udev_device_get_parent(dev); + + if (!parent) { + if (device_info_verbose >= 3) + printf("no parent found, therefore virtual device\n"); + info->type = TYPE_VIRTUAL; + info->partition = 0; + udev_device_unref(dev); + return 0; + } + + attr = udev_device_get_sysattr_value(dev, "removable"); + if (device_info_verbose >= 3) { + if (attr) + printf("attribute \"removable\" is \"%s\"\n", attr); + else + printf("attribute \"removable\" not found\n"); + } + if (attr && !strcmp(attr, "1")) + info->type = TYPE_REMOVABLE; + else + info->type = TYPE_FIXED; + + attr = udev_device_get_sysattr_value(dev, "partition"); + if (attr) { + if (device_info_verbose >= 3) + printf("attribute \"partition\" is \"%s\"\n", attr); + + number = strtoul(attr, &endptr, 10); + if (!*endptr) + info->partition = number; + } else { + printf("attribute \"partition\" not found\n"); + if (info->type != TYPE_VIRTUAL && parent) { + /* partitions have other block devices as parent */ + attr = udev_device_get_subsystem(parent); + if (attr) { + if (device_info_verbose >= 3) + printf("parent subsystem is \"%s\"\n", attr); + + if (!strcmp(attr, "block")) + /* we don't know the partition number, use 1 */ + info->partition = 1; + else + info->partition = 0; + } + } + } + + udev_device_unref(dev); + udev_unref(ctx); + return 0; +} +#else /* HAVE_UDEV */ +static int udev_fill_info(struct device_info *info, struct stat *stat) +{ + /* prevent "unused parameter" warning */ + (void)stat; + (void)info; + + return -1; +} +#endif + + +int get_device_info(int fd, struct device_info *info) +{ + struct stat stat; + int ret; + + *info = device_info_clueless; + + ret = fstat(fd, &stat); + if (ret < 0) { + perror("fstat on target failed"); + return -1; + } + + if (S_ISREG(stat.st_mode)) { + /* there is nothing more to discover for an image file */ + info->type = TYPE_FILE; + info->partition = 0; + info->size = stat.st_size; + return 0; + } + + if (!S_ISBLK(stat.st_mode)) { + /* neither regular file nor block device? not usable */ + info->type = TYPE_BAD; + return 0; + } + + get_block_device_size(info, fd); + get_block_geometry(info, fd); + get_sector_size(info, fd); + + /* use udev information if available */ + udev_fill_info(info, &stat); + + return 0; +} + + +int is_device_mounted(const char *path) +{ +#if HAVE_DECL_GETMNTENT + FILE *f; + struct mntent *mnt; + + if ((f = setmntent(_PATH_MOUNTED, "r")) == NULL) + return 0; + while ((mnt = getmntent(f)) != NULL) + if (strcmp(path, mnt->mnt_fsname) == 0) + return 1; + endmntent(f); + return 0; +#endif + +#if HAVE_DECL_GETMNTINFO + struct statfs *stat; + int count, i; + + count = getmntinfo(&stat, 0); + for (i = 0; i < count; i++) + if (!strcmp(path, stat[i].f_mntfromname)) + return 1; + return 0; +#endif + + (void)path; /* prevent unused parameter warning */ + return 0; +} diff --git a/src/device_info.h b/src/device_info.h new file mode 100644 index 0000000..3f4195a --- /dev/null +++ b/src/device_info.h @@ -0,0 +1,56 @@ +#ifndef DEVICE_INFO_H +#define DEVICE_INFO_H + +enum device_type { + TYPE_UNKNOWN, /* type could not be determined */ + TYPE_BAD, /* neither file nor block device */ + TYPE_FILE, /* image file rather than device */ + TYPE_VIRTUAL, /* block devices like LVM or RAID volumes */ + TYPE_REMOVABLE, /* removable disk device */ + TYPE_FIXED /* fixed disk device */ +}; + +struct device_info { + enum device_type type; + + /* + * partition number if detected + * 0 = whole disk device (including unpartitioned image file) + * -1 = could not be determined + */ + int partition; + + /* + * whether partitions or device mapper devices or any other kind of + * children use this device + * 1 = yes + * 0 = no + * -1 = could not be determined + */ + int has_children; + + /* + * detected geometry, or -1 if unknown + */ + int geom_heads; + int geom_sectors; + long geom_start; + + /* + * detected sector size or -1 if unknown + */ + int sector_size; + + /* + * size in bytes, or -1 if unknown + */ + long long size; +}; + + +extern int device_info_verbose; + +int get_device_info(int fd, struct device_info *info); +int is_device_mounted(const char *path); + +#endif diff --git a/src/endian_compat.h b/src/endian_compat.h new file mode 100644 index 0000000..44168c7 --- /dev/null +++ b/src/endian_compat.h @@ -0,0 +1,29 @@ +#ifndef ENDIAN_COMPAT_H +#define ENDIAN_COMPAT_H + +#if defined(HAVE_ENDIAN_H) +#include <endian.h> +#elif defined(HAVE_SYS_ENDIAN_H) +#include <sys/endian.h> +#elif defined(__APPLE__) + #include <libkern/OSByteOrder.h> + + #define htobe16(x) OSSwapHostToBigInt16(x) + #define htole16(x) OSSwapHostToLittleInt16(x) + #define be16toh(x) OSSwapBigToHostInt16(x) + #define le16toh(x) OSSwapLittleToHostInt16(x) + + #define htobe32(x) OSSwapHostToBigInt32(x) + #define htole32(x) OSSwapHostToLittleInt32(x) + #define be32toh(x) OSSwapBigToHostInt32(x) + #define le32toh(x) OSSwapLittleToHostInt32(x) + + #define htobe64(x) OSSwapHostToBigInt64(x) + #define htole64(x) OSSwapHostToLittleInt64(x) + #define be64toh(x) OSSwapBigToHostInt64(x) + #define le64toh(x) OSSwapLittleToHostInt64(x) +#else +#error No endian.h available and no fallback code +#endif + +#endif @@ -47,6 +47,11 @@ void get_fat(FAT_ENTRY * entry, void *fat, uint32_t cluster, DOS_FS * fs) { unsigned char *ptr; + if (cluster > fs->data_clusters + 1) { + die("Internal error: cluster out of range in get_fat() (%lu > %lu).", + (unsigned long)cluster, (unsigned long)(fs->data_clusters + 1)); + } + switch (fs->fat_bits) { case 12: ptr = &((unsigned char *)fat)[cluster * 3 / 2]; @@ -94,7 +99,7 @@ void read_fat(DOS_FS * fs) fs->fat = NULL; fs->cluster_owner = NULL; - total_num_clusters = fs->clusters + 2UL; + total_num_clusters = fs->data_clusters + 2; eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL; if (fs->fat_bits != 12) @@ -155,17 +160,19 @@ void read_fat(DOS_FS * fs) memset(fs->cluster_owner, 0, (total_num_clusters * sizeof(DOS_FILE *))); /* Truncate any cluster chains that link to something out of range */ - for (i = 2; i < fs->clusters + 2; i++) { + for (i = 2; i < fs->data_clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); if (curEntry.value == 1) { - printf("Cluster %ld out of range (1). Setting to EOF.\n", (long)(i - 2)); + printf("Cluster %ld out of range (1). Setting to EOF.\n", + (long)(i - 2)); set_fat(fs, i, -1); } - if (curEntry.value >= fs->clusters + 2 && + if (curEntry.value >= fs->data_clusters + 2 && (curEntry.value < FAT_MIN_BAD(fs))) { printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", - (long)(i - 2), (long)curEntry.value, (long)(fs->clusters + 2 - 1)); + (long)(i - 2), (long)curEntry.value, + (long)(fs->data_clusters + 2 - 1)); set_fat(fs, i, -1); } } @@ -188,12 +195,22 @@ void set_fat(DOS_FS * fs, uint32_t cluster, int32_t new) { unsigned char *data = NULL; int size; - loff_t offs; + off_t offs; + + if (cluster > fs->data_clusters + 1) { + die("Internal error: cluster out of range in set_fat() (%lu > %lu).", + (unsigned long)cluster, (unsigned long)(fs->data_clusters + 1)); + } if (new == -1) new = FAT_EOF(fs); else if ((long)new == -2) new = FAT_BAD(fs); + else if (new > fs->data_clusters + 1) { + die("Internal error: new cluster out of range in set_fat() (%lu > %lu).", + (unsigned long)new, (unsigned long)(fs->data_clusters + 1)); + } + switch (fs->fat_bits) { case 12: data = fs->fat + cluster * 3 / 2; @@ -205,10 +222,12 @@ void set_fat(DOS_FS * fs, uint32_t cluster, int32_t new) data[1] = new >> 4; } else { FAT_ENTRY subseqEntry; - get_fat(&subseqEntry, fs->fat, cluster + 1, fs); + if (cluster != fs->data_clusters + 1) + get_fat(&subseqEntry, fs->fat, cluster + 1, fs); + else + subseqEntry.value = 0; data[0] = new & 0xff; - data[1] = (new >> 8) | (cluster == fs->clusters - 1 ? 0 : - (0xff & subseqEntry.value) << 4); + data[1] = (new >> 8) | ((0xff & subseqEntry.value) << 4); } size = 2; break; @@ -272,10 +291,9 @@ uint32_t next_cluster(DOS_FS * fs, uint32_t cluster) return FAT_IS_EOF(fs, value) ? -1 : value; } -loff_t cluster_start(DOS_FS * fs, uint32_t cluster) +off_t cluster_start(DOS_FS * fs, uint32_t cluster) { - return fs->data_start + ((loff_t) cluster - - 2) * (uint64_t)fs->cluster_size; + return fs->data_start + ((off_t)cluster - 2) * (uint64_t)fs->cluster_size; } /** @@ -312,7 +330,7 @@ void fix_bad(DOS_FS * fs) if (verbose) printf("Checking for bad clusters.\n"); - for (i = 2; i < fs->clusters + 2; i++) { + for (i = 2; i < fs->data_clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); @@ -332,7 +350,7 @@ void reclaim_free(DOS_FS * fs) if (verbose) printf("Checking for unused clusters.\n"); reclaimed = 0; - for (i = 2; i < fs->clusters + 2; i++) { + for (i = 2; i < fs->data_clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); @@ -367,7 +385,7 @@ static void tag_free(DOS_FS * fs, DOS_FILE * owner, uint32_t *num_refs, if (start_cluster == 0) start_cluster = 2; - for (i = start_cluster; i < fs->clusters + 2; i++) { + for (i = start_cluster; i < fs->data_clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); @@ -418,7 +436,7 @@ void reclaim_file(DOS_FS * fs) if (verbose) printf("Reclaiming unconnected clusters.\n"); - total_num_clusters = fs->clusters + 2UL; + total_num_clusters = fs->data_clusters + 2; num_refs = alloc(total_num_clusters * sizeof(uint32_t)); memset(num_refs, 0, (total_num_clusters * sizeof(uint32_t))); @@ -431,7 +449,7 @@ void reclaim_file(DOS_FS * fs) get_fat(&curEntry, fs->fat, i, fs); next = curEntry.value; - if (!get_owner(fs, i) && next && next < fs->clusters + 2) { + if (!get_owner(fs, i) && next && next < fs->data_clusters + 2) { /* Cluster is linked, but not owned (orphan) */ FAT_ENTRY nextEntry; get_fat(&nextEntry, fs->fat, next, fs); @@ -483,7 +501,7 @@ void reclaim_file(DOS_FS * fs) /* If this cluster is the head of an orphan chain... */ if (get_owner(fs, i) == &orphan && !num_refs[i]) { DIR_ENT de; - loff_t offset; + off_t offset; files++; offset = alloc_rootdir_entry(fs, &de, "FSCK%04dREC"); de.start = htole16(i & 0xffff); @@ -511,7 +529,7 @@ uint32_t update_free(DOS_FS * fs) uint32_t free = 0; int do_set = 0; - for (i = 2; i < fs->clusters + 2; i++) { + for (i = 2; i < fs->data_clusters + 2; i++) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); @@ -49,7 +49,7 @@ uint32_t next_cluster(DOS_FS * fs, uint32_t cluster); last cluster of the respective cluster chain. CLUSTER must not be a bad cluster. */ -loff_t cluster_start(DOS_FS * fs, uint32_t cluster); +off_t cluster_start(DOS_FS * fs, uint32_t cluster); /* Returns the byte offset of CLUSTER, relative to the respective device. */ diff --git a/src/fatlabel.c b/src/fatlabel.c index 1484ba5..9268ddb 100644 --- a/src/fatlabel.c +++ b/src/fatlabel.c @@ -4,6 +4,7 @@ Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2007 Red Hat, Inc. Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -92,7 +93,7 @@ int main(int argc, char *argv[]) char *device = NULL; char label[12] = { 0 }; - loff_t offset; + off_t offset; DIR_ENT de; check_atari(); @@ -134,7 +135,7 @@ int main(int argc, char *argv[]) if (offset == 0) fprintf(stdout, "%s\n", fs.label); else - fprintf(stdout, "%.8s%.3s\n", de.name, de.ext); + fprintf(stdout, "%.8s%.3s\n", de.name, de.name + 8); exit(0); } diff --git a/src/fsck.fat.c b/src/fsck.fat.c index f786a93..c244aba 100644 --- a/src/fsck.fat.c +++ b/src/fsck.fat.c @@ -217,7 +217,8 @@ exit: if (!boot_only) printf("%s: %u files, %lu/%lu clusters\n", argv[optind], - n_files, (unsigned long)fs.clusters - free_clusters, (unsigned long)fs.clusters); + n_files, (unsigned long)fs.data_clusters - free_clusters, + (unsigned long)fs.data_clusters); return fs_close(rw) ? 1 : 0; } diff --git a/src/fsck.fat.h b/src/fsck.fat.h index e5f6178..168049e 100644 --- a/src/fsck.fat.h +++ b/src/fsck.fat.h @@ -3,6 +3,7 @@ Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -27,10 +28,11 @@ #ifndef _DOSFSCK_H #define _DOSFSCK_H +#include <sys/types.h> #include <fcntl.h> #include <stddef.h> #include <stdint.h> -#include <endian.h> +#include "endian_compat.h" #include "msdos_fs.h" @@ -120,7 +122,7 @@ struct info_sector { }; typedef struct { - uint8_t name[8], ext[3]; /* name and extension */ + uint8_t name[MSDOS_NAME]; /* name including extension */ uint8_t attr; /* attribute bits */ uint8_t lcase; /* Case for base and extension */ uint8_t ctime_ms; /* Creation time, milliseconds */ @@ -135,8 +137,8 @@ typedef struct { typedef struct _dos_file { DIR_ENT dir_ent; char *lfn; - loff_t offset; - loff_t lfn_offset; + off_t offset; + off_t lfn_offset; struct _dos_file *parent; /* parent directory */ struct _dos_file *next; /* next entry */ struct _dos_file *first; /* first entry (directory only) */ @@ -149,19 +151,19 @@ typedef struct { typedef struct { int nfats; - loff_t fat_start; - unsigned int fat_size; /* unit is bytes */ + off_t fat_start; + off_t fat_size; /* unit is bytes */ unsigned int fat_bits; /* size of a FAT entry */ unsigned int eff_fat_bits; /* # of used bits in a FAT entry */ uint32_t root_cluster; /* 0 for old-style root dir */ - loff_t root_start; + off_t root_start; unsigned int root_entries; - loff_t data_start; + off_t data_start; unsigned int cluster_size; - uint32_t clusters; - loff_t fsinfo_start; /* 0 if not present */ + uint32_t data_clusters; /* not including two reserved cluster numbers */ + off_t fsinfo_start; /* 0 if not present */ long free_clusters; - loff_t backupboot_start; /* 0 if not present */ + off_t backupboot_start; /* 0 if not present */ unsigned char *fat; DOS_FILE **cluster_owner; char *label; @@ -3,6 +3,7 @@ Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch> Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -35,11 +36,10 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <sys/types.h> #include <sys/stat.h> -#include <sys/ioctl.h> #include <errno.h> #include <fcntl.h> -#include <linux/fd.h> #include "fsck.fat.h" #include "common.h" @@ -47,7 +47,7 @@ typedef struct _change { void *data; - loff_t pos; + off_t pos; int size; struct _change *next; } CHANGE; @@ -55,57 +55,15 @@ typedef struct _change { static CHANGE *changes, *last; static int fd, did_change = 0; -unsigned device_no; - -#ifdef __DJGPP__ -#include "volume.h" /* DOS lowlevel disk access functions */ -loff_t llseek(int fd, loff_t offset, int whence) -{ - if ((whence != SEEK_SET) || (fd == 4711)) - return -1; /* only those supported */ - return VolumeSeek(offset); -} - -#define open OpenVolume -#define close CloseVolume -#define read(a,b,c) ReadVolume(b,c) -#define write(a,b,c) WriteVolume(b,c) -#else -loff_t llseek(int fd, loff_t offset, int whence) -{ - return (loff_t) lseek64(fd, (off64_t) offset, whence); -} -#endif void fs_open(char *path, int rw) { - struct stat stbuf; - if ((fd = open(path, rw ? O_RDWR : O_RDONLY)) < 0) { perror("open"); exit(6); } changes = last = NULL; did_change = 0; - -#ifndef _DJGPP_ - if (fstat(fd, &stbuf) < 0) - pdie("fstat %s", path); - device_no = S_ISBLK(stbuf.st_mode) ? (stbuf.st_rdev >> 8) & 0xff : 0; -#else - if (IsWorkingOnImageFile()) { - if (fstat(GetVolumeHandle(), &stbuf) < 0) - pdie("fstat image %s", path); - device_no = 0; - } else { - /* return 2 for floppy, 1 for ramdisk, 7 for loopback */ - /* used by boot.c in Atari mode: floppy always FAT12, */ - /* loopback / ramdisk only FAT12 if usual floppy size, */ - /* harddisk always FAT16 on Atari... */ - device_no = (GetVolumeHandle() < 2) ? 2 : 1; - /* telling "floppy" for A:/B:, "ramdisk" for the rest */ - } -#endif } /** @@ -117,12 +75,12 @@ void fs_open(char *path, int rw) * @param[in] size Number of bytes to read * @param[out] data Where to put the data read */ -void fs_read(loff_t pos, int size, void *data) +void fs_read(off_t pos, int size, void *data) { CHANGE *walk; int got; - if (llseek(fd, pos, 0) != pos) + if (lseek(fd, pos, 0) != pos) pdie("Seek to %lld", pos); if ((got = read(fd, data, size)) < 0) pdie("Read %d bytes at %lld", size, pos); @@ -131,12 +89,8 @@ void fs_read(loff_t pos, int size, void *data) for (walk = changes; walk; walk = walk->next) { if (walk->pos < pos + size && walk->pos + walk->size > pos) { if (walk->pos < pos) - memcpy(data, (char *)walk->data + pos - walk->pos, min(size, - walk-> - size - - pos + - walk-> - pos)); + memcpy(data, (char *)walk->data + pos - walk->pos, + min(size, walk->size - pos + walk->pos)); else memcpy((char *)data + walk->pos - pos, walk->data, min(walk->size, size + pos - walk->pos)); @@ -144,12 +98,12 @@ void fs_read(loff_t pos, int size, void *data) } } -int fs_test(loff_t pos, int size) +int fs_test(off_t pos, int size) { void *scratch; int okay; - if (llseek(fd, pos, 0) != pos) + if (lseek(fd, pos, 0) != pos) pdie("Seek to %lld", pos); scratch = alloc(size); okay = read(fd, scratch, size) == size; @@ -157,14 +111,14 @@ int fs_test(loff_t pos, int size) return okay; } -void fs_write(loff_t pos, int size, void *data) +void fs_write(off_t pos, int size, void *data) { CHANGE *new; int did; if (write_immed) { did_change = 1; - if (llseek(fd, pos, 0) != pos) + if (lseek(fd, pos, 0) != pos) pdie("Seek to %lld", pos); if ((did = write(fd, data, size)) == size) return; @@ -191,7 +145,7 @@ static void fs_flush(void) while (changes) { this = changes; changes = changes->next; - if (llseek(fd, this->pos, 0) != this->pos) + if (lseek(fd, this->pos, 0) != this->pos) fprintf(stderr, "Seek to %lld failed: %s\n Did not write %d bytes.\n", (long long)this->pos, strerror(errno), this->size); @@ -27,28 +27,24 @@ #ifndef _IO_H #define _IO_H -#include <fcntl.h> /* for loff_t */ - -loff_t llseek(int fd, loff_t offset, int whence); - -/* lseek() analogue for large offsets. */ +#include <fcntl.h> /* for off_t */ void fs_open(char *path, int rw); /* Opens the filesystem PATH. If RW is zero, the filesystem is opened read-only, otherwise, it is opened read-write. */ -void fs_read(loff_t pos, int size, void *data); +void fs_read(off_t pos, int size, void *data); /* Reads SIZE bytes starting at POS into DATA. Performs all applicable changes. */ -int fs_test(loff_t pos, int size); +int fs_test(off_t pos, int size); /* Returns a non-zero integer if SIZE bytes starting at POS can be read without errors. Otherwise, it returns zero. */ -void fs_write(loff_t pos, int size, void *data); +void fs_write(off_t pos, int size, void *data); /* If write_immed is non-zero, SIZE bytes are written from DATA to the disk, starting at POS. If write_immed is zero, the change is added to a list in @@ -64,8 +60,4 @@ int fs_changed(void); /* Determines whether the filesystem has changed. See fs_close. */ -extern unsigned device_no; - -/* Major number of device (0 if file) and size (in 512 byte sectors) */ - #endif @@ -2,6 +2,7 @@ Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -53,7 +54,7 @@ typedef struct { unsigned char *lfn_unicode = NULL; unsigned char lfn_checksum; int lfn_slot = -1; -loff_t *lfn_offsets = NULL; +off_t *lfn_offsets = NULL; int lfn_parts = 0; static unsigned char fat_uni2esc[64] = { @@ -171,7 +172,7 @@ static void clear_lfn_slots(int start, int end) } } -void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name) +void lfn_fix_checksum(off_t from, off_t to, const char *short_name) { int i; uint8_t sum; @@ -196,7 +197,7 @@ void lfn_reset(void) /* This function is only called with de->attr == VFAT_LN_ATTR. It stores part * of the long name. */ -void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) +void lfn_add_slot(DIR_ENT * de, off_t dir_offset) { LFN_ENT *lfn = (LFN_ENT *) de; int slot = lfn->id & LFN_ID_SLOTMASK; @@ -254,7 +255,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) lfn_slot = slot; lfn_checksum = lfn->alias_checksum; lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2); - lfn_offsets = alloc(lfn_slot * sizeof(loff_t)); + lfn_offsets = alloc(lfn_slot * sizeof(off_t)); lfn_parts = 0; } else if (lfn_slot == -1 && slot != 0) { /* No LFN in progress, but slot found; start bit missing */ @@ -265,6 +266,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) printf("Long filename fragment \"%s\" found outside a LFN " "sequence.\n (Maybe the start bit is missing on the " "last fragment)\n", part); + free(part); if (interactive) { printf("1: Delete fragment\n2: Leave it as it is.\n" "3: Set start bit\n"); @@ -273,7 +275,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) switch (interactive ? get_key("123", "?") : '2') { case '1': if (!lfn_offsets) - lfn_offsets = alloc(sizeof(loff_t)); + lfn_offsets = alloc(sizeof(off_t)); lfn_offsets[0] = dir_offset; clear_lfn_slots(0, 0); lfn_reset(); @@ -288,7 +290,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) lfn_slot = slot; lfn_checksum = lfn->alias_checksum; lfn_unicode = alloc((lfn_slot * CHARS_PER_LFN + 1) * 2); - lfn_offsets = alloc(lfn_slot * sizeof(loff_t)); + lfn_offsets = alloc(lfn_slot * sizeof(off_t)); lfn_parts = 0; break; } @@ -320,7 +322,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) switch (interactive ? get_key(can_fix ? "123" : "12", "?") : '2') { case '1': if (!lfn_offsets) { - lfn_offsets = alloc(sizeof(loff_t)); + lfn_offsets = alloc(sizeof(off_t)); lfn_parts = 0; } lfn_offsets[lfn_parts++] = dir_offset; @@ -407,7 +409,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) /* This function is always called when de->attr != VFAT_LN_ATTR is found, to * retrieve the previously constructed LFN. */ -char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) +char *lfn_get(DIR_ENT * de, off_t * lfn_offset) { char *lfn; uint8_t sum; @@ -464,10 +466,8 @@ char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) } } - for (sum = 0, i = 0; i < 8; i++) + for (sum = 0, i = 0; i < MSDOS_NAME; i++) sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->name[i]; - for (i = 0; i < 3; i++) - sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->ext[i]; if (sum != lfn_checksum) { /* checksum doesn't match, long name doesn't apply to this alias */ /* Causes: 1) alias renamed */ @@ -517,6 +517,7 @@ void lfn_check_orphaned(void) long_name = CNV_PARTS_SO_FAR(); printf("Orphaned long file name part \"%s\"\n", long_name); + free(long_name); if (interactive) printf("1: Delete.\n2: Leave it.\n"); else @@ -26,14 +26,14 @@ void lfn_reset(void); /* Reset the state of the LFN parser. */ -void lfn_add_slot(DIR_ENT * de, loff_t dir_offset); +void lfn_add_slot(DIR_ENT * de, off_t dir_offset); /* Process a dir slot that is a VFAT LFN entry. */ -char *lfn_get(DIR_ENT * de, loff_t * lfn_offset); +char *lfn_get(DIR_ENT * de, off_t * lfn_offset); /* Retrieve the long name for the proper dir entry. */ void lfn_check_orphaned(void); -void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name); +void lfn_fix_checksum(off_t from, off_t to, const char *short_name); #endif diff --git a/src/mkfs.fat.c b/src/mkfs.fat.c index b38d116..8a320fd 100644 --- a/src/mkfs.fat.c +++ b/src/mkfs.fat.c @@ -6,6 +6,7 @@ Copyright (C) 1998 H. Peter Anvin <hpa@zytor.com> Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> + Copyright (C) 2015-2016 Andreas Bombe <aeb@debian.org> This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -47,17 +48,11 @@ #include "version.h" #include <fcntl.h> -#include <linux/hdreg.h> -#include <sys/mount.h> -#include <linux/fs.h> -#include <linux/fd.h> -#include <endian.h> -#include <mntent.h> #include <signal.h> #include <string.h> #include <stdio.h> #include <stdlib.h> -#include <sys/ioctl.h> +#include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include <unistd.h> @@ -65,14 +60,12 @@ #include <errno.h> #include <ctype.h> #include <stdint.h> -#include <endian.h> #include <getopt.h> +#include "endian_compat.h" #include "msdos_fs.h" +#include "device_info.h" -/* In earlier versions, an own llseek() was used, but glibc lseek() is - * sufficient (or even better :) for 64 bit offsets in the meantime */ -#define llseek lseek /* Constant definitions */ @@ -80,6 +73,7 @@ #define FALSE 0 #define TEST_BUFFER_BLOCKS 16 +#define BLOCK_SIZE 1024 #define HARD_SECTOR_SIZE 512 #define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE ) @@ -247,9 +241,11 @@ static int start_data_sector; /* Sector number for the start of the data area */ static int start_data_block; /* Block number for the start of the data area */ static unsigned char *fat; /* File allocation table */ static unsigned alloced_fat_length; /* # of FAT sectors we can keep in memory */ +static unsigned fat_entries; /* total entries in FAT table (including reserved) */ static unsigned char *info_sector; /* FAT32 info sector */ static struct msdos_dir_entry *root_dir; /* Root directory */ static int size_root_dir; /* Size of the root directory in bytes */ +static uint32_t num_sectors; /* Total number of sectors in device */ static int sectors_per_cluster = 0; /* Number of sectors per disk cluster */ static int root_dir_entries = 0; /* Number of root directory entries */ static char *blank_sector; /* Blank sector - all zeros */ @@ -274,10 +270,8 @@ static long do_check(char *buffer, int try, off_t current_block); static void alarm_intr(int alnum); static void check_blocks(void); static void get_list_blocks(char *filename); -static int valid_offset(int fd, loff_t offset); -static uint64_t count_blocks(char *filename, int *remainder); static void check_mount(char *device_name); -static void establish_params(int device_num, int size); +static void establish_params(struct device_info *info); static void setup_tables(void); static void write_tables(void); @@ -295,6 +289,10 @@ static void fatal_error(const char *fmt_string) static void mark_FAT_cluster(int cluster, unsigned int value) { + + if (cluster < 0 || cluster >= fat_entries) + die("Internal error: out of range cluster number in mark_FAT_cluster"); + switch (size_fat) { case 12: value &= 0x0fff; @@ -334,12 +332,11 @@ static void mark_FAT_cluster(int cluster, unsigned int value) static void mark_FAT_sector(int sector, unsigned int value) { - int cluster; + int cluster = (sector - start_data_sector) / (int)(bs.cluster_size) / + (sector_size / HARD_SECTOR_SIZE) + 2; - cluster = (sector - start_data_sector) / (int)(bs.cluster_size) / - (sector_size / HARD_SECTOR_SIZE); - if (cluster < 0) - die("Invalid cluster number in mark_FAT_sector: probably bug!"); + if (sector < start_data_sector || sector >= num_sectors) + die("Internal error: out of range sector number in mark_FAT_sector"); mark_FAT_cluster(cluster, value); } @@ -350,7 +347,7 @@ static long do_check(char *buffer, int try, off_t current_block) { long got; - if (llseek(dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */ + if (lseek(dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */ !=current_block * BLOCK_SIZE) die("seek failed during testing for blocks"); @@ -431,270 +428,180 @@ static void get_list_blocks(char *filename) int i; FILE *listfile; long blockno; + char *line = NULL; + size_t linesize = 0; + int lineno = 0; + char *end, *check; listfile = fopen(filename, "r"); if (listfile == (FILE *) NULL) die("Can't open file of bad blocks"); - while (!feof(listfile)) { - fscanf(listfile, "%ld\n", &blockno); - for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ - mark_sector_bad(blockno * SECTORS_PER_BLOCK + i); - badblocks++; - } - fclose(listfile); + while (1) { + lineno++; + ssize_t length = getline(&line, &linesize, listfile); + if (length < 0) { + if (errno == 0) /* end of file */ + break; - if (badblocks) - printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); -} + perror("getline"); + die("Error while reading bad blocks file"); + } -/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it - isn't valid or TRUE if it is */ + errno = 0; + blockno = strtol(line, &end, 10); -static int valid_offset(int fd, loff_t offset) -{ - char ch; + if (errno) { + fprintf(stderr, + "While converting bad block number in line %d: %s\n", + lineno, strerror(errno)); + die("Error in bad blocks file"); + } - if (llseek(fd, offset, SEEK_SET) < 0) - return FALSE; - if (read(fd, &ch, 1) < 1) - return FALSE; - return TRUE; -} + check = end; + while (*check) { + if (!isspace(*check)) { + fprintf(stderr, + "Badly formed number in bad blocks file line %d\n", + lineno); + die("Error in bad blocks file"); + } -/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */ + check++; + } -static uint64_t count_blocks(char *filename, int *remainder) -{ - loff_t high, low; - int fd; + /* ignore empty or white space only lines */ + if (end == line) + continue; - if ((fd = open(filename, O_RDONLY)) < 0) { - perror(filename); - exit(1); - } + /* Mark all of the sectors in the block as bad */ + for (i = 0; i < SECTORS_PER_BLOCK; i++) { + unsigned long sector = blockno * SECTORS_PER_BLOCK + i; - /* first try SEEK_END, which should work on most devices nowadays */ - if ((low = llseek(fd, 0, SEEK_END)) <= 0) { - low = 0; - for (high = 1; valid_offset(fd, high); high *= 2) - low = high; - while (low < high - 1) { - const loff_t mid = (low + high) / 2; - if (valid_offset(fd, mid)) - low = mid; - else - high = mid; + if (sector < start_data_sector) { + fprintf(stderr, "Block number %ld is before data area\n", + blockno); + die("Error in bad blocks file"); + } + + if (sector >= num_sectors) { + fprintf(stderr, "Block number %ld is behind end of filesystem\n", + blockno); + die("Error in bad blocks file"); + } + + mark_sector_bad(sector); } - ++low; + badblocks++; } + fclose(listfile); + free(line); - close(fd); - *remainder = (low % BLOCK_SIZE) / sector_size; - return (low / BLOCK_SIZE); + if (badblocks) + printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); } /* Check to see if the specified device is currently mounted - abort if it is */ static void check_mount(char *device_name) { - FILE *f; - struct mntent *mnt; - - if ((f = setmntent(MOUNTED, "r")) == NULL) - return; - while ((mnt = getmntent(f)) != NULL) - if (strcmp(device_name, mnt->mnt_fsname) == 0) - die("%s contains a mounted filesystem."); - endmntent(f); + if (is_device_mounted(device_name)) + die("%s contains a mounted filesystem."); } /* Establish the geometry and media parameters for the device */ -static void establish_params(int device_num, int size) +static void establish_params(struct device_info *info) { - long loop_size; - struct hd_geometry geometry; - struct floppy_struct param; + unsigned int sec_per_track = 63; + unsigned int heads = 255; + unsigned int media = 0xf8; + unsigned int cluster_size = 4; /* starting point for FAT12 and FAT16 */ int def_root_dir_entries = 512; - if ((0 == device_num) || ((device_num & 0xff00) == 0x0200)) - /* file image or floppy disk */ - { - if (0 == device_num) { - param.size = size / 512; - switch (param.size) { + if (info->type != TYPE_FIXED) { + /* enter default parameters for floppy disks if the size matches */ + switch (info->size / 1024) { + case 360: + sec_per_track = 9; + heads = 2; + media = 0xfd; + cluster_size = 2; + def_root_dir_entries = 112; + break; + case 720: - param.sect = 9; - param.head = 2; + sec_per_track = 9; + heads = 2; + media = 0xf9; + cluster_size = 2; + def_root_dir_entries = 112; break; - case 1440: - param.sect = 9; - param.head = 2; + + case 1200: + sec_per_track = 15; + heads = 2; + media = 0xf9; + cluster_size = (atari_format ? 2 : 1); + def_root_dir_entries = 224; break; - case 2400: - param.sect = 15; - param.head = 2; + + case 1440: + sec_per_track = 18; + heads = 2; + media = 0xf0; + cluster_size = (atari_format ? 2 : 1); + def_root_dir_entries = 224; break; + case 2880: - param.sect = 18; - param.head = 2; + sec_per_track = 36; + heads = 2; + media = 0xf0; + cluster_size = 2; + def_root_dir_entries = 224; break; - case 5760: - param.sect = 36; - param.head = 2; - break; - default: - /* fake values */ - param.sect = 32; - param.head = 64; - break; - } - - } else { /* is a floppy diskette */ - - if (ioctl(dev, FDGETPRM, ¶m)) /* Can we get the diskette geometry? */ - die("unable to get diskette geometry for '%s'"); - } - bs.secs_track = htole16(param.sect); /* Set up the geometry information */ - bs.heads = htole16(param.head); - switch (param.size) { /* Set up the media descriptor byte */ - case 720: /* 5.25", 2, 9, 40 - 360K */ - bs.media = (char)0xfd; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 1440: /* 3.5", 2, 9, 80 - 720K */ - bs.media = (char)0xf9; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 2400: /* 5.25", 2, 15, 80 - 1200K */ - bs.media = (char)0xf9; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - case 5760: /* 3.5", 2, 36, 80 - 2880K */ - bs.media = (char)0xf0; - bs.cluster_size = (char)2; - def_root_dir_entries = 224; - break; - - case 2880: /* 3.5", 2, 18, 80 - 1440K */ -floppy_default: - bs.media = (char)0xf0; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - default: /* Anything else */ - if (0 == device_num) - goto def_hd_params; - else - goto floppy_default; } - } else if ((device_num & 0xff00) == 0x0700) { /* This is a loop device */ - if (ioctl(dev, BLKGETSIZE, &loop_size)) - die("unable to get loop device size"); - - switch (loop_size) { /* Assuming the loop device -> floppy later */ - case 720: /* 5.25", 2, 9, 40 - 360K */ - bs.secs_track = le16toh(9); - bs.heads = le16toh(2); - bs.media = (char)0xfd; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 1440: /* 3.5", 2, 9, 80 - 720K */ - bs.secs_track = le16toh(9); - bs.heads = le16toh(2); - bs.media = (char)0xf9; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 2400: /* 5.25", 2, 15, 80 - 1200K */ - bs.secs_track = le16toh(15); - bs.heads = le16toh(2); - bs.media = (char)0xf9; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - case 5760: /* 3.5", 2, 36, 80 - 2880K */ - bs.secs_track = le16toh(36); - bs.heads = le16toh(2); - bs.media = (char)0xf0; - bs.cluster_size = (char)2; - bs.dir_entries[0] = (char)224; - bs.dir_entries[1] = (char)0; - break; + } - case 2880: /* 3.5", 2, 18, 80 - 1440K */ - bs.secs_track = le16toh(18); - bs.heads = le16toh(2); - bs.media = (char)0xf0; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; + if (!size_fat && info->size >= 512 * 1024 * 1024) { + if (verbose) + printf("Auto-selecting FAT32 for large filesystem\n"); + size_fat = 32; + } + if (size_fat == 32) { + /* + * For FAT32, try to do the same as M$'s format command + * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): + * fs size <= 260M: 0.5k clusters + * fs size <= 8G: 4k clusters + * fs size <= 16G: 8k clusters + * fs size <= 32G: 16k clusters + * fs size > 32G: 32k clusters + * + * This only works correctly for 512 byte sectors! + */ + uint32_t sz_mb = info->size / (1024 * 1024); + cluster_size = + sz_mb > 32 * 1024 ? 64 : sz_mb > 16 * 1024 ? 32 : sz_mb > + 8 * 1024 ? 16 : sz_mb > 260 ? 8 : 1; + } - default: /* Anything else: default hd setup */ - printf("Loop device does not match a floppy size, using " - "default hd params\n"); - bs.secs_track = htole16(32); /* these are fake values... */ - bs.heads = htole16(64); - goto def_hd_params; - } - } else - /* Must be a hard disk then! */ - { - /* Can we get the drive geometry? (Note I'm not too sure about */ - /* whether to use HDIO_GETGEO or HDIO_REQ) */ - if (ioctl(dev, HDIO_GETGEO, &geometry) || geometry.sectors == 0 - || geometry.heads == 0) { - printf("unable to get drive geometry, using default 255/63\n"); - bs.secs_track = htole16(63); - bs.heads = htole16(255); - } else { - bs.secs_track = htole16(geometry.sectors); /* Set up the geometry information */ - bs.heads = htole16(geometry.heads); - if (!hidden_sectors_by_user) - hidden_sectors = htole32(geometry.start); - } -def_hd_params: - bs.media = (char)0xf8; /* Set up the media descriptor for a hard drive */ - if (!size_fat && blocks * SECTORS_PER_BLOCK > 1064960) { - if (verbose) - printf("Auto-selecting FAT32 for large filesystem\n"); - size_fat = 32; - } - if (size_fat == 32) { - /* For FAT32, try to do the same as M$'s format command - * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): - * fs size <= 260M: 0.5k clusters - * fs size <= 8G: 4k clusters - * fs size <= 16G: 8k clusters - * fs size <= 32G: 16k clusters - * fs size > 32G: 32k clusters - */ - uint32_t sz_mb = - (blocks + (1 << (20 - BLOCK_SIZE_BITS)) - 1) >> (20 - - BLOCK_SIZE_BITS); - bs.cluster_size = - sz_mb > 32 * 1024 ? 64 : sz_mb > 16 * 1024 ? 32 : sz_mb > - 8 * 1024 ? 16 : sz_mb > 260 ? 8 : 1; - } else { - /* FAT12 and FAT16: start at 4 sectors per cluster */ - bs.cluster_size = (char)4; - } + if (info->geom_heads > 0) { + heads = info->geom_heads; + sec_per_track = info->geom_sectors; } + if (!hidden_sectors_by_user && info->geom_start >= 0) + hidden_sectors = htole32(info->geom_start); + if (!root_dir_entries) root_dir_entries = def_root_dir_entries; + + bs.secs_track = htole16(sec_per_track); + bs.heads = htole16(heads); + bs.media = media; + bs.cluster_size = cluster_size; } /* @@ -713,7 +620,6 @@ static unsigned int align_object(unsigned int sectors, unsigned int clustsize) static void setup_tables(void) { - unsigned num_sectors; unsigned cluster_count = 0, fat_length; struct tm *ctime; struct msdos_volume_info *vi = @@ -807,8 +713,15 @@ static void setup_tables(void) memcpy(&bs.hidden, &hidden, 2); } - num_sectors = - (long long)(blocks * BLOCK_SIZE / sector_size) + orphaned_sectors; + if ((long long)(blocks * BLOCK_SIZE / sector_size) + orphaned_sectors > + UINT32_MAX) { + printf("Warning: target too large, space at end will be left unused\n"); + num_sectors = UINT32_MAX; + blocks = (uint64_t)UINT32_MAX * sector_size / BLOCK_SIZE; + } else { + num_sectors = + (long long)(blocks * BLOCK_SIZE / sector_size) + orphaned_sectors; + } if (!atari_format) { unsigned fatdata1216; /* Sectors for FATs + data area (FAT12/16) */ @@ -837,7 +750,8 @@ static void setup_tables(void) maxclustsize = 128; do { - fatdata32 = num_sectors - reserved_sectors; + fatdata32 = num_sectors + - align_object(reserved_sectors, bs.cluster_size); fatdata1216 = fatdata32 - align_object(root_dir_sectors, bs.cluster_size); @@ -899,6 +813,7 @@ static void setup_tables(void) clust32 = ((long long)fatdata32 * sector_size + nr_fats * 8) / ((int)bs.cluster_size * sector_size + nr_fats * 4); fatlength32 = cdiv((clust32 + 2) * 4, sector_size); + fatlength32 = align_object(fatlength32, bs.cluster_size); /* Need to recalculate number of clusters, since the unused parts of the * FATS and data area together could make up space for an additional, * not really present cluster. */ @@ -985,6 +900,10 @@ static void setup_tables(void) die("FAT not 12, 16 or 32 bits"); } + /* Adjust the reserved number of sectors for alignment */ + reserved_sectors = align_object(reserved_sectors, bs.cluster_size); + bs.reserved = htole16(reserved_sectors); + /* Adjust the number of root directory entries to help enforce alignment */ if (align_structures) { root_dir_entries = align_object(root_dir_sectors, bs.cluster_size) @@ -1130,9 +1049,11 @@ static void setup_tables(void) else die("Attempting to create a too large filesystem"); } + fat_entries = cluster_count + 2; /* The two following vars are in hard sectors, i.e. 512 byte sectors! */ - start_data_sector = (reserved_sectors + nr_fats * fat_length) * + start_data_sector = (reserved_sectors + nr_fats * fat_length + + cdiv(root_dir_entries * 32, sector_size)) * (sector_size / HARD_SECTOR_SIZE); start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK; @@ -1209,10 +1130,9 @@ static void setup_tables(void) } memset(root_dir, 0, size_root_dir); - if (memcmp(volume_name, NO_NAME, 11)) { + if (memcmp(volume_name, NO_NAME, MSDOS_NAME)) { struct msdos_dir_entry *de = &root_dir[0]; - memcpy(de->name, volume_name, 8); - memcpy(de->ext, volume_name + 8, 3); + memcpy(de->name, volume_name, MSDOS_NAME); de->attr = ATTR_VOLUME; if (!invariant) ctime = localtime(&create_time); @@ -1277,8 +1197,8 @@ static void setup_tables(void) #define seekto(pos,errstr) \ do { \ - loff_t __pos = (pos); \ - if (llseek (dev, __pos, SEEK_SET) != __pos) \ + off_t __pos = (pos); \ + if (lseek (dev, __pos, SEEK_SET) != __pos) \ error ("seek to " errstr " failed whilst writing tables"); \ } while(0) @@ -1388,12 +1308,11 @@ int main(int argc, char **argv) char *tmp; char *listfile = NULL; FILE *msgfile; - struct stat statbuf; + struct device_info devinfo; int i = 0, pos, ch; int create = 0; uint64_t cblocks = 0; - int min_sector_size; - int bad_block_count = 0; + int blocks_specified = 0; struct timeval create_timeval; enum {OPT_HELP=1000, OPT_INVARIANT,}; @@ -1412,7 +1331,7 @@ int main(int argc, char **argv) gettimeofday(&create_timeval, NULL); create_time = create_timeval.tv_sec; - volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ + volume_id = (uint32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ check_atari(); printf("mkfs.fat " VERSION " (" VERSION_DATE ")\n"); @@ -1641,38 +1560,34 @@ int main(int argc, char **argv) printf("Unknown option: %c\n", c); usage(1); } - if (optind < argc) { - device_name = argv[optind]; /* Determine the number of blocks in the FS */ - if (!device_name) { - printf("No device specified.\n"); + if (optind == argc || !argv[optind]) { + printf("No device specified.\n"); + usage(1); + } + + device_name = argv[optind++]; + + if (optind != argc) { + blocks_specified = 1; + blocks = strtoull(argv[optind], &tmp, 0); + + if (*tmp) { + printf("Bad block count : %s\n", argv[optind]); usage(1); } - if (!create) - cblocks = count_blocks(device_name, &orphaned_sectors); /* Have a look and see! */ - } - if (optind == argc - 2) { /* Either check the user specified number */ - blocks = strtoull(argv[optind + 1], &tmp, 0); - if (!create && blocks != cblocks) { - fprintf(stderr, "Warning: block count mismatch: "); - fprintf(stderr, "found %llu but assuming %llu.\n", (unsigned long long)cblocks, (unsigned long long)blocks); - } - if (*tmp) - bad_block_count = 1; - } else if (optind == argc - 1) { /* Or use value found */ - if (create) - die("Need intended size with -C."); - blocks = cblocks; - } else { - fprintf(stderr, "No device specified!\n"); - usage(1); + optind++; } - if (bad_block_count) { - printf("Bad block count : %s\n", argv[optind + 1]); + + if (optind != argc) { + fprintf(stderr, "Excess arguments on command line\n"); usage(1); } + if (create && !blocks_specified) + die("Need intended size with -C."); + if (check && listfile) /* Auto and specified bad block handling are mutually */ die("-c and -l are incompatible"); /* exclusive of each other! */ @@ -1698,47 +1613,58 @@ int main(int argc, char **argv) die("unable to resize %s"); } - if (fstat(dev, &statbuf) < 0) - die("unable to stat %s"); - if (!S_ISBLK(statbuf.st_mode)) { - statbuf.st_rdev = 0; - check = 0; - } else - /* - * Ignore any 'full' fixed disk devices, if -I is not given. - * On a MO-disk one doesn't need partitions. The filesytem can go - * directly to the whole disk. Under other OSes this is known as - * the 'superfloppy' format. As I don't know how to find out if - * this is a MO disk I introduce a -I (ignore) switch. -Joey - */ - if (!ignore_full_disk && ((statbuf.st_rdev & 0xffffff3f) == 0x0300 || /* hda, hdb */ - (statbuf.st_rdev & 0xffffff0f) == 0x0800 || /* sd */ - (statbuf.st_rdev & 0xffffff3f) == 0x0d00 || /* xd */ - (statbuf.st_rdev & 0xffffff3f) == 0x1600) /* hdc, hdd */ - ) + if (get_device_info(dev, &devinfo) < 0) + die("error collecting information about %s"); + + if (devinfo.size <= 0) + die("unable to discover size of %s"); + + if (devinfo.sector_size > 0) + sector_size = devinfo.sector_size; + + cblocks = devinfo.size / BLOCK_SIZE; + orphaned_sectors = (devinfo.size % BLOCK_SIZE) / sector_size; + + if (blocks_specified) { + if (blocks != cblocks) { + fprintf(stderr, "Warning: block count mismatch: "); + fprintf(stderr, "found %llu but assuming %llu.\n", + (unsigned long long)cblocks, (unsigned long long)blocks); + } + } else { + blocks = cblocks; + } + + /* + * Ignore any 'full' fixed disk devices, if -I is not given. + */ + if (!ignore_full_disk && devinfo.type == TYPE_FIXED && + devinfo.partition == 0) die("Device partition expected, not making filesystem on entire device '%s' (use -I to override)"); - if (sector_size_set) { - if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) - if (sector_size < min_sector_size) { - sector_size = min_sector_size; + if (!ignore_full_disk && devinfo.has_children > 0) + die("Partitions or virtual mappings on device '%s', not making filesystem (use -I to override)"); + + if (devinfo.sector_size > 0) { + if (sector_size_set) { + if (sector_size < devinfo.sector_size) { + sector_size = devinfo.sector_size; fprintf(stderr, "Warning: sector size was set to %d (minimal for this device)\n", sector_size); } - } else { - if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) { - sector_size = min_sector_size; + } else { + sector_size = devinfo.sector_size; sector_size_set = 1; } } if (sector_size > 4096) fprintf(stderr, - "Warning: sector size is set to %d > 4096, such filesystem will not propably mount\n", + "Warning: sector size %d > 4096 is non-standard, filesystem may not be usable\n", sector_size); - establish_params(statbuf.st_rdev, statbuf.st_size); + establish_params(&devinfo); /* Establish the media parameters */ setup_tables(); /* Establish the filesystem tables */ diff --git a/src/msdos_fs.h b/src/msdos_fs.h index 54b2a34..d33a2e4 100644 --- a/src/msdos_fs.h +++ b/src/msdos_fs.h @@ -46,7 +46,7 @@ #define MSDOS_DOTDOT ".. " /* "..", padded to MSDOS_NAME chars */ struct msdos_dir_entry { - uint8_t name[8], ext[3]; /* name and extension */ + uint8_t name[MSDOS_NAME]; /* name including extension */ uint8_t attr; /* attribute bits */ uint8_t lcase; /* Case for base and extension */ uint8_t ctime_cs; /* Creation time, centiseconds (0-199) */ diff --git a/src/testdevinfo.c b/src/testdevinfo.c new file mode 100644 index 0000000..9c555ed --- /dev/null +++ b/src/testdevinfo.c @@ -0,0 +1,125 @@ +/* testdevinfo - Display device info findings for debugging + + Copyright (C) 2015 Andreas Bombe <aeb@debian.org> + + This program 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 3 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, see <http://www.gnu.org/licenses/>. +*/ + +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include "device_info.h" + + +int main(int argc, char **argv) +{ + struct device_info info; + int fd; + + if (argc != 2) { + printf("Usage: testdevinfo FILENAME\n"); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + perror("open device"); + return 1; + } + + device_info_verbose = 100; + get_device_info(fd, &info); + close(fd); + + printf("\nfound information:\n"); + + printf("device type: "); + switch (info.type) { + case TYPE_UNKNOWN: + printf("unknown\n"); + break; + + case TYPE_BAD: + printf("unusable\n"); + break; + + case TYPE_FILE: + printf("image file\n"); + break; + + case TYPE_VIRTUAL: + printf("virtual\n"); + break; + + case TYPE_REMOVABLE: + printf("removable\n"); + break; + + case TYPE_FIXED: + printf("fixed\n"); + break; + + default: + printf("internal error! invalid value\n"); + break; + } + + printf("is partition: "); + if (info.partition < 0) + printf("unknown\n"); + else if (info.partition == 0) + printf("no, full disk\n"); + else + printf("number %d\n", info.partition); + + printf("has children: "); + if (info.has_children < 0) + printf("unknown\n"); + else if (info.has_children == 0) + printf("no\n"); + else + printf("yes\n"); + + printf("heads: "); + if (info.geom_heads < 0) + printf("unknown\n"); + else + printf("%d\n", info.geom_heads); + + printf("sectors: "); + if (info.geom_sectors < 0) + printf("unknown\n"); + else + printf("%d\n", info.geom_sectors); + + printf("start: "); + if (info.geom_start < 0) + printf("unknown\n"); + else + printf("%ld\n", info.geom_start); + + printf("sector size: "); + if (info.sector_size < 0) + printf("unknown\n"); + else + printf("%d\n", info.sector_size); + + printf("size: "); + if (info.size < 0) + printf("unknown\n"); + else + printf("%lld\n", info.size); + + return 0; +} diff --git a/src/version.h b/src/version.h.in index f0716d3..1487fc5 100644 --- a/src/version.h +++ b/src/version.h.in @@ -1,4 +1,4 @@ -/* version.h +/* @configure_input@ Copyright (C) 1998-2005 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch> @@ -23,7 +23,7 @@ #ifndef _version_h #define _version_h -#define VERSION "3.0.28" -#define VERSION_DATE "2015-05-16" +#define VERSION "@PACKAGE_VERSION@" +#define VERSION_DATE "@RELEASE_DATE@" #endif |