dosfstools-2.11/Makefile0000644000410000021150000000500510214573605015535 0ustar romanroman00000000000000# # Makefile for dosfstools (mkdosfs and dosfsck) # CC = gcc CPP = $(CC) -E OPTFLAGS = -O2 -fomit-frame-pointer -D_FILE_OFFSET_BITS=64 WARNFLAGS = -Wall DEBUGFLAGS = CFLAGS = $(OPTFLAGS) $(WARNFLAGS) $(DEBUGFLAGS) LDFLAGS = PREFIX = SBINDIR = $(PREFIX)/sbin MANDIR = $(PREFIX)/usr/man/man8 .PHONY: clean distclean install depend .EXPORT_ALL_VARIABLES: all dep clean install: $(MAKE) -C mkdosfs $@ $(MAKE) -C dosfsck $@ distclean: $(MAKE) -C mkdosfs $@ $(MAKE) -C dosfsck $@ rm -f TAGS .#* .new* \#*# *~ TAGS: etags -d -T `find . -name '*.[ch]'` dist: binary tar tar: distclean cd ..; \ name="$(notdir $(shell pwd))"; \ namev="$$name-$(shell perl -ne 'print "$$1\n" if /VERSION.*"(\S+)"/;' version.h)"; \ mv $$name $$namev; \ tar cf $$namev.src.tar `find $$namev \( -name CVS -o -path $$namev/debian \) -prune -o ! -type d -print`; \ gzip -9f $$namev.src.tar; \ mv $$namev $$name binary: all doit=""; [ root = "`whoami`" ] || doit=sudo; $$doit $(MAKE) binary-sub cd tmp; \ name="$(notdir $(shell pwd))"; \ namev="$$name-$(shell perl -ne 'print "$$1\n" if /VERSION.*"(\S+)"/;' version.h)"; \ arch=`uname -m | sed 's/i.86/i386/'`; \ nameva=$$namev.$$arch.tar; \ tar cf ../../$$nameva * ; \ gzip -9f ../../$$nameva doit=""; [ root = "`whoami`" ] || doit=sudo; $$doit rm -rf tmp binary-sub: @[ root = "`whoami`" ] || (echo "Must be root for this!"; exit 1) mkdir -p tmp/$(SBINDIR) tmp/$(MANDIR) $(MAKE) install PREFIX=$(shell pwd)/tmp # usage: make diff OLDVER= diff: @if [ "x$(OLDVER)" = "x" ]; then \ echo "Usage: make diff OLDVER="; \ exit 1; \ fi; \ name="$(notdir $(shell pwd))"; \ namev="$$name-$(shell perl -ne 'print "$$1\n" if /VERSION.*"(\S+)"/;' version.h)"; \ cvs diff -u -rRELEASE-$(OLDVER) >../$$namev.diff; \ gzip -9f ../$$namev.diff # usage: make release VER= release: @if [ "x$(VER)" = "x" ]; then \ echo "Usage: make release VER="; \ exit 1; \ fi if [ -d CVS ]; then \ modified=`cvs status 2>/dev/null | awk '/Status:/ { if ($$4 != "Up-to-date") print $$2 }'`; \ if [ "x$$modified" != "x" ]; then \ echo "There are modified files: $$modified"; \ echo "Commit first"; \ exit 1; \ fi; \ fi sed "/VERSION/s/\".*\"/\"$(VER)\"/" version.h.tmp date="`date +'%d %b %Y'`"; sed "/VERSION_DATE/s/\".*\"/\"$$date\"/" version.h rm version.h.tmp if [ -d CVS ]; then \ cvs commit -m"Raised version to $(VER)" version.h; \ cvs tag -c -F RELEASE-`echo $(VER) | sed 's/\./-/g'`; \ fi dosfstools-2.11/CHANGES0000644000410000021150000001477710214612416015102 0ustar romanroman00000000000000version 2.11 ============ - all: don't use own llseek() anymore, glibc lseek() does everything we need - dosfsck: lfn.c: avoid segfault - dosfsck: check.c, lfn.c: check for orphaned LFN slots - dosfsck: check.c alloc_rootdir_entry(): set owner of newly alloced clusters - dosfsck: dosfsck.h: better use for byte swapping - dosfsck: io.c: added code for real DOS - mkdosfs: raised FAT12_THRESHOLD from 4078 to 4085, introduced MIN_CLUST_32 - mkdosfs: fix loop device size - mkdosfs: by default, use FAT32 on devices >= 512MB - mkdosfs: fix a memory leak (blank_sector) - mkdosfs: fix parsing of number of blocks on command line, so that numbers >2G can be used - mkdosfs: add 'b' to getopt() string so this option can be used :) - mkdosfs: fix parsing of -i arg (should be unsigned) - mkdosfs: change default permissions of created images (-C) to 0666 & ~umask - mkdosfs: relax geometry check: if HDIO_GETGEO fails, print a warning and default to H=255,S=63 - dosfsck: new option -n (no-op): just check non-interactively, but don't write anything to filesystem - A few #include changes to support compilation with linux 2.6 headers (thanks to Jim Gifford ) - dosfsck: remove directory entries pointing to start cluster 0, if they're not "." or ".." entries that should actually point to the root dir (pointed out by Thomas Winkler ) - mkdosfs: new option -h to set number of hidden sectors (thanks to Godwin Stewart ) - all: updated my mail address everywhere... version 2.10 ============ - dosfsck: various 64-bit fixes and removed some warnings by Michal Cihar - mkdosfs: better error message if called without parameters (also suggested by Michal) version 2.9 =========== - dosfsck: if EOF from stdin, exit with error code - dosfsck: Fix potential for "Internal error: next_cluster on bad cluster". - dosfsck: When clearing long file names, don't overwrite the dir entries with all zeros, but put 0xe5 into the first byte. Otherwise, some OSes stop reading the directory at that point... - dosfsck: in statistics printed by -v, fix 32bit overflow in number of data bytes. - dosfsck: fix an potential overflow in "too many clusters" check - dosfsck: fix 64bit problem in fat.c (Debian bug #152769) - dosfsck: allow FAT size > 32MB. - dosfsck: allow for only one FAT - dosfsck: with -v, also check that last sector of the filesystem can be read (in case a partition is smaller than the fs thinks) - mkdosfs: add note in manpage that creating bootable filesystems is not supported. - mkdosfs: better error message with pointer to -I if target is a full-disk device. version 2.8 =========== - dosfsck: Fixed endless loop whenever a volume label was present. version 2.7 =========== - dosfsck: Don't check volume label for bad characters, everything seems to be allowed there... Also ignore duplicate names where one of them is a volume label. version 2.6 =========== - mkdosfs: Added correct heads definition for 2.88M floppies if not created via loopback. - dosfsck: If boot sector and its backup are different (FAT32), offer to write the backup to sector 0. (tnx to Pavel Roskin for this) - For 64 bit alpha, struct bootsector in dosfsck.h must be defined with __attribute__((packed)). - mkdosfs now actually accepts -R option. (tnx to David Kerrawn) - Fixed typo in dosfsck boot.c (recognition of boot signature in FSINFO) - Various compilation fixes for 2.4 kernel headers and for ia64. version 2.5 =========== - The llseek() implementation for alpha didn't really work; fixed it. version 2.4 =========== - Fix compiling problem on alpha (made a silly typo...) version 2.3 =========== - mkdosfs: Fixed usage message (printed only "bad address"). - both: made man pages and usage statements more consistent. - both: fix llseek function for alpha. - dosfsck: fix reading of unaligned fields in boot sector for alpha. - dosfsck: fixed renaming of files (extension wasn't really written). version 2.2 =========== - Added dosfsck/COPYING, putting dosfsck officially under GPL (Werner and I agree that it should be GPL). - mkdosfs: Allow creation of a 16 bit FAT on filesystems that are too small for it if the user explicitly selected FAT16 (but a warning is printed). Formerly, you got the misleading error message "make the fs a bit smaller". - dosfsck: new option -y as synonym for -y; for compability with other fs checkers, which also accept this option. - dosfsck: Now prints messages similar to e2fsck: at start version and feature list; at end number of files (and directories) and number of used/total clusters. This makes the printouts of *fsck at boot time nicer. - dosfsck: -a (auto repair) now turns on -f (salvage files), too. -a should act as non-destructive as possible, so lost clusters should be assigned to files. Otherwise the data in them might be overwritten later. - dosfsck: Don't drop a directory with lots of bad entries in auto-repair mode for the same reason as above. - dosfsck: avoid deleting the whole FAT32 root dir if something is wrong with it (bad start cluster or the like). - general: also create symlinks {mkfs,fsck}.vfat.8 to the respective real man pages. version 2.1 =========== - Fix some forgotten loff_t's for filesystems > 4GB. (Thanks to ). - Fix typo in mkdosfs manpage. - Removed inclusion of from mkdosfs.c; it's unnecessary and caused problems in some environments. - Fix condition when to expect . and .. entries in a directory. (Was wrong for non-FAT32 if first entry in root dir was a directory also.) - Also create mkfs.vfat and fsck.vfat symlinks, so that also filesystems listed with type "vfat" in /etc/fstab can be automatically checked. version 2.0 =========== - merge of mkdosfs and dosfstools in one package - new maintainer: Roman Hodek - FAT32 support in both mkdosfs and dosfsck - VFAT (long filename) support in dosfsck - Support for Atari variant of MS-DOS filesystem in both tools - Working support for big-endian systems in both tools - Better support for loop devices in mkdosfs: usual floppy sizes are detected and media byte etc. set accordingly; if loop fs has no standard floppy size, use hd params (mainly by Giuliano Procida ) - Removed lots of gcc warnings - Fixed some minor calculation bugs in mkdosfs. For change logs previous to 2.0, see the CHANGES files in the subdirectories. dosfstools-2.11/README.Atari0000644000410000021150000000554006712331063016016 0ustar romanroman00000000000000 Atari format support ==================== Both mkdosfs and dosfsck now can also handle the Atari variation of the MS-DOS filesystem format. The Atari format has some minor differences, some caused by the different machine architecture (m68k), some being "historic" (Atari didn't change some things that M$ changed). Both tools automatically select Atari format if they run on an Atari. Additionally the -A switch toggles between Atari and MS-DOS format. I.e., on an Atari it selects plain DOS format, on any other machine it switches to Atari format. The differences are in detail: - Atari TOS doesn't like cluster sizes != 2, so the usual solution for bigger partitions was to increase the logical sector size. So mkdosfs can handle sector sizes != 512 now, you can also manually select it with the -S option. On filesystems larger than approx. 32 MB, the sector size is automatically increased (stead of the cluster size) to make the filesystem fit. mkdosfs will always use 2 sectors per cluster (also with the floppy standard configurations), except when directed otherwise on the command line. - From the docs, all values between 0xfff8 and 0xffff in the FAT mark an end-of-file. However, DOS usually uses 0xfff8 and Atari 0xffff. This seems to be only an consmetic difference. At least TOS doesn't complain about 0xffff EOF marks. Don't know what DOS thinks of 0xfff8 :-) Anyway, both tools use the EOF mark common to the system (DOS/Atari). - Something similar of the bad cluster marks: On Atari the FAT values 0xfff0 to 0xfff7 are used for this, under DOS only 0xfff7 (the others can be normal cluster numbers, allowing 7 more clusters :-) However, both systems usually mark with 0xfff7. Just dosfsck has to interpret 0xfff0...0xfff7 differently. - Some fields in the boot sector are interpreted differently. For example, Atari has a disk serial number (used to aid disk change detection) where DOS stores the system name; the 'hidden' field is 32 bit for DOS, but 16 bit for Atari, and there's no 'total_sect' field; the 12/16 bit FAT decision is different: it's not based on the number of clusters, but always FAT12 on floppies and FAT16 on hard disks. mkdosfs nows about these differences and constructs the boot sector accordingly. - In dosfsck, the boot sector differences also have to known, to not warn about things that are no error on Atari. In addition, most Atari formatting tools fill the 'tracks' and 'heads' fields with 0 for hard disks, because they're meaningless on SCSI disks (Atari has/had no IDE). Due to this, the check that they should be non-zero is switched off. - Under Atari TOS, some other characters are illegal in filenames: '<', '>', '|', '"', and ':' are allowed, but all non-ASCII chars (codes >= 128) are forbidden. - Roman dosfstools-2.11/TODO0000644000410000021150000000105007733642750014573 0ustar romanroman00000000000000 -*- mode: indented-text -*- - dosfsck: Better checking of file times: ctime <= mtime <= atime - mkdosfs: If /etc/bootsect.dos (or similar) exists, use it as a template for generating boot sectors. This way, you can, e.g., make bootable DOS disks. Addendum: Don't know if that's so wise... There are really many variants of DOS/Windows bootcode out in the wild, and the code is proprietary, too. - dosfsck: read-only sector test (-t without -a or -r); just print out errors. dosfstools-2.11/version.h0000644000410000021150000000017610214614507015734 0ustar romanroman00000000000000#ifndef _version_h #define _version_h #define VERSION "2.11" #define VERSION_DATE "12 Mar 2005" #endif /* _version_h */ dosfstools-2.11/dosfsck/.cvsignore0000644000410000021150000000001006523125173017521 0ustar romanroman00000000000000dosfsck dosfstools-2.11/dosfsck/CHANGES0000644000410000021150000000063506351731742016535 0ustar romanroman00000000000000Changes from version 0 to 1 =========================== - fixed an off-by-two error in check.c:check_file - fixed marking clusters bad in fat.c:set_fat - fat.c:reclaim_free was also reclaiming bad clusters. - fixed many incorrect byte sex conversions in check.c and fat.c - -t and -w now require -a or -r - added option -d to drop files. - added option -u to try to "undelete" non-directory files. dosfstools-2.11/dosfsck/COPYING0000644000410000021150000004337606727140753016611 0ustar romanroman00000000000000The license below applies to dosfsck, which is copyrighted by Werner Almesberger and Roman Hodek . GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. dosfstools-2.11/dosfsck/Makefile0000644000410000021150000000215306740410517017173 0ustar romanroman00000000000000 OBJECTS = boot.o check.o common.o dosfsck.o fat.o file.o io.o lfn.o all: dosfsck dosfsck: $(OBJECTS) $(CC) -o $@ $(LDFLAGS) $^ .c.o: $(CC) -c $(CFLAGS) $*.c install: dosfsck mkdir -p $(SBINDIR) $(MANDIR) install -m 755 dosfsck $(SBINDIR) install -m 644 dosfsck.8 $(MANDIR) rm -f $(SBINDIR)/fsck.msdos rm -f $(SBINDIR)/fsck.vfat ln -s dosfsck $(SBINDIR)/fsck.msdos ln -s dosfsck $(SBINDIR)/fsck.vfat rm -f $(MANDIR)/fsck.msdos.8 ln -s dosfsck.8 $(MANDIR)/fsck.msdos.8 ln -s dosfsck.8 $(MANDIR)/fsck.vfat.8 clean: rm -f *.o *.s *.i *~ \#*# tmp_make .#* .new* distclean: clean rm -f *.a dosfsck dep: sed '/\#\#\# Dependencies/q' tmp_make $(CPP) $(CFLAGS) -MM *.c >>tmp_make mv tmp_make Makefile ### Dependencies boot.o: boot.c common.h dosfsck.h io.h boot.h check.o: check.c common.h dosfsck.h io.h fat.h file.h lfn.h check.h common.o: common.c common.h dosfsck.o: dosfsck.c common.h dosfsck.h io.h boot.h fat.h file.h \ check.h fat.o: fat.c common.h dosfsck.h io.h check.h fat.h file.o: file.c common.h file.h io.o: io.c dosfsck.h common.h io.h lfn.o: lfn.c common.h io.h dosfsck.h lfn.h file.h dosfstools-2.11/dosfsck/README0000644000410000021150000000451310017175240016406 0ustar romanroman00000000000000dosfsck, version 1 ================== WARNING: This is ALPHA test software. Use at your own risk. dosfsck is the Linux equivalent of PC/MS-DOS' CHKDSK. It checks the consistency of PC/MS-DOS file systems and optionally tries to repair them. The tests dosfsck performs are described in the man page. dosfsck needs header files from dosfs.9 (or later) to compile. Before using dosfsck to repair a file system that contains data of any value, you should verify that dosfsck is able to correct all reported errors. (Except fatal errors and those reported as unfixable, of course.) In order to do this, run it with the -V option, e.g. dosfsck -V /dev/sda1 (automatic check) or dosfsck -V -r /dev/sda1 (interactive check and repair) dosfsck will perform two passes: in the first pass, inconsistencies are detected and a list of changes to correct the problems is generated. In the second pass, those changes are applied whenever dosfsck reads data from disk. Hence no fixable errors should be reported in the second pass if the first pass was successful. Please notify the author if fixable errors are reported in the second pass. After verifying that dosfsck appears to be able to perform the desired operations, either confirm that you want the changes to be performed (if dosfsck was started with -r) or re-run dosfsck with the -a option (if it was started without -r). Please send bug reports, comments, flames, etc. to almesber@nessie.cs.id.ethz.ch or almesber@bernina.ethz.ch - Werner FAT32 and LFN support ===================== I've finally implemented some of the new features of MS-DOS filesystems: FAT32 and long filenames. FAT32 is automatically detected and of course the different FAT structure is handled. (Internally many changes were needed, so 32 bit variables for all cluster numbers and 64 bit vars for offsets inside the filesystem.) New checks for FAT32 are most notably on the backup boot sector and the new info sector. Also the possibility that the root directory resides in a cluster chain (instead of in a static area) on FAT32 is handled. dosfscheck also knows about VFAT long filenames now. It parses those names and uses them in listings etc. when available. There are also some checks on the (cruel) structure of how LFNs are stored and some attempts to fix problems. - Roman BTW, version 2 isn't ALPHA anymore :-) dosfstools-2.11/dosfsck/boot.c0000644000410000021150000003161410214574666016655 0ustar romanroman00000000000000/* boot.c - Read and analyze ia PC/MS-DOS boot sector */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include "common.h" #include "dosfsck.h" #include "io.h" #include "boot.h" #define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0) /* don't divide by zero */ static struct { __u8 media; char *descr; } mediabytes[] = { { 0xf0, "5.25\" or 3.5\" HD floppy" }, { 0xf8, "hard disk" }, { 0xf9, "3,5\" 720k floppy 2s/80tr/9sec or " "5.25\" 1.2M floppy 2s/80tr/15sec" }, { 0xfa, "5.25\" 320k floppy 1s/80tr/8sec" }, { 0xfb, "3.5\" 640k floppy 2s/80tr/8sec" }, { 0xfc, "5.25\" 180k floppy 1s/40tr/9sec" }, { 0xfd, "5.25\" 360k floppy 2s/40tr/9sec" }, { 0xfe, "5.25\" 160k floppy 1s/40tr/8sec" }, { 0xff, "5.25\" 320k floppy 2s/40tr/8sec" }, }; #if defined __alpha || defined __ia64__ || defined __s390x__ || defined __x86_64__ || defined __ppc64__ /* Unaligned fields must first be copied byte-wise */ #define GET_UNALIGNED_W(f) \ ({ \ unsigned short __v; \ memcpy( &__v, &f, sizeof(__v) ); \ CF_LE_W( *(unsigned short *)&f ); \ }) #else #define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f ) #endif static char *get_media_descr( unsigned char media ) { int i; for( i = 0; i < sizeof(mediabytes)/sizeof(*mediabytes); ++i ) { if (mediabytes[i].media == media) return( mediabytes[i].descr ); } return( "undefined" ); } static void dump_boot(DOS_FS *fs,struct boot_sector *b,unsigned lss) { unsigned short sectors; printf("Boot sector contents:\n"); if (!atari_format) { char id[9]; strncpy(id,b->system_id,8); id[8] = 0; printf("System ID \"%s\"\n",id); } else { /* On Atari, a 24 bit serial number is stored at offset 8 of the boot * sector */ printf("Serial number 0x%x\n", b->system_id[5] | (b->system_id[6]<<8) | (b->system_id[7]<<16)); } printf("Media byte 0x%02x (%s)\n",b->media,get_media_descr(b->media)); printf("%10d bytes per logical sector\n",GET_UNALIGNED_W(b->sector_size)); printf("%10d bytes per cluster\n",fs->cluster_size); printf("%10d reserved sector%s\n",CF_LE_W(b->reserved), CF_LE_W(b->reserved) == 1 ? "" : "s"); printf("First FAT starts at byte %llu (sector %llu)\n", (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); if (!fs->root_cluster) { printf("Root directory starts at byte %llu (sector %llu)\n", (unsigned long long)fs->root_start, (unsigned long long)fs->root_start/lss); printf("%10d root directory entries\n",fs->root_entries); } else { printf( "Root directory start at cluster %lu (arbitrary size)\n", fs->root_cluster); } 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",fs->clusters, (unsigned long long)fs->clusters*fs->cluster_size); printf("%u sectors/track, %u heads\n",CF_LE_W(b->secs_track), CF_LE_W(b->heads)); printf("%10u hidden sectors\n", atari_format ? /* On Atari, the hidden field is only 16 bit wide and unused */ (((unsigned char *)&b->hidden)[0] | ((unsigned char *)&b->hidden)[1] << 8) : CF_LE_L(b->hidden)); sectors = GET_UNALIGNED_W( b->sectors ); printf("%10u sectors total\n", sectors ? sectors : CF_LE_L(b->total_sect)); } static void check_backup_boot(DOS_FS *fs, struct boot_sector *b, int lss) { struct boot_sector b2; if (!fs->backupboot_start) { printf( "There is no backup boot sector.\n" ); if (CF_LE_W(b->reserved) < 3) { printf( "And there is no space for creating one!\n" ); return; } if (interactive) printf( "1) Create one\n2) Do without a backup\n" ); else printf( " Auto-creating backup boot block.\n" ); if (!interactive || get_key("12","?") == '1') { int bbs; /* The usual place for the backup boot sector is sector 6. Choose * that or the last reserved sector. */ if (CF_LE_W(b->reserved) >= 7 && CF_LE_W(b->info_sector) != 6) bbs = 6; else { bbs = CF_LE_W(b->reserved) - 1; if (bbs == CF_LE_W(b->info_sector)) --bbs; /* this is never 0, as we checked reserved >= 3! */ } fs->backupboot_start = bbs*lss; b->backup_boot = CT_LE_W(bbs); fs_write(fs->backupboot_start,sizeof(*b),b); fs_write((off_t)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; } else return; } fs_read(fs->backupboot_start,sizeof(b2),&b2); if (memcmp(b,&b2,sizeof(b2)) != 0) { /* there are any differences */ __u8 *p, *q; int i, pos, first = 1; char buf[20]; printf( "There are differences between boot sector and its backup.\n" ); printf( "Differences: (offset:original/backup)\n " ); pos = 2; for( p = (__u8 *)b, q = (__u8 *)&b2, i = 0; i < sizeof(b2); ++p, ++q, ++i ) { if (*p != *q) { sprintf( buf, "%s%u:%02x/%02x", first ? "" : ", ", (unsigned)(p-(__u8 *)b), *p, *q ); if (pos + strlen(buf) > 78) printf( "\n " ), pos = 2; printf( "%s", buf ); pos += strlen(buf); first = 0; } } printf( "\n" ); if (interactive) printf( "1) Copy original to backup\n" "2) Copy backup to original\n" "3) No action\n" ); else printf( " Not automatically fixing this.\n" ); switch (interactive ? get_key("123","?") : '3') { case '1': fs_write(fs->backupboot_start,sizeof(*b),b); break; case '2': fs_write(0,sizeof(b2),&b2); break; default: break; } } } static void init_fsinfo(struct info_sector *i) { i->magic = CT_LE_L(0x41615252); i->signature = CT_LE_L(0x61417272); i->free_clusters = CT_LE_L(-1); i->next_cluster = CT_LE_L(2); i->boot_sign = CT_LE_W(0xaa55); } static void read_fsinfo(DOS_FS *fs, struct boot_sector *b,int lss) { struct info_sector i; if (!b->info_sector) { printf( "No FSINFO sector\n" ); if (interactive) printf( "1) Create one\n2) Do without FSINFO\n" ); else printf( " Not automatically creating it.\n" ); if (interactive && get_key("12","?") == '1') { /* search for a free reserved sector (not boot sector and not * backup boot sector) */ __u32 s; for( s = 1; s < CF_LE_W(b->reserved); ++s ) if (s != CF_LE_W(b->backup_boot)) break; if (s > 0 && s < CF_LE_W(b->reserved)) { init_fsinfo(&i); fs_write((off_t)s*lss,sizeof(i),&i); b->info_sector = CT_LE_W(s); fs_write((off_t)offsetof(struct boot_sector,info_sector), sizeof(b->info_sector),&b->info_sector); if (fs->backupboot_start) fs_write(fs->backupboot_start+ offsetof(struct boot_sector,info_sector), sizeof(b->info_sector),&b->info_sector); } else { printf( "No free reserved sector found -- " "no space for FSINFO sector!\n" ); return; } } else return; } fs->fsinfo_start = CF_LE_W(b->info_sector)*lss; fs_read(fs->fsinfo_start,sizeof(i),&i); if (i.magic != CT_LE_L(0x41615252) || i.signature != CT_LE_L(0x61417272) || i.boot_sign != CT_LE_W(0xaa55)) { printf( "FSINFO sector has bad magic number(s):\n" ); if (i.magic != CT_LE_L(0x41615252)) printf( " Offset %llu: 0x%08x != expected 0x%08x\n", (unsigned long long)offsetof(struct info_sector,magic), CF_LE_L(i.magic),0x41615252); if (i.signature != CT_LE_L(0x61417272)) printf( " Offset %llu: 0x%08x != expected 0x%08x\n", (unsigned long long)offsetof(struct info_sector,signature), CF_LE_L(i.signature),0x61417272); if (i.boot_sign != CT_LE_W(0xaa55)) printf( " Offset %llu: 0x%04x != expected 0x%04x\n", (unsigned long long)offsetof(struct info_sector,boot_sign), CF_LE_W(i.boot_sign),0xaa55); if (interactive) printf( "1) Correct\n2) Don't correct (FSINFO invalid then)\n" ); else printf( " Auto-correcting it.\n" ); if (!interactive || get_key("12","?") == '1') { init_fsinfo(&i); fs_write(fs->fsinfo_start,sizeof(i),&i); } else fs->fsinfo_start = 0; } if (fs->fsinfo_start) fs->free_clusters = CF_LE_L(i.free_clusters); } void read_boot(DOS_FS *fs) { struct boot_sector b; unsigned total_sectors; unsigned short logical_sector_size, sectors; unsigned fat_length; off_t data_size; fs_read(0,sizeof(b),&b); logical_sector_size = GET_UNALIGNED_W(b.sector_size); if (!logical_sector_size) die("Logical sector size is zero."); fs->cluster_size = b.cluster_size*logical_sector_size; if (!fs->cluster_size) die("Cluster size is zero."); if (b.fats != 2 && b.fats != 1) die("Currently, only 1 or 2 FATs are supported, not %d.\n",b.fats); fs->nfats = b.fats; sectors = GET_UNALIGNED_W(b.sectors); total_sectors = sectors ? sectors : CF_LE_L(b.total_sect); 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((off_t)((total_sectors & ~1)-1)*(off_t)logical_sector_size, logical_sector_size); fat_length = CF_LE_W(b.fat_length) ? CF_LE_W(b.fat_length) : CF_LE_L(b.fat32_length); fs->fat_start = (off_t)CF_LE_W(b.reserved)*logical_sector_size; fs->root_start = ((off_t)CF_LE_W(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 = (off_t)total_sectors*logical_sector_size-fs->data_start; fs->clusters = data_size/fs->cluster_size; fs->root_cluster = 0; /* indicates standard, pre-FAT32 root dir */ fs->fsinfo_start = 0; /* no FSINFO structure */ fs->free_clusters = -1; /* unknown */ if (!b.fat_length && b.fat32_length) { fs->fat_bits = 32; fs->root_cluster = CF_LE_L(b.root_cluster); if (!fs->root_cluster && fs->root_entries) /* M$ hasn't specified this, but it looks reasonable: If * root_cluster is 0 but there is a separate root dir * (root_entries != 0), we handle the root dir the old way. Give a * warning, but convertig to a root dir in a cluster chain seems * to complex for now... */ printf( "Warning: FAT32 root dir not in cluster chain! " "Compability mode...\n" ); else if (!fs->root_cluster && !fs->root_entries) die("No root directory!"); else if (fs->root_cluster && fs->root_entries) printf( "Warning: FAT32 root dir is in a cluster chain, but " "a separate root dir\n" " area is defined. Cannot fix this easily.\n" ); fs->backupboot_start = CF_LE_W(b.backup_boot)*logical_sector_size; check_backup_boot(fs,&b,logical_sector_size); read_fsinfo(fs,&b,logical_sector_size); } 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 > MSDOS_FAT12) ? 16 : 12; } else { /* On Atari, things are more difficult: GEMDOS always uses 12bit FATs * on floppies, and always 16 bit on harddisks. */ 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))) fs->fat_bits = 12; } /* On FAT32, the high 4 bits of a FAT entry are reserved */ fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits; fs->fat_size = fat_length*logical_sector_size; if (fs->clusters > ((unsigned long long)fs->fat_size*8/fs->fat_bits)-2) die("File system has %d clusters but only space for %d FAT entries.", fs->clusters,((unsigned long long)fs->fat_size*8/fs->fat_bits)-2); if (!fs->root_entries && !fs->root_cluster) die("Root directory has zero size."); if (fs->root_entries & (MSDOS_DPS-1)) die("Root directory (%d entries) doesn't span an integral number of " "sectors.",fs->root_entries); if (logical_sector_size & (SECTOR_SIZE-1)) die("Logical sector size (%d bytes) is not a multiple of the physical " "sector size.",logical_sector_size); /* ++roman: On Atari, these two fields are often left uninitialized */ if (!atari_format && (!b.secs_track || !b.heads)) die("Invalid disk format in boot sector."); if (verbose) dump_boot(fs,&b,logical_sector_size); } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/boot.h0000644000410000021150000000037406351731742016656 0ustar romanroman00000000000000/* boot.h - Read and analyze ia PC/MS-DOS boot sector */ /* Written 1993 by Werner Almesberger */ #ifndef _BOOT_H #define _BOOT_H void read_boot(DOS_FS *fs); /* Reads the boot sector from the currently open device and initializes *FS */ #endif dosfstools-2.11/dosfsck/check.c0000644000410000021150000005627510214602773016770 0ustar romanroman00000000000000/* check.c - Check and repair a PC/MS-DOS file system */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include #include "common.h" #include "dosfsck.h" #include "io.h" #include "fat.h" #include "file.h" #include "lfn.h" #include "check.h" static DOS_FILE *root; /* get start field of a dir entry */ #define FSTART(p,fs) \ ((unsigned long)CF_LE_W(p->dir_ent.start) | \ (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0)) #define MODIFY(p,i,v) \ do { \ if (p->offset) { \ p->dir_ent.i = v; \ fs_write(p->offset+offsetof(DIR_ENT,i), \ sizeof(p->dir_ent.i),&p->dir_ent.i); \ } \ } while(0) #define MODIFY_START(p,v,fs) \ do { \ unsigned long __v = (v); \ if (!p->offset) { \ /* writing to fake entry for FAT32 root dir */ \ if (!__v) die("Oops, deleting FAT32 root dir!"); \ fs->root_cluster = __v; \ p->dir_ent.start = CT_LE_W(__v&0xffff); \ p->dir_ent.starthi = CT_LE_W(__v>>16); \ __v = CT_LE_L(__v); \ fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \ sizeof(((struct boot_sector *)0)->root_cluster), \ &__v); \ } \ else { \ MODIFY(p,start,CT_LE_W((__v)&0xffff)); \ if (fs->fat_bits == 32) \ MODIFY(p,starthi,CT_LE_W((__v)>>16)); \ } \ } while(0) loff_t alloc_rootdir_entry(DOS_FS *fs, DIR_ENT *de, const char *pattern) { static int curr_num = 0; loff_t offset; if (fs->root_cluster) { DIR_ENT d2; int i = 0, got = 0; unsigned long clu_num, prev = 0; loff_t offset2; clu_num = fs->root_cluster; offset = cluster_start(fs,clu_num); while (clu_num > 0 && clu_num != -1) { fs_read(offset,sizeof(DIR_ENT),&d2); if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) { got = 1; break; } i += sizeof(DIR_ENT); offset += sizeof(DIR_ENT); if ((i % fs->cluster_size) == 0) { prev = clu_num; if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1) break; offset = cluster_start(fs,clu_num); } } if (!got) { /* no free slot, need to extend root dir: alloc next free cluster * after previous one */ if (!prev) die("Root directory has no cluster allocated!"); for (clu_num = prev+1; clu_num != prev; clu_num++) { if (clu_num >= fs->clusters+2) clu_num = 2; if (!fs->fat[clu_num].value) break; } if (clu_num == prev) die("Root directory full and no free cluster"); set_fat(fs,prev,clu_num); set_fat(fs,clu_num,-1); set_owner(fs, clu_num, get_owner(fs, fs->root_cluster)); /* clear new cluster */ memset( &d2, 0, sizeof(d2) ); offset = cluster_start(fs,clu_num); for( i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT) ) fs_write( offset+i, sizeof(d2), &d2 ); } memset(de,0,sizeof(DIR_ENT)); while (1) { sprintf(de->name,pattern,curr_num); clu_num = fs->root_cluster; i = 0; offset2 = cluster_start(fs,clu_num); while (clu_num > 0 && clu_num != -1) { fs_read(offset2,sizeof(DIR_ENT),&d2); if (offset2 != offset && !strncmp(d2.name,de->name,MSDOS_NAME)) break; i += sizeof(DIR_ENT); offset2 += sizeof(DIR_ENT); if ((i % fs->cluster_size) == 0) { if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1) break; offset2 = cluster_start(fs,clu_num); } } if (clu_num == 0 || clu_num == -1) break; if (++curr_num >= 10000) die("Unable to create unique name"); } } else { DIR_ENT *root; int next_free = 0, scan; root = alloc(fs->root_entries*sizeof(DIR_ENT)); fs_read(fs->root_start,fs->root_entries*sizeof(DIR_ENT),root); while (next_free < fs->root_entries) if (IS_FREE(root[next_free].name) && root[next_free].attr != VFAT_LN_ATTR) break; else next_free++; if (next_free == fs->root_entries) die("Root directory is full."); offset = fs->root_start+next_free*sizeof(DIR_ENT); memset(de,0,sizeof(DIR_ENT)); while (1) { sprintf(de->name,pattern,curr_num); for (scan = 0; scan < fs->root_entries; scan++) if (scan != next_free && !strncmp(root[scan].name,de->name,MSDOS_NAME)) break; if (scan == fs->root_entries) break; if (++curr_num >= 10000) die("Unable to create unique name"); } free(root); } ++n_files; return offset; } static char *path_name(DOS_FILE *file) { static char path[PATH_MAX*2]; if (!file) *path = 0; else { if (strlen(path_name(file->parent)) > PATH_MAX) die("Path name too long."); if (strcmp(path,"/") != 0) strcat(path,"/"); strcpy(strrchr(path,0),file->lfn?file->lfn:file_name(file->dir_ent.name)); } 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 */ /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ time_t date_dos2unix(unsigned short time,unsigned short date) { int month,year; time_t secs; month = ((date >> 5) & 15)-1; year = date >> 9; secs = (time & 31)*2+60*((time >> 5) & 63)+(time >> 11)*3600+86400* ((date & 31)-1+day_n[month]+(year/4)+year*365-((year & 3) == 0 && month < 2 ? 1 : 0)+3653); /* days since 1.1.70 plus 80's leap day */ return secs; } static char *file_stat(DOS_FILE *file) { static char temp[100]; struct tm *tm; char tmp[100]; time_t date; date = date_dos2unix(CF_LE_W(file->dir_ent.time),CF_LE_W(file-> dir_ent.date)); tm = localtime(&date); strftime(tmp,99,"%H:%M:%S %b %d %Y",tm); sprintf(temp," Size %u bytes, date %s",CF_LE_L(file->dir_ent.size),tmp); return temp; } static int bad_name(unsigned char *name) { int i, spc, suspicious = 0; char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; /* Do not complain about (and auto-correct) the extended attribute files * of OS/2. */ if (strncmp(name,"EA DATA SF",11) == 0 || strncmp(name,"WP ROOT SF",11) == 0) return 0; for (i = 0; i < 8; i++) { if (name[i] < ' ' || name[i] == 0x7f) return 1; if (name[i] > 0x7f) ++suspicious; if (strchr(bad_chars,name[i])) return 1; } for (i = 8; i < 11; i++) { if (name[i] < ' ' || name[i] == 0x7f) return 1; if (name[i] > 0x7f) ++suspicious; if (strchr(bad_chars,name[i])) return 1; } spc = 0; for (i = 0; i < 8; i++) { if (name[i] == ' ') spc = 1; else if (spc) /* non-space after a space not allowed, space terminates the name * part */ return 1; } spc = 0; for (i = 8; i < 11; i++) { if (name[i] == ' ') spc = 1; else if (spc) /* non-space after a space not allowed, space terminates the name * part */ return 1; } /* Under GEMDOS, chars >= 128 are never allowed. */ if (atari_format && suspicious) return 1; /* Only complain about too much suspicious chars in interactive mode, * never correct them automatically. The chars are all basically ok, so we * shouldn't auto-correct such names. */ if (interactive && suspicious > 6) return 1; return 0; } static void drop_file(DOS_FS *fs,DOS_FILE *file) { unsigned long cluster; MODIFY(file,name[0],DELETED_FLAG); for (cluster = FSTART(file,fs); cluster > 0 && cluster < fs->clusters+2; cluster = next_cluster(fs,cluster)) set_owner(fs,cluster,NULL); --n_files; } static void truncate_file(DOS_FS *fs,DOS_FILE *file,unsigned long clusters) { int deleting; unsigned long walk,next,prev; walk = FSTART(file,fs); prev = 0; if ((deleting = !clusters)) MODIFY_START(file,0,fs); while (walk > 0 && walk != -1) { next = next_cluster(fs,walk); if (deleting) set_fat(fs,walk,0); else if ((deleting = !--clusters)) set_fat(fs,walk,-1); prev = walk; walk = next; } } static void auto_rename(DOS_FILE *file) { DOS_FILE *first,*walk; int number; if (!file->offset) return; /* cannot rename FAT32 root dir */ first = file->parent ? file->parent->first : root; number = 0; while (1) { sprintf(file->dir_ent.name,"FSCK%04d",number); strncpy(file->dir_ent.ext,"REN",3); for (walk = first; walk; walk = walk->next) if (walk != file && !strncmp(walk->dir_ent.name,file->dir_ent. name,MSDOS_NAME)) break; if (!walk) { fs_write(file->offset,MSDOS_NAME,file->dir_ent.name); return; } number++; } die("Can't generate a unique name."); } static void rename_file(DOS_FILE *file) { unsigned char name[46]; unsigned char *walk,*here; if (!file->offset) { printf( "Cannot rename FAT32 root dir\n" ); return; /* cannot rename FAT32 root dir */ } while (1) { printf("New name: "); fflush(stdout); if (fgets(name,45,stdin)) { if ((here = strchr(name,'\n'))) *here = 0; for (walk = strrchr(name,0); walk >= name && (*walk == ' ' || *walk == '\t'); walk--); walk[1] = 0; for (walk = name; *walk == ' ' || *walk == '\t'; walk++); if (file_cvt(walk,file->dir_ent.name)) { fs_write(file->offset,MSDOS_NAME,file->dir_ent.name); return; } } } } static int handle_dot(DOS_FS *fs,DOS_FILE *file,int dots) { char *name; name = strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME) ? ".." : "."; if (!(file->dir_ent.attr & ATTR_DIR)) { printf("%s\n Is a non-directory.\n",path_name(file)); if (interactive) printf("1) Drop it\n2) Auto-rename\n3) Rename\n" "4) Convert to directory\n"); else printf(" Auto-renaming it.\n"); switch (interactive ? get_key("1234","?") : '2') { case '1': drop_file(fs,file); return 1; case '2': auto_rename(file); printf(" Renamed to %s\n",file_name(file->dir_ent.name)); return 0; case '3': rename_file(file); return 0; case '4': MODIFY(file,size,CT_LE_L(0)); MODIFY(file,attr,file->dir_ent.attr | ATTR_DIR); break; } } if (!dots) { printf("Root contains directory \"%s\". Dropping it.\n",name); drop_file(fs,file); return 1; } return 0; } static int check_file(DOS_FS *fs,DOS_FILE *file) { DOS_FILE *owner; int restart; unsigned long expect,curr,this,clusters,prev,walk,clusters2; if (file->dir_ent.attr & ATTR_DIR) { if (CF_LE_L(file->dir_ent.size)) { printf("%s\n Directory has non-zero size. Fixing it.\n", path_name(file)); MODIFY(file,size,CT_LE_L(0)); } if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) { expect = FSTART(file->parent,fs); if (FSTART(file,fs) != expect) { printf("%s\n Start (%ld) does not point to parent (%ld)\n", path_name(file),FSTART(file,fs),expect); MODIFY_START(file,expect,fs); } return 0; } if (file->parent && !strncmp(file->dir_ent.name,MSDOS_DOTDOT, MSDOS_NAME)) { expect = file->parent->parent ? FSTART(file->parent->parent,fs):0; if (fs->root_cluster && expect == fs->root_cluster) expect = 0; if (FSTART(file,fs) != expect) { printf("%s\n Start (%lu) does not point to .. (%lu)\n", path_name(file),FSTART(file,fs),expect); MODIFY_START(file,expect,fs); } return 0; } if (FSTART(file,fs)==0){ printf ("%s\n Start does point to root directory. Deleting dir. \n", path_name(file)); MODIFY(file,name[0],DELETED_FLAG); return 0; } } if (FSTART(file,fs) >= fs->clusters+2) { printf("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n", path_name(file),FSTART(file,fs),fs->clusters+1); if (!file->offset) die( "Bad FAT32 root directory! (bad start cluster)\n" ); MODIFY_START(file,0,fs); } clusters = prev = 0; for (curr = FSTART(file,fs) ? FSTART(file,fs) : -1; curr != -1; curr = next_cluster(fs,curr)) { if (!fs->fat[curr].value || bad_cluster(fs,curr)) { printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n", path_name(file),fs->fat[curr].value ? "bad" : "free",curr); if (prev) set_fat(fs,prev,-1); else if (!file->offset) die( "FAT32 root dir starts with a bad cluster!" ); else MODIFY_START(file,0,fs); break; } if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <= clusters*fs->cluster_size) { printf("%s\n File size is %u bytes, cluster chain length is > %lu " "bytes.\n Truncating file to %u bytes.\n",path_name(file), CF_LE_L(file->dir_ent.size),clusters*fs->cluster_size, CF_LE_L(file->dir_ent.size)); truncate_file(fs,file,clusters); break; } if ((owner = get_owner(fs,curr))) { int do_trunc = 0; printf("%s and\n",path_name(owner)); printf("%s\n share clusters.\n",path_name(file)); clusters2 = 0; for (walk = FSTART(owner,fs); walk > 0 && walk != -1; walk = next_cluster(fs,walk)) if (walk == curr) break; else clusters2++; restart = file->dir_ent.attr & ATTR_DIR; if (!owner->offset) { printf( " Truncating second to %lu bytes because first " "is FAT32 root dir.\n", clusters2*fs->cluster_size ); do_trunc = 2; } else if (!file->offset) { printf( " Truncating first to %lu bytes because second " "is FAT32 root dir.\n", clusters*fs->cluster_size ); do_trunc = 1; } else if (interactive) printf("1) Truncate first to %lu bytes%s\n" "2) Truncate second to %lu bytes\n",clusters*fs->cluster_size, restart ? " and restart" : "",clusters2*fs->cluster_size); else printf(" Truncating second to %lu bytes.\n",clusters2* fs->cluster_size); if (do_trunc != 2 && (do_trunc == 1 || (interactive && get_key("12","?") == '1'))) { prev = 0; clusters = 0; for (this = FSTART(owner,fs); this > 0 && this != -1; this = next_cluster(fs,this)) { if (this == curr) { if (prev) set_fat(fs,prev,-1); else MODIFY_START(owner,0,fs); MODIFY(owner,size,CT_LE_L(clusters*fs->cluster_size)); if (restart) return 1; while (this > 0 && this != -1) { set_owner(fs,this,NULL); this = next_cluster(fs,this); } break; } clusters++; prev = this; } if (this != curr) die("Internal error: didn't find cluster %d in chain" " starting at %d",curr,FSTART(owner,fs)); } else { if (prev) set_fat(fs,prev,-1); else MODIFY_START(file,0,fs); break; } } set_owner(fs,curr,file); clusters++; prev = curr; } if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) > clusters*fs->cluster_size) { printf("%s\n File size is %u bytes, cluster chain length is %lu bytes." "\n Truncating file to %lu bytes.\n",path_name(file),CF_LE_L(file-> dir_ent.size),clusters*fs->cluster_size,clusters*fs->cluster_size); MODIFY(file,size,CT_LE_L(clusters*fs->cluster_size)); } return 0; } static int check_files(DOS_FS *fs,DOS_FILE *start) { while (start) { if (check_file(fs,start)) return 1; start = start->next; } return 0; } static int check_dir(DOS_FS *fs,DOS_FILE **root,int dots) { DOS_FILE *parent,**walk,**scan; int dot,dotdot,skip,redo; int good,bad; if (!*root) return 0; parent = (*root)->parent; good = bad = 0; for (walk = root; *walk; walk = &(*walk)->next) if (bad_name((*walk)->dir_ent.name)) bad++; else good++; if (*root && parent && good+bad > 4 && bad > good/2) { printf("%s\n Has a large number of bad entries. (%d/%d)\n", path_name(parent),bad,good+bad); if (!dots) printf( " Not dropping root directory.\n" ); else if (!interactive) printf(" Not dropping it in auto-mode.\n"); else if (get_key("yn","Drop directory ? (y/n)") == 'y') { truncate_file(fs,parent,0); MODIFY(parent,name[0],DELETED_FLAG); /* buglet: deleted directory stays in the list. */ return 1; } } dot = dotdot = redo = 0; walk = root; while (*walk) { if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME) || !strncmp((*walk)->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) { if (handle_dot(fs,*walk,dots)) { *walk = (*walk)->next; continue; } if (!strncmp((*walk)->dir_ent.name,MSDOS_DOT,MSDOS_NAME)) dot++; else dotdot++; } if (!((*walk)->dir_ent.attr & ATTR_VOLUME) && bad_name((*walk)->dir_ent.name)) { printf("%s\n Bad file name.\n",path_name(*walk)); if (interactive) printf("1) Drop file\n2) Rename file\n3) Auto-rename\n" "4) Keep it\n"); else printf(" Auto-renaming it.\n"); switch (interactive ? get_key("1234","?") : '3') { case '1': drop_file(fs,*walk); walk = &(*walk)->next; continue; case '2': rename_file(*walk); redo = 1; break; case '3': auto_rename(*walk); printf(" Renamed to %s\n",file_name((*walk)->dir_ent. name)); break; case '4': break; } } /* don't check for duplicates of the volume label */ if (!((*walk)->dir_ent.attr & ATTR_VOLUME)) { scan = &(*walk)->next; skip = 0; while (*scan && !skip) { if (!((*scan)->dir_ent.attr & ATTR_VOLUME) && !strncmp((*walk)->dir_ent.name,(*scan)->dir_ent.name,MSDOS_NAME)) { printf("%s\n Duplicate directory entry.\n First %s\n", path_name(*walk),file_stat(*walk)); printf(" Second %s\n",file_stat(*scan)); if (interactive) printf("1) Drop first\n2) Drop second\n3) Rename first\n" "4) Rename second\n5) Auto-rename first\n" "6) Auto-rename second\n"); else printf(" Auto-renaming second.\n"); switch (interactive ? get_key("123456","?") : '6') { case '1': drop_file(fs,*walk); *walk = (*walk)->next; skip = 1; break; case '2': drop_file(fs,*scan); *scan = (*scan)->next; continue; case '3': rename_file(*walk); printf(" Renamed to %s\n",path_name(*walk)); redo = 1; break; case '4': rename_file(*scan); printf(" Renamed to %s\n",path_name(*walk)); redo = 1; break; case '5': auto_rename(*walk); printf(" Renamed to %s\n",file_name((*walk)->dir_ent. name)); break; case '6': auto_rename(*scan); printf(" Renamed to %s\n",file_name((*scan)->dir_ent. name)); break; } } scan = &(*scan)->next; } if (skip) continue; } if (!redo) walk = &(*walk)->next; else { walk = root; dot = dotdot = redo = 0; } } if (dots && !dot) printf("%s\n \".\" is missing. Can't fix this yet.\n", path_name(parent)); if (dots && !dotdot) printf("%s\n \"..\" is missing. Can't fix this yet.\n", path_name(parent)); return 0; } static void test_file(DOS_FS *fs,DOS_FILE *file,int read_test) { DOS_FILE *owner; unsigned long walk,prev,clusters,next_clu; prev = clusters = 0; for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2; walk = next_clu) { next_clu = next_cluster(fs,walk); if ((owner = get_owner(fs,walk))) { if (owner == file) { printf("%s\n Circular cluster chain. Truncating to %lu " "cluster%s.\n",path_name(file),clusters,clusters == 1 ? "" : "s"); if (prev) set_fat(fs,prev,-1); else if (!file->offset) die( "Bad FAT32 root directory! (bad start cluster)\n" ); else MODIFY_START(file,0,fs); } break; } if (bad_cluster(fs,walk)) break; if (read_test) { if (fs_test(cluster_start(fs,walk),fs->cluster_size)) { prev = walk; clusters++; } else { printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n", path_name(file),clusters,walk); if (prev) set_fat(fs,prev,next_cluster(fs,walk)); else MODIFY_START(file,next_cluster(fs,walk),fs); set_fat(fs,walk,-2); } } set_owner(fs,walk,file); } for (walk = FSTART(file,fs); walk > 0 && walk < fs->clusters+2; walk = next_cluster(fs,walk)) if (bad_cluster(fs,walk)) break; else if (get_owner(fs,walk) == file) set_owner(fs,walk,NULL); else break; } static void undelete(DOS_FS *fs,DOS_FILE *file) { unsigned long clusters,left,prev,walk; clusters = left = (CF_LE_L(file->dir_ent.size)+fs->cluster_size-1)/ fs->cluster_size; prev = 0; for (walk = FSTART(file,fs); left && walk >= 2 && walk < fs->clusters+2 && !fs->fat[walk].value; walk++) { left--; if (prev) set_fat(fs,prev,walk); prev = walk; } if (prev) set_fat(fs,prev,-1); else MODIFY_START(file,0,fs); if (left) printf("Warning: Did only undelete %lu of %lu cluster%s.\n",clusters-left, clusters,clusters == 1 ? "" : "s"); } static void new_dir( void ) { lfn_reset(); } static void add_file(DOS_FS *fs,DOS_FILE ***chain,DOS_FILE *parent, loff_t offset,FDSC **cp) { DOS_FILE *new; DIR_ENT de; FD_TYPE type; if (offset) fs_read(offset,sizeof(DIR_ENT),&de); else { memcpy(de.name," ",MSDOS_NAME); de.attr = ATTR_DIR; de.size = de.time = de.date = 0; de.start = CT_LE_W(fs->root_cluster & 0xffff); de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff); } if ((type = file_type(cp,de.name)) != fdt_none) { if (type == fdt_undelete && (de.attr & ATTR_DIR)) die("Can't undelete directories."); file_modify(cp,de.name); fs_write(offset,1,&de); } if (IS_FREE(de.name)) { lfn_check_orphaned(); return; } if (de.attr == VFAT_LN_ATTR) { lfn_add_slot(&de,offset); return; } new = qalloc(&mem_queue,sizeof(DOS_FILE)); new->lfn = lfn_get(&de); new->offset = offset; memcpy(&new->dir_ent,&de,sizeof(de)); new->next = new->first = NULL; new->parent = parent; if (type == fdt_undelete) undelete(fs,new); **chain = new; *chain = &new->next; if (list) { printf("Checking file %s",path_name(new)); if (new->lfn) printf(" (%s)", file_name(new->dir_ent.name) ); printf("\n"); } if (offset && strncmp(de.name,MSDOS_DOT,MSDOS_NAME) != 0 && strncmp(de.name,MSDOS_DOTDOT,MSDOS_NAME) != 0) ++n_files; test_file(fs,new,test); } static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp); static int scan_dir(DOS_FS *fs,DOS_FILE *this,FDSC **cp) { DOS_FILE **chain; int i; unsigned long clu_num; chain = &this->first; i = 0; clu_num = FSTART(this,fs); new_dir(); while (clu_num > 0 && clu_num != -1) { add_file(fs,&chain,this,cluster_start(fs,clu_num)+(i % fs-> cluster_size),cp); i += sizeof(DIR_ENT); if (!(i % fs->cluster_size)) if ((clu_num = next_cluster(fs,clu_num)) == 0 || clu_num == -1) break; } lfn_check_orphaned(); if (check_dir(fs,&this->first,this->offset)) return 0; if (check_files(fs,this->first)) return 1; return subdirs(fs,this,cp); } static int subdirs(DOS_FS *fs,DOS_FILE *parent,FDSC **cp) { DOS_FILE *walk; for (walk = parent ? parent->first : root; walk; walk = walk->next) if (walk->dir_ent.attr & ATTR_DIR) if (strncmp(walk->dir_ent.name,MSDOS_DOT,MSDOS_NAME) && strncmp(walk->dir_ent.name,MSDOS_DOTDOT,MSDOS_NAME)) if (scan_dir(fs,walk,file_cd(cp,walk->dir_ent.name))) return 1; return 0; } int scan_root(DOS_FS *fs) { DOS_FILE **chain; int i; root = NULL; chain = &root; new_dir(); if (fs->root_cluster) { add_file(fs,&chain,NULL,0,&fp_root); } else { for (i = 0; i < fs->root_entries; i++) add_file(fs,&chain,NULL,fs->root_start+i*sizeof(DIR_ENT),&fp_root); } lfn_check_orphaned(); (void) check_dir(fs,&root,0); if (check_files(fs,root)) return 1; return subdirs(fs,NULL,&fp_root); } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/check.h0000644000410000021150000000143006526566712016771 0ustar romanroman00000000000000/* check.h - Check and repair a PC/MS-DOS file system */ /* Written 1993 by Werner Almesberger */ #ifndef _CHECK_H #define _CHECK_H loff_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 and expand to exactly 11 characters. The name actually used is written into the 'de' structure, the rest of *de is cleared. The offset returned is to where in the filesystem the entry belongs. */ int scan_root(DOS_FS *fs); /* Scans the root directory and recurses into all subdirectories. See check.c for all the details. Returns a non-zero integer if the file system has to be checked again. */ #endif dosfstools-2.11/dosfsck/common.c0000644000410000021150000000326307660757324017206 0ustar romanroman00000000000000/* common.c - Common functions */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include #include "common.h" typedef struct _link { void *data; struct _link *next; } LINK; void die(char *msg,...) { va_list args; va_start(args,msg); vfprintf(stderr,msg,args); va_end(args); fprintf(stderr,"\n"); exit(1); } void pdie(char *msg,...) { va_list args; va_start(args,msg); vfprintf(stderr,msg,args); va_end(args); fprintf(stderr,":%s\n",strerror(errno)); exit(1); } void *alloc(int size) { void *this; if ((this = malloc(size))) return this; pdie("malloc"); return NULL; /* for GCC */ } void *qalloc(void **root,int size) { LINK *link; link = alloc(sizeof(LINK)); link->next = *root; *root = link; return link->data = alloc(size); } void qfree(void **root) { LINK *this; while (*root) { this = (LINK *) *root; *root = this->next; free(this->data); free(this); } } int min(int a,int b) { return a < b ? a : b; } char get_key(char *valid,char *prompt) { int ch,okay; while (1) { if (prompt) printf("%s ",prompt); fflush(stdout); while (ch = getchar(), ch == ' ' || ch == '\t'); if (ch == EOF) exit(1); if (!strchr(valid,okay = ch)) okay = 0; while (ch = getchar(), ch != '\n' && ch != EOF); if (ch == EOF) exit(1); if (okay) return okay; printf("Invalid input.\n"); } } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/common.h0000644000410000021150000000224010017062665017170 0ustar romanroman00000000000000/* common.h - Common functions */ /* Written 1993 by Werner Almesberger */ #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # define __KERNEL__ # include # undef __KERNEL__ # define MSDOS_FAT12 4084 /* maximum number of clusters in a 12 bit FAT */ #endif #ifndef _COMMON_H #define _COMMON_H void die(char *msg,...) __attribute((noreturn)); /* Displays a prinf-style message and terminates the program. */ void pdie(char *msg,...) __attribute((noreturn)); /* Like die, but appends an error message according to the state of errno. */ void *alloc(int size); /* mallocs SIZE bytes and returns a pointer to the data. Terminates the program if malloc fails. */ void *qalloc(void **root,int size); /* Like alloc, but registers the data area in a list described by ROOT. */ void qfree(void **root); /* Deallocates all qalloc'ed data areas described by ROOT. */ int min(int a,int b); /* Returns the smaller integer value of a and b. */ char get_key(char *valid,char *prompt); /* Displays PROMPT and waits for user input. Only characters in VALID are accepted. Terminates the program on EOF. Returns the character. */ #endif dosfstools-2.11/dosfsck/dosfsck.80000644000410000021150000001213710214612712017253 0ustar romanroman00000000000000.TH DOSFSCK 8 "December 31 1997" "Linux" "MAINTENANCE COMMANDS" .SH NAME dosfsck \- check and repair MS-DOS file systems .SH SYNOPSIS .ad l .B dosfsck .RB [ \-aAflnrtvVwy ] .RB [ \-d\ \fIpath\fB\ \-d\ \fI...\fB ] .RB [ \-u\ \fIpath\fB\ \-u\ \fI...\fB ] .I device .ad b .SH DESCRIPTION .B dosfsck verifies the consistency of MS-DOS file systems and optionally tries to repair them. The following file system problems can be corrected (in this order): .IP \- FAT contains invalid cluster numbers. Cluster is changed to EOF. .PD 0 .IP \- File's cluster chain contains a loop. The loop is broken. .IP \- Bad clusters (read errors). The clusters are marked bad and they are removed from files owning them. This check is optional. .IP \- Directories with a large number of bad entries (probably corrupt). The directory can be dropped. .IP \- Files . and .. are non-directories. They can be dropped or renamed. .IP \- Directories . and .. in root directory. They are dropped. .IP \- Bad file names. They can be renamed. .IP \- Duplicate directory entries. They can be dropped or renamed. .IP \- Directories with non-zero size field. Size is set to zero. .IP \- Directory . does not point to parent directory. The start pointer is adjusted. .IP \- Directory .. does not point to parent of parent directory. The start pointer is adjusted. .IP \- Start cluster number of a file is invalid. The file is truncated. .IP \- File contains bad or free clusters. The file is truncated. .IP \- File's cluster chain is longer than indicated by the size fields. The file is truncated. .IP \- Two or more files share the same cluster(s). All but one of the files are truncated. If the file being truncated is a directory file that has already been read, the file system check is restarted after truncation. .IP \- File's cluster chain is shorter than indicated by the size fields. The file is truncated. .IP \- Clusters are marked as used but are not owned by a file. They are marked as free. .PD .LP Additionally, the following problems are detected, but not repaired: .IP \- Invalid parameters in boot sector. .PD 0 .IP \- Absence of . and .. entries in non-root directories .PD .LP When \fBdosfsck\fP checks a file system, it accumulates all changes in memory and performs them only after all checks are complete. This can be disabled with the \fB\-w\fP option. .SH OPTIONS .IP \fB\-a\fP Automatically repair the file system. No user intervention is necessary. Whenever there is more than one method to solve a problem, the least destructive approach is used. .IP \fB\-A\fP Use Atari variation of the MS-DOS filesystem. This is default if \fBdosfsck\fP is run on an Atari, then this option turns off Atari format. There are some minor differences in Atari format: Some boot sector fields are interpreted slightly different, and the special FAT entries for end-of-file and bad cluster can be different. Under MS-DOS 0xfff8 is used for EOF and Atari employs 0xffff by default, but both systems recognize all values from 0xfff8...0xffff as end-of-file. 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\-d\fP Drop the specified file. If more that one file with that name exists, the first one is dropped. .IP \fB\-f\fP Salvage unused cluster chains to files. By default, unused clusters are added to the free disk space except in auto mode (\fB-a\fP). .IP \fB\-l\fP List path names of files being processed. .IP \fB\-n\fP No-operation mode: non-interactively check for errors, but don't write anything to the filesystem. .IP \fB\-r\fP Interactively repair the file system. The user is asked for advice whenever there is more than one approach to fix an inconsistency. .IP \fB\-t\fP Mark unreadable clusters as bad. .IP \fB-u\fP Try to undelete the specified file. \fBdosfsck\fP tries to allocate a chain of contiguous unallocated clusters beginning with the start cluster of the undeleted file. .IP \fB\-v\fP Verbose mode. Generates slightly more output. .IP \fB\-V\fP Perform a verification pass. The file system check is repeated after the first run. The second pass should never report any fixable errors. It may take considerably longer than the first pass, because the first pass may have generated long list of modifications that have to be scanned for each disk read. .IP \fB\-w\fP Write changes to disk immediately. .IP \fB\-y\fP Same as \fB\-a\fP (automatically repair filesystem) for compatibility with other fsck tools. .LP If \fB\-a\fP and \fB\-r\fP are absent, the file system is only checked, but not repaired. .SH "EXIT STATUS" .IP 0 No recoverable errors have been detected. .IP 1 Recoverable errors have been detected or \fBdosfsck\fP has discovered an internal inconsistency. .IP 2 Usage error. \fBdosfsck\fP did not access the file system. .SH BUGS Does not create . and .. files where necessary. Does not remove entirely empty directories. Should give more diagnostic messages. Undeleting files should use a more sophisticated algorithm. .\".SH "SEE ALSO" .\"fs(5) .SH AUTHORS Werner Almesberger Extensions (FAT32, VFAT) by and current maintainer: Roman Hodek dosfstools-2.11/dosfsck/dosfsck.c0000644000410000021150000001037110017173571017332 0ustar romanroman00000000000000/* dosfsck.c - User interface */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include "../version.h" #include #include #include #include #include #include #include "common.h" #include "dosfsck.h" #include "io.h" #include "boot.h" #include "fat.h" #include "file.h" #include "check.h" int interactive = 0,list = 0,test = 0,verbose = 0,write_immed = 0; int atari_format = 0; unsigned n_files = 0; void *mem_queue = NULL; static void usage(char *name) { fprintf(stderr,"usage: %s [-aAflrtvVwy] [-d path -d ...] " "[-u path -u ...]\n%15sdevice\n",name,""); fprintf(stderr," -a automatically repair the file system\n"); fprintf(stderr," -A toggle Atari file system format\n"); fprintf(stderr," -d path drop that file\n"); fprintf(stderr," -f salvage unused chains to files\n"); fprintf(stderr," -l list path names\n"); fprintf(stderr," -n no-op, check non-interactively without changing\n"); fprintf(stderr," -r interactively repair the file system\n"); fprintf(stderr," -t test for bad clusters\n"); fprintf(stderr," -u path try to undelete that (non-directory) file\n"); fprintf(stderr," -v verbose mode\n"); fprintf(stderr," -V perform a verification pass\n"); fprintf(stderr," -w write changes to disk immediately\n"); fprintf(stderr," -y same as -a, for compat with other *fsck\n"); exit(2); } /* * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant * of MS-DOS filesystem by default. */ static void check_atari( void ) { #ifdef __mc68000__ FILE *f; char line[128], *p; if (!(f = fopen( "/proc/hardware", "r" ))) { perror( "/proc/hardware" ); return; } while( fgets( line, sizeof(line), f ) ) { if (strncmp( line, "Model:", 6 ) == 0) { p = line + 6; p += strspn( p, " \t" ); if (strncmp( p, "Atari ", 6 ) == 0) atari_format = 1; break; } } fclose( f ); #endif } int main(int argc,char **argv) { DOS_FS fs; int rw,salvage_files,verify,c; unsigned long free_clusters; rw = salvage_files = verify = 0; interactive = 1; check_atari(); while ((c = getopt(argc,argv,"Aad:flnrtu:vVwy")) != EOF) switch (c) { case 'A': /* toggle Atari format */ atari_format = !atari_format; break; case 'a': case 'y': rw = 1; interactive = 0; salvage_files = 1; break; case 'd': file_add(optarg,fdt_drop); break; case 'f': salvage_files = 1; break; case 'l': list = 1; break; case 'n': rw = 0; interactive = 0; break; case 'r': rw = 1; interactive = 1; break; case 't': test = 1; break; case 'u': file_add(optarg,fdt_undelete); break; case 'v': verbose = 1; printf("dosfsck " VERSION " (" VERSION_DATE ")\n"); break; case 'V': verify = 1; break; case 'w': write_immed = 1; break; default: usage(argv[0]); } if ((test || write_immed) && !rw) { fprintf(stderr,"-t and -w require -a or -r\n"); exit(2); } if (optind != argc-1) usage(argv[0]); printf( "dosfsck " VERSION ", " VERSION_DATE ", FAT32, LFN\n" ); fs_open(argv[optind],rw); read_boot(&fs); if (verify) printf("Starting check/repair pass.\n"); while (read_fat(&fs), scan_root(&fs)) qfree(&mem_queue); if (test) fix_bad(&fs); if (salvage_files) reclaim_file(&fs); else reclaim_free(&fs); free_clusters = update_free(&fs); file_unused(); qfree(&mem_queue); if (verify) { printf("Starting verification pass.\n"); read_fat(&fs); scan_root(&fs); reclaim_free(&fs); qfree(&mem_queue); } if (fs_changed()) { if (rw) { if (interactive) rw = get_key("yn","Perform changes ? (y/n)") == 'y'; else printf("Performing changes.\n"); } else printf("Leaving file system unchanged.\n"); } printf( "%s: %u files, %lu/%lu clusters\n", argv[optind], n_files, fs.clusters - free_clusters, fs.clusters ); return fs_close(rw) ? 1 : 0; } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/dosfsck.h0000644000410000021150000001235710214575520017344 0ustar romanroman00000000000000/* dosfsck.h - Common data structures and global variables */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #ifndef _DOSFSCK_H #define _DOSFSCK_H #include #define _LINUX_STAT_H /* hack to avoid inclusion of */ #define _LINUX_STRING_H_ /* hack to avoid inclusion of */ #define _LINUX_FS_H /* hack to avoid inclusion of */ #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # define __KERNEL__ # include # include # undef __KERNEL__ #endif #include #undef CF_LE_W #undef CF_LE_L #undef CT_LE_W #undef CT_LE_L #if __BYTE_ORDER == __BIG_ENDIAN #include #define CF_LE_W(v) bswap_16(v) #define CF_LE_L(v) bswap_32(v) #define CT_LE_W(v) CF_LE_W(v) #define CT_LE_L(v) CF_LE_L(v) #else #define CF_LE_W(v) (v) #define CF_LE_L(v) (v) #define CT_LE_W(v) (v) #define CT_LE_L(v) (v) #endif /* __BIG_ENDIAN */ #define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME) /* ++roman: Use own definition of boot sector structure -- the kernel headers' * name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */ struct boot_sector { __u8 ignored[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case partition manager volumes */ __u8 sector_size[2]; /* bytes per logical sector */ __u8 cluster_size; /* sectors/cluster */ __u16 reserved; /* reserved sectors */ __u8 fats; /* number of FATs */ __u8 dir_entries[2]; /* root directory entries */ __u8 sectors[2]; /* number of sectors */ __u8 media; /* media code (unused) */ __u16 fat_length; /* sectors/FAT */ __u16 secs_track; /* sectors per track */ __u16 heads; /* number of heads */ __u32 hidden; /* hidden sectors (unused) */ __u32 total_sect; /* number of sectors (if sectors == 0) */ /* The following fields are only used by FAT32 */ __u32 fat32_length; /* sectors/FAT */ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ __u8 version[2]; /* major, minor filesystem version */ __u32 root_cluster; /* first cluster in root directory */ __u16 info_sector; /* filesystem info sector */ __u16 backup_boot; /* backup boot sector */ __u16 reserved2[6]; /* Unused */ /* fill up to 512 bytes */ __u8 junk[448]; } __attribute__ ((packed)); struct info_sector { __u32 magic; /* Magic for info sector ('RRaA') */ __u8 junk[0x1dc]; __u32 reserved1; /* Nothing as far as I can tell */ __u32 signature; /* 0x61417272 ('rrAa') */ __u32 free_clusters; /* Free cluster count. -1 if unknown */ __u32 next_cluster; /* Most recently allocated cluster. */ __u32 reserved2[3]; __u16 reserved3; __u16 boot_sign; }; typedef struct { __u8 name[8],ext[3]; /* name and extension */ __u8 attr; /* attribute bits */ __u8 lcase; /* Case for base and extension */ __u8 ctime_ms; /* Creation time, milliseconds */ __u16 ctime; /* Creation time */ __u16 cdate; /* Creation date */ __u16 adate; /* Last access date */ __u16 starthi; /* High 16 bits of cluster in FAT32 */ __u16 time,date,start;/* time, date and first cluster */ __u32 size; /* file size (in bytes) */ } DIR_ENT; typedef struct _dos_file { DIR_ENT dir_ent; char *lfn; loff_t offset; struct _dos_file *parent; /* parent directory */ struct _dos_file *next; /* next entry */ struct _dos_file *first; /* first entry (directory only) */ } DOS_FILE; typedef struct { unsigned long value; unsigned long reserved; DOS_FILE *owner; int prev; /* number of previous clusters */ } FAT_ENTRY; typedef struct { int nfats; loff_t fat_start; unsigned int 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 */ unsigned long root_cluster; /* 0 for old-style root dir */ loff_t root_start; unsigned int root_entries; loff_t data_start; unsigned int cluster_size; unsigned long clusters; loff_t fsinfo_start; /* 0 if not present */ long free_clusters; loff_t backupboot_start; /* 0 if not present */ FAT_ENTRY *fat; } DOS_FS; #ifndef offsetof #define offsetof(t,e) ((int)&(((t *)0)->e)) #endif extern int interactive,list,verbose,test,write_immed; extern int atari_format; extern unsigned n_files; extern void *mem_queue; /* value to use as end-of-file marker */ #define FAT_EOF(fs) ((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs)) #define FAT_IS_EOF(fs,v) ((unsigned long)(v) >= (0xff8|FAT_EXTD(fs))) /* value to mark bad clusters */ #define FAT_BAD(fs) (0xff7 | FAT_EXTD(fs)) /* range of values used for bad clusters */ #define FAT_MIN_BAD(fs) ((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs)) #define FAT_MAX_BAD(fs) ((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs)) #define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs)) /* return -16 as a number with fs->fat_bits bits */ #define FAT_EXTD(fs) (((1 << fs->eff_fat_bits)-1) & ~0xf) #endif /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/fat.c0000644000410000021150000002234407672677610016473 0ustar romanroman00000000000000/* fat.c - Read/write access to the FAT */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include "common.h" #include "dosfsck.h" #include "io.h" #include "check.h" #include "fat.h" static void get_fat(FAT_ENTRY *entry,void *fat,unsigned long cluster,DOS_FS *fs) { unsigned char *ptr; switch(fs->fat_bits) { case 12: ptr = &((unsigned char *) fat)[cluster*3/2]; entry->value = 0xfff & (cluster & 1 ? (ptr[0] >> 4) | (ptr[1] << 4) : (ptr[0] | ptr[1] << 8)); break; case 16: entry->value = CF_LE_W(((unsigned short *) fat)[cluster]); break; case 32: /* According to M$, the high 4 bits of a FAT32 entry are reserved and * are not part of the cluster number. So we cut them off. */ { unsigned long e = CF_LE_L(((unsigned int *) fat)[cluster]); entry->value = e & 0xfffffff; entry->reserved = e >> 28; } break; default: die("Bad FAT entry size: %d bits.",fs->fat_bits); } entry->owner = NULL; } void read_fat(DOS_FS *fs) { int eff_size; unsigned long i; void *first,*second,*use; int first_ok,second_ok; eff_size = ((fs->clusters+2)*fs->fat_bits+7)/8; first = alloc(eff_size); fs_read(fs->fat_start,eff_size,first); use = first; if (fs->nfats > 1) { second = alloc(eff_size); fs_read(fs->fat_start+fs->fat_size,eff_size,second); } else second = NULL; if (second && memcmp(first,second,eff_size) != 0) { FAT_ENTRY first_media, second_media; get_fat(&first_media,first,0,fs); get_fat(&second_media,second,0,fs); first_ok = (first_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); second_ok = (second_media.value & FAT_EXTD(fs)) == FAT_EXTD(fs); if (first_ok && !second_ok) { printf("FATs differ - using first FAT.\n"); fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); } if (!first_ok && second_ok) { printf("FATs differ - using second FAT.\n"); fs_write(fs->fat_start,eff_size,use = second); } if (first_ok && second_ok) { if (interactive) { printf("FATs differ but appear to be intact. Use which FAT ?\n" "1) Use first FAT\n2) Use second FAT\n"); if (get_key("12","?") == '1') fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); else fs_write(fs->fat_start,eff_size,use = second); } else { printf("FATs differ but appear to be intact. Using first " "FAT.\n"); fs_write(fs->fat_start+fs->fat_size,eff_size,use = first); } } if (!first_ok && !second_ok) { printf("Both FATs appear to be corrupt. Giving up.\n"); exit(1); } } fs->fat = qalloc(&mem_queue,sizeof(FAT_ENTRY)*(fs->clusters+2)); for (i = 2; i < fs->clusters+2; i++) get_fat(&fs->fat[i],use,i,fs); for (i = 2; i < fs->clusters+2; i++) if (fs->fat[i].value >= fs->clusters+2 && (fs->fat[i].value < FAT_MIN_BAD(fs))) { printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", i-2,fs->fat[i].value,fs->clusters+2-1); set_fat(fs,i,-1); } free(first); if (second) free(second); } void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new) { unsigned char data[4]; int size; loff_t offs; if ((long)new == -1) new = FAT_EOF(fs); else if ((long)new == -2) new = FAT_BAD(fs); switch( fs->fat_bits ) { case 12: offs = fs->fat_start+cluster*3/2; if (cluster & 1) { data[0] = ((new & 0xf) << 4) | (fs->fat[cluster-1].value >> 8); data[1] = new >> 4; } else { data[0] = new & 0xff; data[1] = (new >> 8) | (cluster == fs->clusters-1 ? 0 : (0xff & fs->fat[cluster+1].value) << 4); } size = 2; break; case 16: offs = fs->fat_start+cluster*2; *(unsigned short *) data = CT_LE_W(new); size = 2; break; case 32: offs = fs->fat_start+cluster*4; /* According to M$, the high 4 bits of a FAT32 entry are reserved and * are not part of the cluster number. So we never touch them. */ *(unsigned long *) data = CT_LE_L( (new & 0xfffffff) | (fs->fat[cluster].reserved << 28) ); size = 4; break; default: die("Bad FAT entry size: %d bits.",fs->fat_bits); } fs->fat[cluster].value = new; fs_write(offs,size,&data); fs_write(offs+fs->fat_size,size,&data); } int bad_cluster(DOS_FS *fs,unsigned long cluster) { return FAT_IS_BAD(fs,fs->fat[cluster].value); } unsigned long next_cluster(DOS_FS *fs,unsigned long cluster) { unsigned long value; value = fs->fat[cluster].value; if (FAT_IS_BAD(fs,value)) die("Internal error: next_cluster on bad cluster"); return FAT_IS_EOF(fs,value) ? -1 : value; } loff_t cluster_start(DOS_FS *fs,unsigned long cluster) { return fs->data_start+((loff_t)cluster-2)*fs->cluster_size; } void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner) { if (owner && fs->fat[cluster].owner) die("Internal error: attempt to change file owner"); fs->fat[cluster].owner = owner; } DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster) { return fs->fat[cluster].owner; } void fix_bad(DOS_FS *fs) { unsigned long i; if (verbose) printf("Checking for bad clusters.\n"); for (i = 2; i < fs->clusters+2; i++) if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value)) if (!fs_test(cluster_start(fs,i),fs->cluster_size)) { printf("Cluster %lu is unreadable.\n",i); set_fat(fs,i,-2); } } void reclaim_free(DOS_FS *fs) { int reclaimed; unsigned long i; if (verbose) printf("Checking for unused clusters.\n"); reclaimed = 0; for (i = 2; i < fs->clusters+2; i++) if (!get_owner(fs,i) && fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value)) { set_fat(fs,i,0); reclaimed++; } if (reclaimed) printf("Reclaimed %d unused cluster%s (%d bytes).\n",reclaimed, reclaimed == 1 ? "" : "s",reclaimed*fs->cluster_size); } static void tag_free(DOS_FS *fs,DOS_FILE *ptr) { DOS_FILE *owner; int prev; unsigned long i,walk; for (i = 2; i < fs->clusters+2; i++) if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) && !get_owner(fs,i) && !fs->fat[i].prev) { prev = 0; for (walk = i; walk > 0 && walk != -1; walk = next_cluster(fs,walk)) { if (!(owner = get_owner(fs,walk))) set_owner(fs,walk,ptr); else if (owner != ptr) die("Internal error: free chain collides with file"); else { set_fat(fs,prev,-1); break; } prev = walk; } } } void reclaim_file(DOS_FS *fs) { DOS_FILE dummy; int reclaimed,files,changed; unsigned long i,next,walk; if (verbose) printf("Reclaiming unconnected clusters.\n"); for (i = 2; i < fs->clusters+2; i++) fs->fat[i].prev = 0; for (i = 2; i < fs->clusters+2; i++) { next = fs->fat[i].value; if (!get_owner(fs,i) && next && next < fs->clusters+2) { if (get_owner(fs,next) || !fs->fat[next].value || FAT_IS_BAD(fs,fs->fat[next].value)) set_fat(fs,i,-1); else fs->fat[next].prev++; } } do { tag_free(fs,&dummy); changed = 0; for (i = 2; i < fs->clusters+2; i++) if (fs->fat[i].value && !FAT_IS_BAD(fs,fs->fat[i].value) && !get_owner(fs, i)) { if (!fs->fat[fs->fat[i].value].prev--) die("Internal error: prev going below zero"); set_fat(fs,i,-1); changed = 1; printf("Broke cycle at cluster %lu in free chain.\n",i); break; } } while (changed); files = reclaimed = 0; for (i = 2; i < fs->clusters+2; i++) if (get_owner(fs,i) == &dummy && !fs->fat[i].prev) { DIR_ENT de; loff_t offset; files++; offset = alloc_rootdir_entry(fs,&de,"FSCK%04dREC"); de.start = CT_LE_W(i&0xffff); if (fs->fat_bits == 32) de.starthi = CT_LE_W(i>>16); for (walk = i; walk > 0 && walk != -1; walk = next_cluster(fs,walk)) { de.size = CT_LE_L(CF_LE_L(de.size)+fs->cluster_size); reclaimed++; } fs_write(offset,sizeof(DIR_ENT),&de); } if (reclaimed) printf("Reclaimed %d unused cluster%s (%d bytes) in %d chain%s.\n", reclaimed,reclaimed == 1 ? "" : "s",reclaimed*fs->cluster_size,files, files == 1 ? "" : "s"); } unsigned long update_free(DOS_FS *fs) { unsigned long i; unsigned long free = 0; int do_set = 0; for (i = 2; i < fs->clusters+2; i++) if (!get_owner(fs,i) && !FAT_IS_BAD(fs,fs->fat[i].value)) ++free; if (!fs->fsinfo_start) return free; if (verbose) printf("Checking free cluster summary.\n"); if (fs->free_clusters >= 0) { if (free != fs->free_clusters) { printf( "Free cluster summary wrong (%ld vs. really %ld)\n", fs->free_clusters,free); if (interactive) printf( "1) Correct\n2) Don't correct\n" ); else printf( " Auto-correcting.\n" ); if (!interactive || get_key("12","?") == '1') do_set = 1; } } else { printf( "Free cluster summary uninitialized (should be %ld)\n", free ); if (interactive) printf( "1) Set it\n2) Leave it uninitialized\n" ); else printf( " Auto-setting.\n" ); if (!interactive || get_key("12","?") == '1') do_set = 1; } if (do_set) { fs->free_clusters = free; free = CT_LE_L(free); fs_write(fs->fsinfo_start+offsetof(struct info_sector,free_clusters), sizeof(free),&free); } return free; } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/fat.h0000644000410000021150000000370106737133551016464 0ustar romanroman00000000000000/* fat.h - Read/write access to the FAT */ /* Written 1993 by Werner Almesberger */ #ifndef _FAT_H #define _FAT_H void read_fat(DOS_FS *fs); /* Loads the FAT of the file system described by FS. Initializes the FAT, replaces broken FATs and rejects invalid cluster entries. */ void set_fat(DOS_FS *fs,unsigned long cluster,unsigned long new); /* Changes the value of the CLUSTERth cluster of the FAT of FS to NEW. Special values of NEW are -1 (EOF, 0xff8 or 0xfff8) and -2 (bad sector, 0xff7 or 0xfff7) */ int bad_cluster(DOS_FS *fs,unsigned long cluster); /* Returns a non-zero integer if the CLUSTERth cluster is marked as bad or zero otherwise. */ unsigned long next_cluster(DOS_FS *fs,unsigned long cluster); /* Returns the number of the cluster following CLUSTER, or -1 if this is the last cluster of the respective cluster chain. CLUSTER must not be a bad cluster. */ loff_t cluster_start(DOS_FS *fs,unsigned long cluster); /* Returns the byte offset of CLUSTER, relative to the respective device. */ void set_owner(DOS_FS *fs,unsigned long cluster,DOS_FILE *owner); /* Sets the owner pointer of the respective cluster to OWNER. If OWNER was NULL before, it can be set to NULL or any non-NULL value. Otherwise, only NULL is accepted as the new value. */ DOS_FILE *get_owner(DOS_FS *fs,unsigned long cluster); /* Returns the owner of the repective cluster or NULL if the cluster has no owner. */ void fix_bad(DOS_FS *fs); /* Scans the disk for currently unused bad clusters and marks them as bad. */ void reclaim_free(DOS_FS *fs); /* Marks all allocated, but unused clusters as free. */ void reclaim_file(DOS_FS *fs); /* Scans the FAT for chains of allocated, but unused clusters and creates files for them in the root directory. Also tries to fix all inconsistencies (e.g. loops, shared clusters, etc.) in the process. */ unsigned long update_free(DOS_FS *fs); /* Updates free cluster count in FSINFO sector. */ #endif dosfstools-2.11/dosfsck/file.c0000644000410000021150000001220110017062732016603 0ustar romanroman00000000000000/* file.c - Additional file attributes */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include #define _LINUX_STAT_H /* hack to avoid inclusion of */ #define _LINUX_STRING_H_ /* hack to avoid inclusion of */ #define _LINUX_FS_H /* hack to avoid inclusion of */ #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # define __KERNEL__ # include # undef __KERNEL__ #endif #include #include "common.h" #include "file.h" FDSC *fp_root = NULL; static void put_char(char **p,unsigned char c) { if ((c >= ' ' && c < 0x7f) || c >= 0xa0) *(*p)++ = c; else { *(*p)++ = '\\'; *(*p)++ = '0'+(c >> 6); *(*p)++ = '0'+((c >> 3) & 7); *(*p)++ = '0'+(c & 7); } } char *file_name(unsigned char *fixed) { static char path[MSDOS_NAME*4+2]; char *p; int i,j; p = path; for (i = j = 0; i < 8; i++) if (fixed[i] != ' ') { while (j++ < i) *p++ = ' '; put_char(&p,fixed[i]); } if (strncmp(fixed+8," ",3)) { *p++ = '.'; for (i = j = 0; i < 3; i++) if (fixed[i+8] != ' ') { while (j++ < i) *p++ = ' '; put_char(&p,fixed[i+8]); } } *p = 0; return path; } int file_cvt(unsigned char *name,unsigned char *fixed) { unsigned char c; int size,ext,cnt; size = 8; ext = 0; while (*name) { c = *name; if (c < ' ' || c > 0x7e || strchr("*?<>|\"/",c)) { printf("Invalid character in name. Use \\ooo for special " "characters.\n"); return 0; } if (c == '.') { if (ext) { printf("Duplicate dots in name.\n"); return 0; } while (size--) *fixed++ = ' '; size = 3; ext = 1; name++; continue; } if (c == '\\') { c = 0; for (cnt = 3; cnt; cnt--) { if (*name < '0' || *name > '7') { printf("Invalid octal character.\n"); return 0; } c = c*8+*name++-'0'; } if (cnt < 4) { printf("Expected three octal digits.\n"); return 0; } name += 3; } if (islower(c)) c = toupper(c); if (size) { *fixed++ = c; size--; } name++; } if (*name || size == 8) return 0; if (!ext) { while (size--) *fixed++ = ' '; size = 3; } while (size--) *fixed++ = ' '; return 1; } void file_add(char *path,FD_TYPE type) { FDSC **current,*walk; char name[MSDOS_NAME]; char *here; current = &fp_root; if (*path != '/') die("%s: Absolute path required.",path); path++; while (1) { if ((here = strchr(path,'/'))) *here = 0; if (!file_cvt(path,name)) exit(2); for (walk = *current; walk; walk = walk->next) if (!here && (!strncmp(name,walk->name,MSDOS_NAME) || (type == fdt_undelete && !strncmp(name+1,walk->name+1,MSDOS_NAME-1)))) die("Ambiguous name: \"%s\"",path); else if (here && !strncmp(name,walk->name,MSDOS_NAME)) break; if (!walk) { walk = alloc(sizeof(FDSC)); strncpy(walk->name,name,MSDOS_NAME); walk->type = here ? fdt_none : type; walk->first = NULL; walk->next = *current; *current = walk; } current = &walk->first; if (!here) break; *here = '/'; path = here+1; } } FDSC **file_cd(FDSC **curr,char *fixed) { FDSC **walk; if (!curr || !*curr) return NULL; for (walk = curr; *walk; walk = &(*walk)->next) if (!strncmp((*walk)->name,fixed,MSDOS_NAME) && (*walk)->first) return &(*walk)->first; return NULL; } static FDSC **file_find(FDSC **dir,char *fixed) { if (!dir || !*dir) return NULL; if (*(unsigned char *) fixed == DELETED_FLAG) { while (*dir) { if (!strncmp((*dir)->name+1,fixed+1,MSDOS_NAME-1) && !(*dir)->first) return dir; dir = &(*dir)->next; } return NULL; } while (*dir) { if (!strncmp((*dir)->name,fixed,MSDOS_NAME) && !(*dir)->first) return dir; dir = &(*dir)->next; } return NULL; } FD_TYPE file_type(FDSC **curr,char *fixed) { FDSC **this; if ((this = file_find(curr,fixed))) return (*this)->type; return fdt_none; } void file_modify(FDSC **curr,char *fixed) { FDSC **this,*next; if (!(this = file_find(curr,fixed))) die("Internal error: file_find failed"); switch ((*this)->type) { case fdt_drop: printf("Dropping %s\n",file_name(fixed)); *(unsigned char *) fixed = DELETED_FLAG; break; case fdt_undelete: *fixed = *(*this)->name; printf("Undeleting %s\n",file_name(fixed)); break; default: die("Internal error: file_modify"); } next = (*this)->next; free(*this); *this = next; } static void report_unused(FDSC *this) { FDSC *next; while (this) { next = this->next; if (this->first) report_unused(this->first); else if (this->type != fdt_none) printf("Warning: did not %s file %s\n",this->type == fdt_drop ? "drop" : "undelete",file_name(this->name)); free(this); this = next; } } void file_unused(void) { report_unused(fp_root); } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/file.h0000644000410000021150000000255207672677610016644 0ustar romanroman00000000000000/* file.h - Additional file attributes */ /* Written 1993 by Werner Almesberger */ #ifndef _FILE_H #define _FILE_H typedef enum { fdt_none,fdt_drop,fdt_undelete } FD_TYPE; typedef struct _fptr { char name[MSDOS_NAME]; FD_TYPE type; struct _fptr *first; /* first entry */ struct _fptr *next; /* next file in directory */ } FDSC; extern FDSC *fp_root; char *file_name(unsigned char *fixed); /* Returns a pointer to a pretty-printed representation of a fixed MS-DOS file name. */ int file_cvt(unsigned char *name,unsigned char *fixed); /* Converts a pretty-printed file name to the fixed MS-DOS format. Returns a non-zero integer on success, zero on failure. */ void file_add(char *path,FD_TYPE type); /* Define special attributes for a path. TYPE can be either FDT_DROP or FDT_UNDELETE. */ FDSC **file_cd(FDSC **curr,char *fixed); /* Returns a pointer to the directory descriptor of the subdirectory FIXED of CURR, or NULL if no such subdirectory exists. */ FD_TYPE file_type(FDSC **curr,char *fixed); /* Returns the attribute of the file FIXED in directory CURR or FDT_NONE if no such file exists or if CURR is NULL. */ void file_modify(FDSC **curr,char *fixed); /* Performs the necessary operation on the entry of CURR that is named FIXED. */ void file_unused(void); /* Displays warnings for all unused file attributes. */ #endif dosfstools-2.11/dosfsck/io.c0000644000410000021150000001110410214605746016303 0ustar romanroman00000000000000/* io.c - Virtual disk input/output */ /* Written 1993 by Werner Almesberger */ /* * Thu Feb 26 01:15:36 CET 1998: Martin Schulze * Fixed nasty bug that caused every file with a name like * xxxxxxxx.xxx to be treated as bad name that needed to be fixed. */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #include #include #include #include #include #include #include #include #include #include "dosfsck.h" #include "common.h" #include "io.h" typedef struct _change { void *data; loff_t pos; int size; struct _change *next; } CHANGE; static CHANGE *changes,*last; static int fd,did_change = 0; unsigned device_no; #ifdef __DJGPP__ #include "volume.h" /* DOS lowlevel disk access functions */ #undef llseek static 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) #endif void fs_open(char *path,int rw) { struct stat stbuf; if ((fd = open(path,rw ? O_RDWR : O_RDONLY)) < 0) pdie("open %s",path); 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 } void fs_read(loff_t pos,int size,void *data) { CHANGE *walk; int got; if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos); if ((got = read(fd,data,size)) < 0) pdie("Read %d bytes at %lld",size,pos); if (got != size) die("Got %d bytes instead of %d at %lld",got,size,pos); 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)); else memcpy((char *) data+walk->pos-pos,walk->data,min(walk->size, size+pos-walk->pos)); } } } int fs_test(loff_t pos,int size) { void *scratch; int okay; if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos); scratch = alloc(size); okay = read(fd,scratch,size) == size; free(scratch); return okay; } void fs_write(loff_t pos,int size,void *data) { CHANGE *new; int did; if (write_immed) { did_change = 1; if (llseek(fd,pos,0) != pos) pdie("Seek to %lld",pos); if ((did = write(fd,data,size)) == size) return; if (did < 0) pdie("Write %d bytes at %lld",size,pos); die("Wrote %d bytes instead of %d at %lld",did,size,pos); } new = alloc(sizeof(CHANGE)); new->pos = pos; memcpy(new->data = alloc(new->size = size),data,size); new->next = NULL; if (last) last->next = new; else changes = new; last = new; } static void fs_flush(void) { CHANGE *this; int size; while (changes) { this = changes; changes = changes->next; if (llseek(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); else if ((size = write(fd,this->data,this->size)) < 0) fprintf(stderr,"Writing %d bytes at %lld failed: %s\n",this->size, (long long)this->pos,strerror(errno)); else if (size != this->size) fprintf(stderr,"Wrote %d bytes instead of %d bytes at %lld." "\n",size,this->size,(long long)this->pos); free(this->data); free(this); } } int fs_close(int write) { CHANGE *next; int changed; changed = !!changes; if (write) fs_flush(); else while (changes) { next = changes->next; free(changes->data); free(changes); changes = next; } if (close(fd) < 0) pdie("closing file system"); return changed || did_change; } int fs_changed(void) { return !!changes || did_change; } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/io.h0000644000410000021150000000304710214573327016316 0ustar romanroman00000000000000/* io.h - Virtual disk input/output */ /* Written 1993 by Werner Almesberger */ /* FAT32, VFAT, Atari format support, and various fixes additions May 1998 * by Roman Hodek */ #ifndef _IO_H #define _IO_H #include /* for loff_t */ /* 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 void fs_open(char *path,int rw); /* Opens the file system PATH. If RW is zero, the file system is opened read-only, otherwise, it is opened read-write. */ void fs_read(loff_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); /* 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); /* 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 memory. */ int fs_close(int write); /* Closes the file system, performs all pending changes if WRITE is non-zero and removes the list of changes. Returns a non-zero integer if the file system has been changed since the last fs_open, zero otherwise. */ int fs_changed(void); /* Determines whether the file system has changed. See fs_close. */ extern unsigned device_no; /* Major number of device (0 if file) and size (in 512 byte sectors) */ #endif dosfstools-2.11/dosfsck/lfn.c0000644000410000021150000003301410214602510016441 0ustar romanroman00000000000000/* lfn.c - Functions for handling VFAT long filenames */ /* Written 1998 by Roman Hodek */ #include #include #include #include #include #include "common.h" #include "io.h" #include "dosfsck.h" #include "lfn.h" #include "file.h" typedef struct { __u8 id; /* sequence number for slot */ __u8 name0_4[10]; /* first 5 characters in name */ __u8 attr; /* attribute byte */ __u8 reserved; /* always 0 */ __u8 alias_checksum; /* checksum for 8.3 alias */ __u8 name5_10[12]; /* 6 more characters in name */ __u16 start; /* starting cluster number, 0 in long slots */ __u8 name11_12[4]; /* last 2 characters in name */ } LFN_ENT; #define LFN_ID_START 0x40 #define LFN_ID_SLOTMASK 0x1f #define CHARS_PER_LFN 13 /* These modul-global vars represent the state of the LFN parser */ unsigned char *lfn_unicode = NULL; unsigned char lfn_checksum; int lfn_slot = -1; loff_t *lfn_offsets = NULL; int lfn_parts = 0; static unsigned char fat_uni2esc[64] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '+', '-' }; /* This defines which unicode chars are directly convertable to ISO-8859-1 */ #define UNICODE_CONVERTABLE(cl,ch) (ch == 0 && (cl < 0x80 || cl >= 0xa0)) /* for maxlen param */ #define UNTIL_0 INT_MAX /* Convert name part in 'lfn' from unicode to ASCII */ #define CNV_THIS_PART(lfn) \ ({ \ char __part_uni[CHARS_PER_LFN*2]; \ copy_lfn_part( __part_uni, lfn ); \ cnv_unicode( __part_uni, CHARS_PER_LFN, 0 ); \ }) /* Convert name parts collected so far (from previous slots) from unicode to * ASCII */ #define CNV_PARTS_SO_FAR() \ (cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2), \ lfn_parts*CHARS_PER_LFN, 0 )) /* This function converts an unicode string to a normal ASCII string, assuming * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */ static char *cnv_unicode( const unsigned char *uni, int maxlen, int use_q ) { const unsigned char *up; unsigned char *out, *cp; int len, val; for( len = 0, up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ){ if (UNICODE_CONVERTABLE(up[0],up[1])) ++len; else len += 4; } cp = out = use_q ? qalloc( &mem_queue, len+1 ) : alloc( len+1 ); for( up = uni; (up-uni)/2 < maxlen && (up[0] || up[1]); up += 2 ) { if (UNICODE_CONVERTABLE(up[0],up[1])) *cp++ = up[0]; else { /* here the same escape notation is used as in the Linux kernel */ *cp++ = ':'; val = (up[1] << 8) + up[0]; cp[2] = fat_uni2esc[val & 0x3f]; val >>= 6; cp[1] = fat_uni2esc[val & 0x3f]; val >>= 6; cp[0] = fat_uni2esc[val & 0x3f]; cp += 3; } } *cp = 0; return( out ); } static void copy_lfn_part( char *dst, LFN_ENT *lfn ) { memcpy( dst, lfn->name0_4, 10 ); memcpy( dst+10, lfn->name5_10, 12 ); memcpy( dst+22, lfn->name11_12, 4 ); } static void clear_lfn_slots( int start, int end ) { int i; LFN_ENT empty; /* New dir entry is zeroed except first byte, which is set to 0xe5. * This is to avoid that some FAT-reading OSes (not Linux! ;) stop reading * a directory at the first zero entry... */ memset( &empty, 0, sizeof(empty) ); empty.id = DELETED_FLAG; for( i = start; i <= end; ++i ) { fs_write( lfn_offsets[i], sizeof(LFN_ENT), &empty ); } } void lfn_reset( void ) { if (lfn_unicode) free( lfn_unicode ); lfn_unicode = NULL; if (lfn_offsets) free( lfn_offsets ); lfn_offsets = NULL; lfn_slot = -1; } /* 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 ) { LFN_ENT *lfn = (LFN_ENT *)de; unsigned offset; if (de->attr != VFAT_LN_ATTR) die("lfn_add_slot called with non-LFN directory entry"); if (lfn->id & LFN_ID_START) { if (lfn_slot != -1) { int can_clear = 0; /* There is already a LFN "in progess", so it is an error that a * new start entry is here. */ /* Causes: 1) if slot# == expected: start bit set mysteriously, 2) * old LFN overwritten by new one */ /* Fixes: 1) delete previous LFN 2) if slot# == expected and * checksum ok: clear start bit */ /* XXX: Should delay that until next LFN known (then can better * display the name) */ printf( "A new long file name starts within an old one.\n" ); if ((lfn->id & LFN_ID_SLOTMASK) == lfn_slot && lfn->alias_checksum == lfn_checksum) { char *part1 = CNV_THIS_PART(lfn); char *part2 = CNV_PARTS_SO_FAR(); printf( " It could be that the LFN start bit is wrong here\n" " if \"%s\" seems to match \"%s\".\n", part1, part2 ); free( part1 ); free( part2 ); can_clear = 1; } if (interactive) { printf( "1: Delete previous LFN\n2: Leave it as it is.\n" ); if (can_clear) printf( "3: Clear start bit and concatenate LFNs\n" ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( can_clear ? "123" : "12", "?" )) { case '1': clear_lfn_slots( 0, lfn_parts-1 ); lfn_reset(); break; case '2': break; case '3': lfn->id &= ~LFN_ID_START; fs_write( dir_offset+offsetof(LFN_ENT,id), sizeof(lfn->id), &lfn->id ); break; } } } lfn_slot = lfn->id & LFN_ID_SLOTMASK; lfn_checksum = lfn->alias_checksum; lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 ); lfn_offsets = alloc( lfn_slot*sizeof(loff_t) ); lfn_parts = 0; } else if (lfn_slot == -1) { /* No LFN in progress, but slot found; start bit missing */ /* Causes: 1) start bit got lost, 2) Previous slot with start bit got * lost */ /* Fixes: 1) delete LFN, 2) set start bit */ char *part = CNV_THIS_PART(lfn); printf( "Long filename fragment \"%s\" found outside a LFN " "sequence.\n (Maybe the start bit is missing on the " "last fragment)\n", part ); if (interactive) { printf( "1: Delete fragment\n2: Leave it as it is.\n" "3: Set start bit\n" ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( "123", "?" )) { case '1': if (!lfn_offsets) lfn_offsets = alloc( sizeof(loff_t) ); lfn_offsets[0] = dir_offset; clear_lfn_slots( 0, 0 ); lfn_reset(); return; case '2': lfn_reset(); return; case '3': lfn->id |= LFN_ID_START; fs_write( dir_offset+offsetof(LFN_ENT,id), sizeof(lfn->id), &lfn->id ); lfn_slot = lfn->id & LFN_ID_SLOTMASK; lfn_checksum = lfn->alias_checksum; lfn_unicode = alloc( (lfn_slot*CHARS_PER_LFN+1)*2 ); lfn_offsets = alloc( lfn_slot*sizeof(loff_t) ); lfn_parts = 0; break; } } } else if ((lfn->id & LFN_ID_SLOTMASK) != lfn_slot) { /* wrong sequence number */ /* Causes: 1) seq-no destroyed */ /* Fixes: 1) delete LFN, 2) fix number (maybe only if following parts * are ok?, maybe only if checksum is ok?) (Attention: space * for name was allocated before!) */ int can_fix = 0; printf( "Unexpected long filename sequence number " "(%d vs. expected %d).\n", (lfn->id & LFN_ID_SLOTMASK), lfn_slot ); if (lfn->alias_checksum == lfn_checksum) { char *part1 = CNV_THIS_PART(lfn); char *part2 = CNV_PARTS_SO_FAR(); printf( " It could be that just the number is wrong\n" " if \"%s\" seems to match \"%s\".\n", part1, part2 ); free( part1 ); free( part2 ); can_fix = 1; } if (interactive) { printf( "1: Delete LFN\n2: Leave it as it is (and ignore LFN so far)\n" ); if (can_fix) printf( "3: Correct sequence number\n" ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( can_fix ? "123" : "12", "?" )) { case '1': lfn_offsets[lfn_parts++] = dir_offset; clear_lfn_slots( 0, lfn_parts-1 ); lfn_reset(); return; case '2': lfn_reset(); return; case '3': lfn->id = (lfn->id & ~LFN_ID_SLOTMASK) | lfn_slot; fs_write( dir_offset+offsetof(LFN_ENT,id), sizeof(lfn->id), &lfn->id ); break; } } } if (lfn->alias_checksum != lfn_checksum) { /* checksum mismatch */ /* Causes: 1) checksum field here destroyed */ /* Fixes: 1) delete LFN, 2) fix checksum */ printf( "Checksum in long filename part wrong " "(%02x vs. expected %02x).\n", lfn->alias_checksum, lfn_checksum ); if (interactive) { printf( "1: Delete LFN\n2: Leave it as it is.\n" "3: Correct checksum\n" ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( "123", "?" )) { case '1': lfn_offsets[lfn_parts++] = dir_offset; clear_lfn_slots( 0, lfn_parts-1 ); lfn_reset(); return; case '2': break; case '3': lfn->alias_checksum = lfn_checksum; fs_write( dir_offset+offsetof(LFN_ENT,alias_checksum), sizeof(lfn->alias_checksum), &lfn->alias_checksum ); break; } } } if (lfn_slot != -1) { lfn_slot--; offset = lfn_slot * CHARS_PER_LFN*2; copy_lfn_part( lfn_unicode+offset, lfn ); if (lfn->id & LFN_ID_START) lfn_unicode[offset+26] = lfn_unicode[offset+27] = 0; lfn_offsets[lfn_parts++] = dir_offset; } if (lfn->reserved != 0) { printf( "Reserved field in VFAT long filename slot is not 0 " "(but 0x%02x).\n", lfn->reserved ); if (interactive) printf( "1: Fix.\n2: Leave it.\n" ); else printf( "Auto-setting to 0.\n" ); if (!interactive || get_key("12","?") == '1') { lfn->reserved = 0; fs_write( dir_offset+offsetof(LFN_ENT,reserved), sizeof(lfn->reserved), &lfn->reserved ); } } if (lfn->start != CT_LE_W(0)) { printf( "Start cluster field in VFAT long filename slot is not 0 " "(but 0x%04x).\n", lfn->start ); if (interactive) printf( "1: Fix.\n2: Leave it.\n" ); else printf( "Auto-setting to 0.\n" ); if (!interactive || get_key("12","?") == '1') { lfn->start = CT_LE_W(0); fs_write( dir_offset+offsetof(LFN_ENT,start), sizeof(lfn->start),&lfn->start ); } } } /* 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 ) { char *lfn; __u8 sum; int i; if (de->attr == VFAT_LN_ATTR) die("lfn_get called with LFN directory entry"); #if 0 if (de->lcase) printf( "lcase=%02x\n",de->lcase ); #endif if (lfn_slot == -1) /* no long name for this file */ return NULL; if (lfn_slot != 0) { /* The long name isn't finished yet. */ /* Causes: 1) LFN slot overwritten by non-VFAT aware tool */ /* Fixes: 1) delete LFN 2) move overwriting entry to somewhere else * and let user enter missing part of LFN (hard to do :-() * 3) renumber entries and truncate name */ char *long_name = CNV_PARTS_SO_FAR(); char *short_name = file_name(de->name); printf( "Unfinished long file name \"%s\".\n" " (Start may have been overwritten by %s)\n", long_name, short_name ); free( long_name ); if (interactive) { printf( "1: Delete LFN\n2: Leave it as it is.\n" "3: Fix numbering (truncates long name and attaches " "it to short name %s)\n", short_name ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( "123", "?" )) { case '1': clear_lfn_slots( 0, lfn_parts-1 ); lfn_reset(); return NULL; case '2': lfn_reset(); return NULL; case '3': for( i = 0; i < lfn_parts; ++i ) { __u8 id = (lfn_parts-i) | (i==0 ? LFN_ID_START : 0); fs_write( lfn_offsets[i]+offsetof(LFN_ENT,id), sizeof(id), &id ); } memmove( lfn_unicode, lfn_unicode+lfn_slot*CHARS_PER_LFN*2, lfn_parts*CHARS_PER_LFN*2 ); break; } } } for (sum = 0, i = 0; i < 11; i++) sum = (((sum&1) << 7) | ((sum&0xfe) >> 1)) + de->name[i]; if (sum != lfn_checksum) { /* checksum doesn't match, long name doesn't apply to this alias */ /* Causes: 1) alias renamed */ /* Fixes: 1) Fix checksum in LFN entries */ char *long_name = CNV_PARTS_SO_FAR(); char *short_name = file_name(de->name); printf( "Wrong checksum for long file name \"%s\".\n" " (Short name %s may have changed without updating the long name)\n", long_name, short_name ); free( long_name ); if (interactive) { printf( "1: Delete LFN\n2: Leave it as it is.\n" "3: Fix checksum (attaches to short name %s)\n", short_name ); } else printf( " Not auto-correcting this.\n" ); if (interactive) { switch( get_key( "123", "?" )) { case '1': clear_lfn_slots( 0, lfn_parts-1 ); lfn_reset(); return NULL; case '2': lfn_reset(); return NULL; case '3': for( i = 0; i < lfn_parts; ++i ) { fs_write( lfn_offsets[i]+offsetof(LFN_ENT,alias_checksum), sizeof(sum), &sum ); } break; } } } lfn = cnv_unicode( lfn_unicode, UNTIL_0, 1 ); lfn_reset(); return( lfn ); } void lfn_check_orphaned(void) { char *long_name; if (lfn_slot == -1) return; long_name = CNV_PARTS_SO_FAR(); printf("Orphaned long file name part \"%s\"\n", long_name); if (interactive) printf( "1: Delete.\n2: Leave it.\n" ); else printf( " Auto-deleting.\n" ); if (!interactive || get_key("12","?") == '1') { clear_lfn_slots(0, lfn_parts - 1); } lfn_reset(); } /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/dosfsck/lfn.h0000644000410000021150000000064710214602150016454 0ustar romanroman00000000000000/* lfn.h - Functions for handling VFAT long filenames */ /* Written 1998 by Roman Hodek */ #ifndef _LFN_H #define _LFN_H void lfn_reset( void ); /* Reset the state of the LFN parser. */ void lfn_add_slot( DIR_ENT *de, loff_t dir_offset ); /* Process a dir slot that is a VFAT LFN entry. */ char *lfn_get( DIR_ENT *de ); /* Retrieve the long name for the proper dir entry. */ void lfn_check_orphaned(void); #endif dosfstools-2.11/mkdosfs/.cvsignore0000644000410000021150000000001006523125142017527 0ustar romanroman00000000000000mkdosfs dosfstools-2.11/mkdosfs/ANNOUNCE0000644000410000021150000000236206351731742016704 0ustar romanroman00000000000000Announcing the release of mkdosfs version 0.3b (Yggdrasil) It seems I didn't get the bug completely fixed in 0.3a. Some borderline cases would still allocate too many sectors for the FAT. Again, nothing to worry about, just a nitpick -- this one would only in certain cases add one sector per FAT. Announcing the release of mkdosfs version 0.3a (Yggdrasil) Fixed a bug which would cause too many sectors to be reserved for the FAT (filesystem will still work fine, but have slightly less space available). Announcing the release of mkdosfs version 0.3 (Yggdrasil) This version correctly handles even very large filesystems, and properly supports the modern (3.3+) DOS bootsector format, including a message printed on boot attempts. Peter Anvin Yggdrasil Computing, Inc. hpa@yggdrasil.com -------------- Announcing the release of mkdosfs version 0.2 I've just uploaded mkdosfs to sunsite.unc.edu. It works in a similar way to Remy Card's mke2fs, but creates an MS-DOS file system. The filename is mkdosfs-0.2.tar.gz. This second release should fix a small bug that could lead to FAT sizes that Linux's dosfs would accept but MS-DOS wouldn't. The archive contains a manual page, binary and source versions. Dave Hudson dave@humbug.demon.co.uk dosfstools-2.11/mkdosfs/COPYING0000644000410000021150000004342206351731742016610 0ustar romanroman00000000000000 The GPL below is copyrighted by the Free Software Foundation, but the instance of code that it refers to (the mkdosfs utility is copyrighted by me - David Hudson ---------------------------------------- GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. dosfstools-2.11/mkdosfs/ChangeLog0000644000410000021150000000136006351731742017322 0ustar romanroman0000000000000028th January 1995 H. Peter Anvin (hpa@yggdrasil.com) Better algorithm to select cluster sizes on large filesystems. Added bogus boot sector code that on attempts to boot prints a message (which can be chosen at mkdosfs time) and lets the user press any key and try again. Corrected support for 1.2 Mb floppies. mkdosfs now generates the extended bootsector (superblock) format of DOS 3.3+, with support for volume ID's and volume labels (volume labels are also written to the root directory, as they should). 18th February 1994 Dave Hudson (dave@humbug.demon.co.uk) Released version 0.2 - clears a bug in the FAT sizing code. 1st September 1993 Dave Hudson (dave@humbug.demon.co.uk) Released version 0.1 - ALPHA release of mkdosfs dosfstools-2.11/mkdosfs/Makefile0000644000410000021150000000117206740410544017205 0ustar romanroman00000000000000 OBJECTS = mkdosfs.o all: mkdosfs mkdosfs: $(OBJECTS) $(CC) $(LDFLAGS) $^ -o $@ .c.o: $(CC) $(CFLAGS) -c $< -o $@ install: mkdosfs mkdir -p $(SBINDIR) $(MANDIR) install -m 755 mkdosfs $(SBINDIR) install -m 644 mkdosfs.8 $(MANDIR) rm -f $(SBINDIR)/mkfs.msdos rm -f $(SBINDIR)/mkfs.vfat ln -s mkdosfs $(SBINDIR)/mkfs.msdos ln -s mkdosfs $(SBINDIR)/mkfs.vfat rm -f $(MANDIR)/mkfs.msdos.8 ln -s mkdosfs.8 $(MANDIR)/mkfs.msdos.8 ln -s mkdosfs.8 $(MANDIR)/mkfs.vfat.8 clean : echo *.o *.i *.s *~ \#*# core .#* .new* rm -f *.o *.i *.s *~ \#*# core .#* .new* distclean: clean rm -f mkdosfs *.a *# *.orig *.rej TAGS dep: dosfstools-2.11/mkdosfs/README0000644000410000021150000000416610017175333016427 0ustar romanroman00000000000000mkdosfs - Make DOS file system utilty. I wrote this, partially to complement the dosfsck utility written by Werner Almesberger (who graciously gave me some pointers when I asked for some advice about writing this code), and also to avoid me having to boot DOS just to create data partitions (I use Linux to back up DOS :-) ). The code is really derived from Remy Card's mke2fs utility - I used this as a framework, although all of the file system specific stuff was removed and the DOS stuff inserted. I believe originally mke2fs was based on Linus' mkfs code, hence the acknowledgements in the source code. Neither Remy nor Linus have had any involvement with mkdosfs, so if there are any bugs they're almost certainly "all my own work". The code has been available for ftp since 1st September 1993, and I have yet to receive any bug reports from users. I don't know of any bugs, but if you do find a bug or have any constructive comments, please mail me! The only bug I found with version 0.1 was an obscure fault that could lead to an invalid (for MS-DOS, not Linux's dos fs) number of sectors used in the file allocation table(s). Dave Hudson dave@humbug.demon.co.uk FAT32 support ============= mkdosfs now can also create filesystems in the new FAT32 format. To do this, give mkdosfs a "-F 32" option. FAT32 isn't selected automatically (yet), even if very large clusters are needed with FAT16. With FAT32 you have two additional options, -R to select the number of reserved sectors (usually 32), and -b to select the location of the backup boot sector (default 6). Of course such a backup is created, as well as the new info sector. On FAT32, the root directory is always created as a cluster chain. Sorry, there's no switch to generate an old static root dir. One bigger bug fix besides FAT32 was to reject filesystems that need a 16 bit FAT to fit all possible clusters, but the bigger FAT needs some more sectors, so the total number of clusters drop below the border where MS-DOS expects a 12 bit FAT. So such filesystems would be FAT16, but interpreted as FAT32 by DOS. The fix is to reduce filesystem size a bit. - Roman dosfstools-2.11/mkdosfs/mkdosfs-ygg-0.3b.lsm0000644000410000021150000000125506351731742021162 0ustar romanroman00000000000000Begin3 Title: mkdosfs Version: Yggdrasil 0.3b Entered-date: 05MAY95 Description: A utility to create MS-DOS FAT filesystems under Linux. This version uses the enhanced boot sector/superblock format of DOS 3.3+ as well as provides a default dummy boot sector code. This is a bug fix release. Keywords: mkdosfs, DOS FAT filesystem Author: Dave Hudson Maintained-by: H. Peter Anvin Primary-site: sunsite.unc.edu /pub/Linux/system/Filesystems/dosfs 18531 mkdosfs-ygg-0.3b.tar.gz Alternate-site: ftp.yggdrasil.com /pub/dist/mkdosfs Original-site: Platform: Linux Copying-policy: GPL End dosfstools-2.11/mkdosfs/mkdosfs.80000644000410000021150000001403010017174447017302 0ustar romanroman00000000000000.\" -*- nroff -*- .TH MKDOSFS 8 "5 May 1995" "Version 2.x" .SH NAME .B mkdosfs \- create an MS-DOS file system under Linux .SH SYNOPSIS .B mkdosfs [ .B \-A ] [ .B \-b .I sector-of-backup ] [ .B \-c ] [ .B \-l .I filename ] [ .B \-C ] [ .B \-f .I number-of-FATs ] [ .B \-F .I FAT-size ] [ .B \-h .I number-of-hidden-sectors ] [ .B \-i .I volume-id ] .RB [ " \-I " ] [ .B \-m .I message-file ] [ .B \-n .I volume-name ] [ .B \-r .I root-dir-entries ] [ .B \-R .I number-of-reserved-sectors ] [ .B \-s .I sectors-per-cluster ] [ .B \-S .I logical-sector-size ] [ .B \-v ] .I device [ .I block-count ] .SH DESCRIPTION .B mkdosfs is used to create an MS-DOS file system under Linux on a device (usually a disk partition). .I device is the special file corresponding to the device (e.g /dev/hdXX). .I block-count is the number of blocks on the device. If omitted, .B mkdosfs automatically determiness the file system size. .SH OPTIONS .TP .B \-A Use Atari variation of the MS-DOS filesystem. This is default if \fBmkdosfs\fP is run on an Atari, then this option turns off Atari format. There are some differences when using Atari format: If not directed otherwise by the user, \fBmkdosfs\fP will always use 2 sectors per cluster, since GEMDOS doesn't like other values very much. It will also obey the maximum number of sectors GEMDOS can handle. Larger filesystems are managed by raising the logical sector size. Under Atari format, an Atari-compatible serial number for the filesystem is generated, and a 12 bit FAT is used only for filesystems that have one of the usual floppy sizes (720k, 1.2M, 1.44M, 2.88M), a 16 bit FAT otherwise. This can be overridden with the \fB\-F\fP option. Some PC-specific boot sector fields aren't written, and a boot message (option \fB\-m\fP) is ignored. .TP .BI \-b " sector-of-backup " Selects the location of the backup boot sector for FAT32. Default depends on number of reserved sectors, but usually is sector 6. The backup must be within the range of reserved sectors. .TP .B \-c Check the device for bad blocks before creating the file system. .TP .B \-C Create the file given as \fIdevice\fP on the command line, and write the to-be-created file system to it. This can be used to create the new file system in a file instead of on a real device, and to avoid using \fBdd\fP in advance to create a file of appropriate size. With this option, the \fIblock-count\fP must be given, because otherwise the intended size of the file system wouldn't be known. The file created is a sparse file, which actually only contains the meta-data areas (boot sector, FATs, and root directory). The data portions won't be stored on the disk, but the file nevertheless will have the correct size. The resulting file can be copied later to a floppy disk or other device, or mounted through a loop device. .TP .BI \-f " number-of-FATs" Specify the number of file allocation tables in the file system. The default is 2. Currently the Linux MS-DOS file system does not support more than 2 FATs. .TP .BI \-F " FAT-size" Specifies the type of file allocation tables used (12, 16 or 32 bit). If nothing is specified, \fBmkdosfs\fR will automatically select between 12 and 16 bit, whatever fits better for the filesystem size. 32 bit FAT (FAT32 format) must (still) be selected explicitly if you want it. .TP .BI \-h " number-of-hidden-sectors " Select the number of hidden sectors in the volume. Apparently some digital cameras get indigestion if you feed them a CF card without such hidden sectors, this option allows you to satisfy them. Assumes \'0\' if no value is given on the command line. .TP .I \-i " volume-id" Sets the volume ID of the newly created filesystem; .I volume-id is a 32-bit hexadecimal number (for example, 2e24ec82). The default is a number which depends on the filesystem creation time. .TP .B \-I Normally you are not allowed to use any 'full' fixed disk devices. .B mkdosfs will complain and tell you that it refuses to work. This is different when usind MO disks. One doesn't always need partitions on MO disks. The filesytem can go directly to the whole disk. Under other OSes this is known as the 'superfloppy' format. This switch will force .B mkdosfs to work properly. .TP .BI \-l " filename" Read the bad blocks list from .IR filename . .TP .BI \-m " message-file" Sets the message the user receives on attempts to boot this filesystem without having properly installed an operating system. The message file must not exceed 418 bytes once line feeds have been converted to carriage return-line feed combinations, and tabs have been expanded. If the filename is a hyphen (-), the text is taken from standard input. .TP .BI \-n " volume-name" Sets the volume name (label) of the filesystem. The volume name can be up to 11 characters long. The default is no label. .TP .BI \-r " root-dir-entries" Select the number of entries available in the root directory. The default is 112 or 224 for floppies and 512 for hard disks. .TP .BI \-R " number-of-reserved-sectors " Select the number of reserved sectos. With FAT32 format at least 2 reserved sectors are needed, the default is 32. Otherwise the default is 1 (only the boot sector). .TP .BI \-s " sectors-per-cluster" Specify the number of disk sectors per cluster. Must be a power of 2, i.e. 1, 2, 4, 8, ... 128. .TP .BI \-S " logical-sector-size" 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. .TP .B \-v Verbose execution. .SH BUGS .B mkdosfs can not create bootable filesystems. This isn't as easy as you might think at first glance for various reasons and has been discussed a lot already. .B mkdosfs simply will not support it ;) .SH AUTHOR Dave Hudson - ; modified by Peter Anvin . Fixes and additions by Roman Hodek for Debian/GNU Linux. .SH ACKNOWLEDGEMENTS .B mkdosfs is based on code from .BR mke2fs (written by Remy Card - ) which is itself based on .BR mkfs (written by Linus Torvalds - ). .SH SEE ALSO .BR dosfsck (8), .BR mkfs (8) dosfstools-2.11/mkdosfs/mkdosfs.c0000644000410000021150000015110610214612340017347 0ustar romanroman00000000000000/* Filename: mkdosfs.c Version: 0.3b (Yggdrasil) Author: Dave Hudson Started: 24th August 1994 Last Updated: 7th May 1998 Updated by: Roman Hodek Target O/S: Linux (2.x) Description: Utility to allow an MS-DOS filesystem to be created under Linux. A lot of the basic structure of this program has been borrowed from Remy Card's "mke2fs" code. As far as possible the aim here is to make the "mkdosfs" command look almost identical to the other Linux filesystem make utilties, eg bad blocks are still specified as blocks, not sectors, but when it comes down to it, DOS is tied to the idea of a sector (512 bytes as a rule), and not the block. For example the boot block does not occupy a full cluster. Fixes/additions May 1998 by Roman Hodek : - Atari format support - New options -A, -S, -C - Support for filesystems > 2GB - FAT32 support Copying: Copyright 1993, 1994 David Hudson (dave@humbug.demon.co.uk) Portions copyright 1992, 1993 Remy Card (card@masi.ibp.fr) and 1991 Linus Torvalds (torvalds@klaava.helsinki.fi) 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 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* Include the header files */ #include "../version.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) # define __KERNEL__ # include # undef __KERNEL__ #endif #if __BYTE_ORDER == __BIG_ENDIAN #include #ifdef __le16_to_cpu /* ++roman: 2.1 kernel headers define these function, they're probably more * efficient then coding the swaps machine-independently. */ #define CF_LE_W __le16_to_cpu #define CF_LE_L __le32_to_cpu #define CT_LE_W __cpu_to_le16 #define CT_LE_L __cpu_to_le32 #else #define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff)) #define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \ (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24)) #define CT_LE_W(v) CF_LE_W(v) #define CT_LE_L(v) CF_LE_L(v) #endif /* defined(__le16_to_cpu) */ #else #define CF_LE_W(v) (v) #define CF_LE_L(v) (v) #define CT_LE_W(v) (v) #define CT_LE_L(v) (v) #endif /* __BIG_ENDIAN */ /* 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 */ #define TRUE 1 /* Boolean constants */ #define FALSE 0 #define TEST_BUFFER_BLOCKS 16 #define HARD_SECTOR_SIZE 512 #define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE ) /* Macro definitions */ /* Report a failure message and return a failure error code */ #define die( str ) fatal_error( "%s: " str "\n" ) /* Mark a cluster in the FAT as bad */ #define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD ) /* Compute ceil(a/b) */ inline int cdiv (int a, int b) { return (a + b - 1) / b; } /* MS-DOS filesystem structures -- I included them here instead of including linux/msdos_fs.h since that doesn't include some fields we need */ #define ATTR_RO 1 /* read-only */ #define ATTR_HIDDEN 2 /* hidden */ #define ATTR_SYS 4 /* system */ #define ATTR_VOLUME 8 /* volume label */ #define ATTR_DIR 16 /* directory */ #define ATTR_ARCH 32 /* archived */ #define ATTR_NONE 0 /* no attribute bits */ #define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN) /* attribute bits that are copied "as is" */ /* FAT values */ #define FAT_EOF (atari_format ? 0x0fffffff : 0x0ffffff8) #define FAT_BAD 0x0ffffff7 #define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */ #define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */ #define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */ #define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */ #define BOOT_SIGN 0xAA55 /* Boot sector magic number */ #define MAX_CLUST_12 ((1 << 12) - 16) #define MAX_CLUST_16 ((1 << 16) - 16) #define MIN_CLUST_32 65529 /* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong * to the cluster number. So the max. cluster# is based on 2^28 */ #define MAX_CLUST_32 ((1 << 28) - 16) #define FAT12_THRESHOLD 4085 #define OLDGEMDOS_MAX_SECTORS 32765 #define GEMDOS_MAX_SECTORS 65531 #define GEMDOS_MAX_SECTOR_SIZE (16*1024) #define BOOTCODE_SIZE 448 #define BOOTCODE_FAT32_SIZE 420 /* __attribute__ ((packed)) is used on all structures to make gcc ignore any * alignments */ struct msdos_volume_info { __u8 drive_number; /* BIOS drive number */ __u8 RESERVED; /* Unused */ __u8 ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */ __u8 volume_id[4]; /* Volume ID number */ __u8 volume_label[11];/* Volume label */ __u8 fs_type[8]; /* Typically FAT12 or FAT16 */ } __attribute__ ((packed)); struct msdos_boot_sector { __u8 boot_jump[3]; /* Boot strap short or near jump */ __u8 system_id[8]; /* Name - can be used to special case partition manager volumes */ __u8 sector_size[2]; /* bytes per logical sector */ __u8 cluster_size; /* sectors/cluster */ __u16 reserved; /* reserved sectors */ __u8 fats; /* number of FATs */ __u8 dir_entries[2]; /* root directory entries */ __u8 sectors[2]; /* number of sectors */ __u8 media; /* media code (unused) */ __u16 fat_length; /* sectors/FAT */ __u16 secs_track; /* sectors per track */ __u16 heads; /* number of heads */ __u32 hidden; /* hidden sectors (unused) */ __u32 total_sect; /* number of sectors (if sectors == 0) */ union { struct { struct msdos_volume_info vi; __u8 boot_code[BOOTCODE_SIZE]; } __attribute__ ((packed)) _oldfat; struct { __u32 fat32_length; /* sectors/FAT */ __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ __u8 version[2]; /* major, minor filesystem version */ __u32 root_cluster; /* first cluster in root directory */ __u16 info_sector; /* filesystem info sector */ __u16 backup_boot; /* backup boot sector */ __u16 reserved2[6]; /* Unused */ struct msdos_volume_info vi; __u8 boot_code[BOOTCODE_FAT32_SIZE]; } __attribute__ ((packed)) _fat32; } __attribute__ ((packed)) fstype; __u16 boot_sign; } __attribute__ ((packed)); #define fat32 fstype._fat32 #define oldfat fstype._oldfat struct fat32_fsinfo { __u32 reserved1; /* Nothing as far as I can tell */ __u32 signature; /* 0x61417272L */ __u32 free_clusters; /* Free cluster count. -1 if unknown */ __u32 next_cluster; /* Most recently allocated cluster. * Unused under Linux. */ __u32 reserved2[4]; }; struct msdos_dir_entry { char name[8], ext[3]; /* name and extension */ __u8 attr; /* attribute bits */ __u8 lcase; /* Case for base and extension */ __u8 ctime_ms; /* Creation time, milliseconds */ __u16 ctime; /* Creation time */ __u16 cdate; /* Creation date */ __u16 adate; /* Last access date */ __u16 starthi; /* high 16 bits of first cl. (FAT32) */ __u16 time, date, start; /* time, date and first cluster */ __u32 size; /* file size (in bytes) */ } __attribute__ ((packed)); /* The "boot code" we put into the filesystem... it writes a message and tells the user to try again */ char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 }; char dummy_boot_jump_m68k[2] = { 0x60, 0x1c }; #define MSG_OFFSET_OFFSET 3 char dummy_boot_code[BOOTCODE_SIZE] = "\x0e" /* push cs */ "\x1f" /* pop ds */ "\xbe\x5b\x7c" /* mov si, offset message_txt */ /* write_msg: */ "\xac" /* lodsb */ "\x22\xc0" /* and al, al */ "\x74\x0b" /* jz key_press */ "\x56" /* push si */ "\xb4\x0e" /* mov ah, 0eh */ "\xbb\x07\x00" /* mov bx, 0007h */ "\xcd\x10" /* int 10h */ "\x5e" /* pop si */ "\xeb\xf0" /* jmp write_msg */ /* key_press: */ "\x32\xe4" /* xor ah, ah */ "\xcd\x16" /* int 16h */ "\xcd\x19" /* int 19h */ "\xeb\xfe" /* foo: jmp foo */ /* message_txt: */ "This is not a bootable disk. Please insert a bootable floppy and\r\n" "press any key to try again ... \r\n"; #define MESSAGE_OFFSET 29 /* Offset of message in above code */ /* Global variables - the root of all evil :-) - see these and weep! */ static char *program_name = "mkdosfs"; /* Name of the program */ static char *device_name = NULL; /* Name of the device on which to create the filesystem */ static int atari_format = 0; /* Use Atari variation of MS-DOS FS format */ static int check = FALSE; /* Default to no readablity checking */ static int verbose = 0; /* Default to verbose mode off */ static long volume_id; /* Volume ID number */ static time_t create_time; /* Creation time */ static char volume_name[] = " "; /* Volume name */ static unsigned long long blocks; /* Number of blocks in filesystem */ static int sector_size = 512; /* Size of a logical sector */ static int sector_size_set = 0; /* User selected sector size */ static int backup_boot = 0; /* Sector# of backup boot sector */ static int reserved_sectors = 0;/* Number of reserved sectors */ static int badblocks = 0; /* Number of bad blocks in the filesystem */ static int nr_fats = 2; /* Default number of FATs to produce */ static int size_fat = 0; /* Size in bits of FAT entries */ static int size_fat_by_user = 0; /* 1 if FAT size user selected */ static int dev = -1; /* FS block device file handle */ static int ignore_full_disk = 0; /* Ignore warning about 'full' disk devices */ static off_t currently_testing = 0; /* Block currently being tested (if autodetect bad blocks) */ static struct msdos_boot_sector bs; /* Boot sector data */ 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 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 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 */ static int hidden_sectors = 0; /* Number of hidden sectors */ /* Function prototype definitions */ static void fatal_error (const char *fmt_string) __attribute__((noreturn)); static void mark_FAT_cluster (int cluster, unsigned int value); static void mark_FAT_sector (int sector, unsigned int value); 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 unsigned long long count_blocks (char *filename); static void check_mount (char *device_name); static void establish_params (int device_num, int size); static void setup_tables (void); static void write_tables (void); /* The function implementations */ /* Handle the reporting of fatal errors. Volatile to let gcc know that this doesn't return */ static void fatal_error (const char *fmt_string) { fprintf (stderr, fmt_string, program_name, device_name); exit (1); /* The error exit code is 1! */ } /* Mark the specified cluster as having a particular value */ static void mark_FAT_cluster (int cluster, unsigned int value) { switch( size_fat ) { case 12: value &= 0x0fff; if (((cluster * 3) & 0x1) == 0) { fat[3 * cluster / 2] = (unsigned char) (value & 0x00ff); fat[(3 * cluster / 2) + 1] = (unsigned char) ((fat[(3 * cluster / 2) + 1] & 0x00f0) | ((value & 0x0f00) >> 8)); } else { fat[3 * cluster / 2] = (unsigned char) ((fat[3 * cluster / 2] & 0x000f) | ((value & 0x000f) << 4)); fat[(3 * cluster / 2) + 1] = (unsigned char) ((value & 0x0ff0) >> 4); } break; case 16: value &= 0xffff; fat[2 * cluster] = (unsigned char) (value & 0x00ff); fat[(2 * cluster) + 1] = (unsigned char) (value >> 8); break; case 32: value &= 0xfffffff; fat[4 * cluster] = (unsigned char) (value & 0x000000ff); fat[(4 * cluster) + 1] = (unsigned char) ((value & 0x0000ff00) >> 8); fat[(4 * cluster) + 2] = (unsigned char) ((value & 0x00ff0000) >> 16); fat[(4 * cluster) + 3] = (unsigned char) ((value & 0xff000000) >> 24); break; default: die("Bad FAT size (not 12, 16, or 32)"); } } /* Mark a specified sector as having a particular value in it's FAT entry */ static void mark_FAT_sector (int sector, unsigned int value) { int cluster; 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!"); mark_FAT_cluster (cluster, value); } /* Perform a test on a block. Return the number of blocks that could be read successfully */ 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 */ != current_block * BLOCK_SIZE) die ("seek failed during testing for blocks"); got = read (dev, buffer, try * BLOCK_SIZE); /* Try reading! */ if (got < 0) got = 0; if (got & (BLOCK_SIZE - 1)) printf ("Unexpected values in do_check: probably bugs\n"); got /= BLOCK_SIZE; return got; } /* Alarm clock handler - display the status of the quest for bad blocks! Then retrigger the alarm for five senconds later (so we can come here again) */ static void alarm_intr (int alnum) { if (currently_testing >= blocks) return; signal (SIGALRM, alarm_intr); alarm (5); if (!currently_testing) return; printf ("%lld... ", (unsigned long long)currently_testing); fflush (stdout); } static void check_blocks (void) { int try, got; int i; static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; if (verbose) { printf ("Searching for bad blocks "); fflush (stdout); } currently_testing = 0; if (verbose) { signal (SIGALRM, alarm_intr); alarm (5); } try = TEST_BUFFER_BLOCKS; while (currently_testing < blocks) { if (currently_testing + try > blocks) try = blocks - currently_testing; got = do_check (blkbuf, try, currently_testing); currently_testing += got; if (got == try) { try = TEST_BUFFER_BLOCKS; continue; } else try = 1; if (currently_testing < start_data_block) die ("bad blocks before data-area: cannot make fs"); for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ mark_sector_bad (currently_testing * SECTORS_PER_BLOCK + i); badblocks++; currently_testing++; } if (verbose) printf ("\n"); if (badblocks) printf ("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); } static void get_list_blocks (char *filename) { int i; FILE *listfile; unsigned long blockno; 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); if (badblocks) printf ("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); } /* 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 */ static int valid_offset (int fd, loff_t offset) { char ch; if (llseek (fd, offset, SEEK_SET) < 0) return FALSE; if (read (fd, &ch, 1) < 1) return FALSE; return TRUE; } /* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */ static unsigned long long count_blocks (char *filename) { off_t high, low; int fd; if ((fd = open (filename, O_RDONLY)) < 0) { perror (filename); exit (1); } /* 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; } ++low; } close (fd); return low / BLOCK_SIZE; } /* 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 file system."); endmntent (f); } /* Establish the geometry and media parameters for the device */ static void establish_params (int device_num,int size) { long loop_size; struct hd_geometry geometry; struct floppy_struct param; if ((0 == device_num) || ((device_num & 0xff00) == 0x0200)) /* file image or floppy disk */ { if (0 == device_num) { param.size = size/512; switch(param.size) { case 720: param.sect = 9 ; param.head = 2; break; case 1440: param.sect = 9; param.head = 2; break; case 2400: param.sect = 15; param.head = 2; break; case 2880: param.sect = 18; param.head = 2; 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 = CT_LE_W(param.sect); /* Set up the geometry information */ bs.heads = CT_LE_W(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; bs.dir_entries[0] = (char) 112; bs.dir_entries[1] = (char) 0; break; case 1440: /* 3.5", 2, 9, 80 - 720K */ bs.media = (char) 0xf9; bs.cluster_size = (char) 2; bs.dir_entries[0] = (char) 112; bs.dir_entries[1] = (char) 0; break; case 2400: /* 5.25", 2, 15, 80 - 1200K */ bs.media = (char) 0xf9; bs.cluster_size = (char)(atari_format ? 2 : 1); bs.dir_entries[0] = (char) 224; bs.dir_entries[1] = (char) 0; break; case 5760: /* 3.5", 2, 36, 80 - 2880K */ 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 */ floppy_default: bs.media = (char) 0xf0; bs.cluster_size = (char)(atari_format ? 2 : 1); bs.dir_entries[0] = (char) 224; bs.dir_entries[1] = (char) 0; 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 = CF_LE_W(9); bs.heads = CF_LE_W(2); bs.media = (char) 0xfd; bs.cluster_size = (char) 2; bs.dir_entries[0] = (char) 112; bs.dir_entries[1] = (char) 0; break; case 1440: /* 3.5", 2, 9, 80 - 720K */ bs.secs_track = CF_LE_W(9); bs.heads = CF_LE_W(2); bs.media = (char) 0xf9; bs.cluster_size = (char) 2; bs.dir_entries[0] = (char) 112; bs.dir_entries[1] = (char) 0; break; case 2400: /* 5.25", 2, 15, 80 - 1200K */ bs.secs_track = CF_LE_W(15); bs.heads = CF_LE_W(2); bs.media = (char) 0xf9; bs.cluster_size = (char)(atari_format ? 2 : 1); bs.dir_entries[0] = (char) 224; bs.dir_entries[1] = (char) 0; break; case 5760: /* 3.5", 2, 36, 80 - 2880K */ bs.secs_track = CF_LE_W(36); bs.heads = CF_LE_W(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 = CF_LE_W(18); bs.heads = CF_LE_W(2); bs.media = (char) 0xf0; bs.cluster_size = (char)(atari_format ? 2 : 1); bs.dir_entries[0] = (char) 224; bs.dir_entries[1] = (char) 0; break; default: /* Anything else: default hd setup */ printf("Loop device does not match a floppy size, using " "default hd params\n"); bs.secs_track = CT_LE_W(32); /* these are fake values... */ bs.heads = CT_LE_W(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)) { printf ("unable to get drive geometry, using default 255/63"); bs.secs_track = CT_LE_W(63); bs.heads = CT_LE_W(255); } else { bs.secs_track = CT_LE_W(geometry.sectors); /* Set up the geometry information */ bs.heads = CT_LE_W(geometry.heads); } def_hd_params: bs.media = (char) 0xf8; /* Set up the media descriptor for a hard drive */ bs.dir_entries[0] = (char) 0; /* Default to 512 entries */ bs.dir_entries[1] = (char) 2; 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: * fs size < 256M: 0.5k clusters * fs size < 8G: 4k clusters * fs size < 16G: 8k clusters * fs size >= 16G: 16k clusters */ unsigned long sz_mb = (blocks+(1<<(20-BLOCK_SIZE_BITS))-1) >> (20-BLOCK_SIZE_BITS); bs.cluster_size = sz_mb >= 16*1024 ? 32 : sz_mb >= 8*1024 ? 16 : sz_mb >= 256 ? 8 : 1; } else { /* FAT12 and FAT16: start at 4 sectors per cluster */ bs.cluster_size = (char) 4; } } } /* Create the filesystem data tables */ static void setup_tables (void) { unsigned num_sectors; unsigned cluster_count = 0, fat_length; unsigned fatdata; /* Sectors for FATs + data area */ struct tm *ctime; struct msdos_volume_info *vi = (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi); if (atari_format) /* On Atari, the first few bytes of the boot sector are assigned * differently: The jump code is only 2 bytes (and m68k machine code * :-), then 6 bytes filler (ignored), then 3 byte serial number. */ strncpy( bs.system_id-1, "mkdosf", 6 ); else strcpy (bs.system_id, "mkdosfs"); if (sectors_per_cluster) bs.cluster_size = (char) sectors_per_cluster; if (size_fat == 32) { /* Under FAT32, the root dir is in a cluster chain, and this is * signalled by bs.dir_entries being 0. */ bs.dir_entries[0] = bs.dir_entries[1] = (char) 0; root_dir_entries = 0; } else if (root_dir_entries) { /* Override default from establish_params() */ bs.dir_entries[0] = (char) (root_dir_entries & 0x00ff); bs.dir_entries[1] = (char) ((root_dir_entries & 0xff00) >> 8); } else root_dir_entries = bs.dir_entries[0] + (bs.dir_entries[1] << 8); if (atari_format) { bs.system_id[5] = (unsigned char) (volume_id & 0x000000ff); bs.system_id[6] = (unsigned char) ((volume_id & 0x0000ff00) >> 8); bs.system_id[7] = (unsigned char) ((volume_id & 0x00ff0000) >> 16); } else { vi->volume_id[0] = (unsigned char) (volume_id & 0x000000ff); vi->volume_id[1] = (unsigned char) ((volume_id & 0x0000ff00) >> 8); vi->volume_id[2] = (unsigned char) ((volume_id & 0x00ff0000) >> 16); vi->volume_id[3] = (unsigned char) (volume_id >> 24); } if (!atari_format) { memcpy(vi->volume_label, volume_name, 11); memcpy(bs.boot_jump, dummy_boot_jump, 3); /* Patch in the correct offset to the boot code */ bs.boot_jump[1] = ((size_fat == 32 ? (char *)&bs.fat32.boot_code : (char *)&bs.oldfat.boot_code) - (char *)&bs) - 2; if (size_fat == 32) { int offset = (char *)&bs.fat32.boot_code - (char *)&bs + MESSAGE_OFFSET + 0x7c00; if (dummy_boot_code[BOOTCODE_FAT32_SIZE-1]) printf ("Warning: message too long; truncated\n"); dummy_boot_code[BOOTCODE_FAT32_SIZE-1] = 0; memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE); bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff; bs.fat32.boot_code[MSG_OFFSET_OFFSET+1] = offset >> 8; } else { memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE); } bs.boot_sign = CT_LE_W(BOOT_SIGN); } else { memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2); } if (verbose >= 2) printf( "Boot jump code is %02x %02x\n", bs.boot_jump[0], bs.boot_jump[1] ); if (!reserved_sectors) reserved_sectors = (size_fat == 32) ? 32 : 1; else { if (size_fat == 32 && reserved_sectors < 2) die("On FAT32 at least 2 reserved sectors are needed."); } bs.reserved = CT_LE_W(reserved_sectors); if (verbose >= 2) printf( "Using %d reserved sectors\n", reserved_sectors ); bs.fats = (char) nr_fats; if (!atari_format || size_fat == 32) bs.hidden = CT_LE_L(hidden_sectors); else { /* In Atari format, hidden is a 16 bit field */ __u16 hidden = CT_LE_W(hidden_sectors); if (hidden_sectors & ~0xffff) die("#hidden doesn't fit in 16bit field of Atari format\n"); memcpy( &bs.hidden, &hidden, 2 ); } num_sectors = (long long)blocks*BLOCK_SIZE/sector_size; if (!atari_format) { unsigned fatlength12, fatlength16, fatlength32; unsigned maxclust12, maxclust16, maxclust32; unsigned clust12, clust16, clust32; int maxclustsize; fatdata = num_sectors - cdiv (root_dir_entries * 32, sector_size) - reserved_sectors; if (sectors_per_cluster) bs.cluster_size = maxclustsize = sectors_per_cluster; else /* An initial guess for bs.cluster_size should already be set */ maxclustsize = 128; if (verbose >= 2) printf( "%d sectors for FAT+data, starting with %d sectors/cluster\n", fatdata, bs.cluster_size ); do { if (verbose >= 2) printf( "Trying with %d sectors/cluster:\n", bs.cluster_size ); /* The factor 2 below avoids cut-off errors for nr_fats == 1. * The "nr_fats*3" is for the reserved first two FAT entries */ clust12 = 2*((long long) fatdata *sector_size + nr_fats*3) / (2*(int) bs.cluster_size * sector_size + nr_fats*3); fatlength12 = cdiv (((clust12+2) * 3 + 1) >> 1, sector_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. */ clust12 = (fatdata - nr_fats*fatlength12)/bs.cluster_size; maxclust12 = (fatlength12 * 2 * sector_size) / 3; if (maxclust12 > MAX_CLUST_12) maxclust12 = MAX_CLUST_12; if (verbose >= 2) printf( "FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", clust12, fatlength12, maxclust12, MAX_CLUST_12 ); if (clust12 > maxclust12-2) { clust12 = 0; if (verbose >= 2) printf( "FAT12: too much clusters\n" ); } clust16 = ((long long) fatdata *sector_size + nr_fats*4) / ((int) bs.cluster_size * sector_size + nr_fats*2); fatlength16 = cdiv ((clust16+2) * 2, sector_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. */ clust16 = (fatdata - nr_fats*fatlength16)/bs.cluster_size; maxclust16 = (fatlength16 * sector_size) / 2; if (maxclust16 > MAX_CLUST_16) maxclust16 = MAX_CLUST_16; if (verbose >= 2) printf( "FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", clust16, fatlength16, maxclust16, MAX_CLUST_16 ); if (clust16 > maxclust16-2) { if (verbose >= 2) printf( "FAT16: too much clusters\n" ); clust16 = 0; } /* The < 4078 avoids that the filesystem will be misdetected as having a * 12 bit FAT. */ if (clust16 < FAT12_THRESHOLD && !(size_fat_by_user && size_fat == 16)) { if (verbose >= 2) printf( clust16 < FAT12_THRESHOLD ? "FAT16: would be misdetected as FAT12\n" : "FAT16: too much clusters\n" ); clust16 = 0; } clust32 = ((long long) fatdata *sector_size + nr_fats*8) / ((int) bs.cluster_size * sector_size + nr_fats*4); fatlength32 = cdiv ((clust32+2) * 4, sector_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. */ clust32 = (fatdata - nr_fats*fatlength32)/bs.cluster_size; maxclust32 = (fatlength32 * sector_size) / 4; if (maxclust32 > MAX_CLUST_32) maxclust32 = MAX_CLUST_32; if (clust32 && clust32 < MIN_CLUST_32 && !(size_fat_by_user && size_fat == 32)) { clust32 = 0; if (verbose >= 2) printf( "FAT32: not enough clusters (%d)\n", MIN_CLUST_32); } if (verbose >= 2) printf( "FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", clust32, fatlength32, maxclust32, MAX_CLUST_32 ); if (clust32 > maxclust32) { clust32 = 0; if (verbose >= 2) printf( "FAT32: too much clusters\n" ); } if ((clust12 && (size_fat == 0 || size_fat == 12)) || (clust16 && (size_fat == 0 || size_fat == 16)) || (clust32 && size_fat == 32)) break; bs.cluster_size <<= 1; } while (bs.cluster_size && bs.cluster_size <= maxclustsize); /* Use the optimal FAT size if not specified; * FAT32 is (not yet) choosen automatically */ if (!size_fat) { size_fat = (clust16 > clust12) ? 16 : 12; if (verbose >= 2) printf( "Choosing %d bits for FAT\n", size_fat ); } switch (size_fat) { case 12: cluster_count = clust12; fat_length = fatlength12; bs.fat_length = CT_LE_W(fatlength12); memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8); break; case 16: if (clust16 < FAT12_THRESHOLD) { if (size_fat_by_user) { fprintf( stderr, "WARNING: Not enough clusters for a " "16 bit FAT! The filesystem will be\n" "misinterpreted as having a 12 bit FAT without " "mount option \"fat=16\".\n" ); } else { fprintf( stderr, "This filesystem has an unfortunate size. " "A 12 bit FAT cannot provide\n" "enough clusters, but a 16 bit FAT takes up a little " "bit more space so that\n" "the total number of clusters becomes less than the " "threshold value for\n" "distinction between 12 and 16 bit FATs.\n" ); die( "Make the file system a bit smaller manually." ); } } cluster_count = clust16; fat_length = fatlength16; bs.fat_length = CT_LE_W(fatlength16); memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8); break; case 32: cluster_count = clust32; fat_length = fatlength32; bs.fat_length = CT_LE_W(0); bs.fat32.fat32_length = CT_LE_L(fatlength32); memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8); break; default: die("FAT not 12, 16 or 32 bits"); } } else { unsigned clusters, maxclust; /* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on * hard disks. So use 12 bit if the size of the file system suggests that * this fs is for a floppy disk, if the user hasn't explicitly requested a * size. */ if (!size_fat) size_fat = (num_sectors == 1440 || num_sectors == 2400 || num_sectors == 2880 || num_sectors == 5760) ? 12 : 16; if (verbose >= 2) printf( "Choosing %d bits for FAT\n", size_fat ); /* Atari format: cluster size should be 2, except explicitly requested by * the user, since GEMDOS doesn't like other cluster sizes very much. * Instead, tune the sector size for the FS to fit. */ bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2; if (!sector_size_set) { while( num_sectors > GEMDOS_MAX_SECTORS ) { num_sectors >>= 1; sector_size <<= 1; } } if (verbose >= 2) printf( "Sector size must be %d to have less than %d log. sectors\n", sector_size, GEMDOS_MAX_SECTORS ); /* Check if there are enough FAT indices for how much clusters we have */ do { fatdata = num_sectors - cdiv (root_dir_entries * 32, sector_size) - reserved_sectors; /* The factor 2 below avoids cut-off errors for nr_fats == 1 and * size_fat == 12 * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries */ clusters = (2*((long long)fatdata*sector_size - 2*nr_fats*size_fat/8)) / (2*((int)bs.cluster_size*sector_size + nr_fats*size_fat/8)); fat_length = cdiv( (clusters+2)*size_fat/8, sector_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. */ clusters = (fatdata - nr_fats*fat_length)/bs.cluster_size; maxclust = (fat_length*sector_size*8)/size_fat; if (verbose >= 2) printf( "ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n", sector_size, clusters, fat_length, maxclust ); /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd); * first two numbers are reserved */ if (maxclust <= (size_fat == 32 ? MAX_CLUST_32 : (1<= 2) printf( clusters > maxclust-2 ? "Too many clusters\n" : "FAT too big\n" ); /* need to increment sector_size once more to */ if (sector_size_set) die( "With this sector size, the maximum number of FAT entries " "would be exceeded." ); num_sectors >>= 1; sector_size <<= 1; } while( sector_size <= GEMDOS_MAX_SECTOR_SIZE ); if (sector_size > GEMDOS_MAX_SECTOR_SIZE) die( "Would need a sector size > 16k, which GEMDOS can't work with"); cluster_count = clusters; if (size_fat != 32) bs.fat_length = CT_LE_W(fat_length); else { bs.fat_length = 0; bs.fat32.fat32_length = CT_LE_L(fat_length); } } bs.sector_size[0] = (char) (sector_size & 0x00ff); bs.sector_size[1] = (char) ((sector_size & 0xff00) >> 8); if (size_fat == 32) { /* set up additional FAT32 fields */ bs.fat32.flags = CT_LE_W(0); bs.fat32.version[0] = 0; bs.fat32.version[1] = 0; bs.fat32.root_cluster = CT_LE_L(2); bs.fat32.info_sector = CT_LE_W(1); if (!backup_boot) backup_boot = (reserved_sectors >= 7) ? 6 : (reserved_sectors >= 2) ? reserved_sectors-1 : 0; else { if (backup_boot == 1) die("Backup boot sector must be after sector 1"); else if (backup_boot >= reserved_sectors) die("Backup boot sector must be a reserved sector"); } if (verbose >= 2) printf( "Using sector %d as backup boot sector (0 = none)\n", backup_boot ); bs.fat32.backup_boot = CT_LE_W(backup_boot); memset( &bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2) ); } if (atari_format) { /* Just some consistency checks */ if (num_sectors >= GEMDOS_MAX_SECTORS) die( "GEMDOS can't handle more than 65531 sectors" ); else if (num_sectors >= OLDGEMDOS_MAX_SECTORS) printf( "Warning: More than 32765 sector need TOS 1.04 " "or higher.\n" ); } if (num_sectors >= 65536) { bs.sectors[0] = (char) 0; bs.sectors[1] = (char) 0; bs.total_sect = CT_LE_L(num_sectors); } else { bs.sectors[0] = (char) (num_sectors & 0x00ff); bs.sectors[1] = (char) ((num_sectors & 0xff00) >> 8); if (!atari_format) bs.total_sect = CT_LE_L(0); } if (!atari_format) vi->ext_boot_sign = MSDOS_EXT_SIGN; if (!cluster_count) { if (sectors_per_cluster) /* If yes, die if we'd spec'd sectors per cluster */ die ("Too many clusters for file system - try more sectors per cluster"); else die ("Attempting to create a too large file system"); } /* The two following vars are in hard sectors, i.e. 512 byte sectors! */ start_data_sector = (reserved_sectors + nr_fats * fat_length) * (sector_size/HARD_SECTOR_SIZE); start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK; if (blocks < start_data_block + 32) /* Arbitrary undersize file system! */ die ("Too few blocks for viable file system"); if (verbose) { printf("%s has %d head%s and %d sector%s per track,\n", device_name, CF_LE_W(bs.heads), (CF_LE_W(bs.heads) != 1) ? "s" : "", CF_LE_W(bs.secs_track), (CF_LE_W(bs.secs_track) != 1) ? "s" : ""); printf("logical sector size is %d,\n",sector_size); printf("using 0x%02x media descriptor, with %d sectors;\n", (int) (bs.media), num_sectors); printf("file system has %d %d-bit FAT%s and %d sector%s per cluster.\n", (int) (bs.fats), size_fat, (bs.fats != 1) ? "s" : "", (int) (bs.cluster_size), (bs.cluster_size != 1) ? "s" : ""); printf ("FAT size is %d sector%s, and provides %d cluster%s.\n", fat_length, (fat_length != 1) ? "s" : "", cluster_count, (cluster_count != 1) ? "s" : ""); if (size_fat != 32) printf ("Root directory contains %d slots.\n", (int) (bs.dir_entries[0]) + (int) (bs.dir_entries[1]) * 256); printf ("Volume ID is %08lx, ", volume_id & (atari_format ? 0x00ffffff : 0xffffffff)); if ( strcmp(volume_name, " ") ) printf("volume label %s.\n", volume_name); else printf("no volume label.\n"); } /* Make the file allocation tables! */ if ((fat = (unsigned char *) malloc (fat_length * sector_size)) == NULL) die ("unable to allocate space for FAT image in memory"); memset( fat, 0, fat_length * sector_size ); mark_FAT_cluster (0, 0xffffffff); /* Initial fat entries */ mark_FAT_cluster (1, 0xffffffff); fat[0] = (unsigned char) bs.media; /* Put media type in first byte! */ if (size_fat == 32) { /* Mark cluster 2 as EOF (used for root dir) */ mark_FAT_cluster (2, FAT_EOF); } /* Make the root directory entries */ size_root_dir = (size_fat == 32) ? bs.cluster_size*sector_size : (((int)bs.dir_entries[1]*256+(int)bs.dir_entries[0]) * sizeof (struct msdos_dir_entry)); if ((root_dir = (struct msdos_dir_entry *) malloc (size_root_dir)) == NULL) { free (fat); /* Tidy up before we die! */ die ("unable to allocate space for root directory in memory"); } memset(root_dir, 0, size_root_dir); if ( memcmp(volume_name, " ", 11) ) { struct msdos_dir_entry *de = &root_dir[0]; memcpy(de->name, volume_name, 11); de->attr = ATTR_VOLUME; ctime = localtime(&create_time); de->time = CT_LE_W((unsigned short)((ctime->tm_sec >> 1) + (ctime->tm_min << 5) + (ctime->tm_hour << 11))); de->date = CT_LE_W((unsigned short)(ctime->tm_mday + ((ctime->tm_mon+1) << 5) + ((ctime->tm_year-80) << 9))); de->ctime_ms = 0; de->ctime = de->time; de->cdate = de->date; de->adate = de->date; de->starthi = CT_LE_W(0); de->start = CT_LE_W(0); de->size = CT_LE_L(0); } if (size_fat == 32) { /* For FAT32, create an info sector */ struct fat32_fsinfo *info; if (!(info_sector = malloc( sector_size ))) die("Out of memory"); memset(info_sector, 0, sector_size); /* fsinfo structure is at offset 0x1e0 in info sector by observation */ info = (struct fat32_fsinfo *)(info_sector + 0x1e0); /* Info sector magic */ info_sector[0] = 'R'; info_sector[1] = 'R'; info_sector[2] = 'a'; info_sector[3] = 'A'; /* Magic for fsinfo structure */ info->signature = CT_LE_L(0x61417272); /* We've allocated cluster 2 for the root dir. */ info->free_clusters = CT_LE_L(cluster_count - 1); info->next_cluster = CT_LE_L(2); /* Info sector also must have boot sign */ *(__u16 *)(info_sector + 0x1fe) = CT_LE_W(BOOT_SIGN); } if (!(blank_sector = malloc( sector_size ))) die( "Out of memory" ); memset(blank_sector, 0, sector_size); } /* Write the new filesystem's data tables to wherever they're going to end up! */ #define error(str) \ do { \ free (fat); \ if (info_sector) free (info_sector); \ free (root_dir); \ die (str); \ } while(0) #define seekto(pos,errstr) \ do { \ loff_t __pos = (pos); \ if (llseek (dev, __pos, SEEK_SET) != __pos) \ error ("seek to " errstr " failed whilst writing tables"); \ } while(0) #define writebuf(buf,size,errstr) \ do { \ int __size = (size); \ if (write (dev, buf, __size) != __size) \ error ("failed whilst writing " errstr); \ } while(0) static void write_tables (void) { int x; int fat_length; fat_length = (size_fat == 32) ? CF_LE_L(bs.fat32.fat32_length) : CF_LE_W(bs.fat_length); seekto( 0, "start of device" ); /* clear all reserved sectors */ for( x = 0; x < reserved_sectors; ++x ) writebuf( blank_sector, sector_size, "reserved sector" ); /* seek back to sector 0 and write the boot sector */ seekto( 0, "boot sector" ); writebuf( (char *) &bs, sizeof (struct msdos_boot_sector), "boot sector" ); /* on FAT32, write the info sector and backup boot sector */ if (size_fat == 32) { seekto( CF_LE_W(bs.fat32.info_sector)*sector_size, "info sector" ); writebuf( info_sector, 512, "info sector" ); if (backup_boot != 0) { seekto( backup_boot*sector_size, "backup boot sector" ); writebuf( (char *) &bs, sizeof (struct msdos_boot_sector), "backup boot sector" ); } } /* seek to start of FATS and write them all */ seekto( reserved_sectors*sector_size, "first FAT" ); for (x = 1; x <= nr_fats; x++) writebuf( fat, fat_length * sector_size, "FAT" ); /* Write the root directory directly after the last FAT. This is the root * dir area on FAT12/16, and the first cluster on FAT32. */ writebuf( (char *) root_dir, size_root_dir, "root directory" ); if (blank_sector) free( blank_sector ); if (info_sector) free( info_sector ); free (root_dir); /* Free up the root directory space from setup_tables */ free (fat); /* Free up the fat table space reserved during setup_tables */ } /* Report the command usage and return a failure error code */ void usage (void) { fatal_error("\ Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file] [-b backup-boot-sector]\n\ [-m boot-msg-file] [-n volume-name] [-i volume-id]\n\ [-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs]\n\ [-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors]\n\ /dev/name [blocks]\n"); } /* * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant * of MS-DOS filesystem by default. */ static void check_atari( void ) { #ifdef __mc68000__ FILE *f; char line[128], *p; if (!(f = fopen( "/proc/hardware", "r" ))) { perror( "/proc/hardware" ); return; } while( fgets( line, sizeof(line), f ) ) { if (strncmp( line, "Model:", 6 ) == 0) { p = line + 6; p += strspn( p, " \t" ); if (strncmp( p, "Atari ", 6 ) == 0) atari_format = 1; break; } } fclose( f ); #endif } /* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible way. In the event that some/all of the options are invalid we need to tell the user so that something can be done! */ int main (int argc, char **argv) { int c; char *tmp; char *listfile = NULL; FILE *msgfile; struct stat statbuf; int i = 0, pos, ch; int create = 0; unsigned long long cblocks; if (argc && *argv) { /* What's the program name? */ char *p; program_name = *argv; if ((p = strrchr( program_name, '/' ))) program_name = p+1; } time(&create_time); volume_id = (long)create_time; /* Default volume ID = creation time */ check_atari(); printf ("%s " VERSION " (" VERSION_DATE ")\n", program_name); while ((c = getopt (argc, argv, "AbcCf:F:Ii:l:m:n:r:R:s:S:h:v")) != EOF) /* Scan the command line for options */ switch (c) { case 'A': /* toggle Atari format */ atari_format = !atari_format; break; case 'b': /* b : location of backup boot sector */ backup_boot = (int) strtol (optarg, &tmp, 0); if (*tmp || backup_boot < 2 || backup_boot > 0xffff) { printf ("Bad location for backup boot sector : %s\n", optarg); usage (); } break; case 'c': /* c : Check FS as we build it */ check = TRUE; break; case 'C': /* C : Create a new file */ create = TRUE; break; case 'f': /* f : Choose number of FATs */ nr_fats = (int) strtol (optarg, &tmp, 0); if (*tmp || nr_fats < 1 || nr_fats > 4) { printf ("Bad number of FATs : %s\n", optarg); usage (); } break; case 'F': /* F : Choose FAT size */ size_fat = (int) strtol (optarg, &tmp, 0); if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32)) { printf ("Bad FAT type : %s\n", optarg); usage (); } size_fat_by_user = 1; break; case 'h': /* h : number of hidden sectors */ hidden_sectors = (int) strtol (optarg, &tmp, 0); if ( *tmp || hidden_sectors < 0 ) { printf("Bad number of hidden sectors : %s\n", optarg); usage (); } break; case 'I': ignore_full_disk = 1; break; case 'i': /* i : specify volume ID */ volume_id = strtoul(optarg, &tmp, 16); if ( *tmp ) { printf("Volume ID must be a hexadecimal number\n"); usage(); } break; case 'l': /* l : Bad block filename */ listfile = optarg; break; case 'm': /* m : Set boot message */ if ( strcmp(optarg, "-") ) { msgfile = fopen(optarg, "r"); if ( !msgfile ) perror(optarg); } else msgfile = stdin; if ( msgfile ) { /* The boot code ends at offset 448 and needs a null terminator */ i = MESSAGE_OFFSET; pos = 0; /* We are at beginning of line */ do { ch = getc(msgfile); switch (ch) { case '\r': /* Ignore CRs */ case '\0': /* and nulls */ break; case '\n': /* LF -> CR+LF if necessary */ if ( pos ) /* If not at beginning of line */ { dummy_boot_code[i++] = '\r'; pos = 0; } dummy_boot_code[i++] = '\n'; break; case '\t': /* Expand tabs */ do { dummy_boot_code[i++] = ' '; pos++; } while ( pos % 8 && i < BOOTCODE_SIZE-1 ); break; case EOF: dummy_boot_code[i++] = '\0'; /* Null terminator */ break; default: dummy_boot_code[i++] = ch; /* Store character */ pos++; /* Advance position */ break; } } while ( ch != EOF && i < BOOTCODE_SIZE-1 ); /* Fill up with zeros */ while( i < BOOTCODE_SIZE-1 ) dummy_boot_code[i++] = '\0'; dummy_boot_code[BOOTCODE_SIZE-1] = '\0'; /* Just in case */ if ( ch != EOF ) printf ("Warning: message too long; truncated\n"); if ( msgfile != stdin ) fclose(msgfile); } break; case 'n': /* n : Volume name */ sprintf(volume_name, "%-11.11s", optarg); break; case 'r': /* r : Root directory entries */ root_dir_entries = (int) strtol (optarg, &tmp, 0); if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768) { printf ("Bad number of root directory entries : %s\n", optarg); usage (); } break; case 'R': /* R : number of reserved sectors */ reserved_sectors = (int) strtol (optarg, &tmp, 0); if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff) { printf ("Bad number of reserved sectors : %s\n", optarg); usage (); } break; case 's': /* s : Sectors per cluster */ sectors_per_cluster = (int) strtol (optarg, &tmp, 0); if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2 && sectors_per_cluster != 4 && sectors_per_cluster != 8 && sectors_per_cluster != 16 && sectors_per_cluster != 32 && sectors_per_cluster != 64 && sectors_per_cluster != 128)) { printf ("Bad number of sectors per cluster : %s\n", optarg); usage (); } break; case 'S': /* S : Sector size */ sector_size = (int) strtol (optarg, &tmp, 0); if (*tmp || (sector_size != 512 && sector_size != 1024 && sector_size != 2048 && sector_size != 4096 && sector_size != 8192 && sector_size != 16384 && sector_size != 32768)) { printf ("Bad logical sector size : %s\n", optarg); usage (); } sector_size_set = 1; break; case 'v': /* v : Verbose execution */ ++verbose; break; default: printf( "Unknown option: %c\n", c ); usage (); } if (optind < argc) { device_name = argv[optind]; /* Determine the number of blocks in the FS */ if (!create) cblocks = count_blocks (device_name); /* 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",cblocks,blocks); } } else if (optind == argc - 1) /* Or use value found */ { if (create) die( "Need intended size with -C." ); blocks = cblocks; tmp = ""; } else { fprintf (stderr, "No device specified!\n"); usage (); } if (*tmp) { printf ("Bad block count : %s\n", argv[optind + 1]); usage (); } if (check && listfile) /* Auto and specified bad block handling are mutually */ die ("-c and -l are incompatible"); /* exclusive of each other! */ if (!create) { check_mount (device_name); /* Is the device already mounted? */ dev = open (device_name, O_RDWR); /* Is it a suitable device to build the FS on? */ if (dev < 0) die ("unable to open %s"); } else { off_t offset = blocks*BLOCK_SIZE - 1; char null = 0; /* create the file */ dev = open( device_name, O_RDWR|O_CREAT|O_TRUNC, 0666 ); if (dev < 0) die("unable to create %s"); /* seek to the intended end-1, and write one byte. this creates a * sparse-as-possible file of appropriate size. */ if (llseek( dev, offset, SEEK_SET ) != offset) die( "seek failed" ); if (write( dev, &null, 1 ) < 0) die( "write failed" ); if (llseek( dev, 0, SEEK_SET ) != 0) die( "seek failed" ); } 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 & 0xff3f) == 0x0300 || /* hda, hdb */ (statbuf.st_rdev & 0xff0f) == 0x0800 || /* sd */ (statbuf.st_rdev & 0xff3f) == 0x0d00 || /* xd */ (statbuf.st_rdev & 0xff3f) == 0x1600 ) /* hdc, hdd */ ) die ("Will not try to make filesystem on full-disk device '%s' (use -I if wanted)"); establish_params (statbuf.st_rdev,statbuf.st_size); /* Establish the media parameters */ setup_tables (); /* Establish the file system tables */ if (check) /* Determine any bad block locations and mark them */ check_blocks (); else if (listfile) get_list_blocks (listfile); write_tables (); /* Write the file system tables away! */ exit (0); /* Terminate with no errors! */ } /* That's All Folks */ /* Local Variables: */ /* tab-width: 8 */ /* End: */ dosfstools-2.11/build0000644000410000021150000000000007733654337015123 0ustar romanroman00000000000000