Adding version 2.11.

Signed-off-by: Daniel Baumann <mail@daniel-baumann.ch>
This commit is contained in:
Daniel Baumann 2008-06-26 12:45:36 +02:00
commit ba6774ae1d
37 changed files with 6630 additions and 0 deletions

161
CHANGES Normal file
View File

@ -0,0 +1,161 @@
version 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 <byteswap.h> 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 <jim@jg555.com>)
- 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 <twinkler@sysgo.de>)
- mkdosfs: new option -h to set number of hidden sectors
(thanks to Godwin Stewart <gstewart@spamcop.net>)
- all: updated my mail address everywhere...
version 2.10
============
- dosfsck: various 64-bit fixes and removed some warnings by Michal
Cihar <mcihar@suse.cz>
- 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
<ki@kretz.co.at>).
- Fix typo in mkdosfs manpage.
- Removed inclusion of <linux/loop.h> 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 <Roman.Hodek@informatik.uni-erlangen.de>
- 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 <gpp10@cus.cam.ac.uk>)
- 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.

92
Makefile Normal file
View File

@ -0,0 +1,92 @@
#
# 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=<last-release-number>
diff:
@if [ "x$(OLDVER)" = "x" ]; then \
echo "Usage: make diff OLDVER=<last-release-number>"; \
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-number>
release:
@if [ "x$(VER)" = "x" ]; then \
echo "Usage: make release VER=<release-number>"; \
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 >version.h.tmp
date="`date +'%d %b %Y'`"; sed "/VERSION_DATE/s/\".*\"/\"$$date\"/" <version.h.tmp >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

60
README.Atari Normal file
View File

@ -0,0 +1,60 @@
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 <Roman.Hodek@informatik.uni-erlangen.de>

15
TODO Normal file
View File

@ -0,0 +1,15 @@
-*- 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.

0
build Normal file
View File

1
dosfsck/.cvsignore Normal file
View File

@ -0,0 +1 @@
dosfsck

10
dosfsck/CHANGES Normal file
View File

@ -0,0 +1,10 @@
Changes 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.

345
dosfsck/COPYING Normal file
View File

@ -0,0 +1,345 @@
The license below applies to dosfsck, which is copyrighted by
Werner Almesberger <almesber@lrc.di.epfl.ch> and Roman Hodek
<Roman.Hodek@informatik.uni-erlangen.de>.
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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.
<signature of Ty Coon>, 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.

44
dosfsck/Makefile Normal file
View File

@ -0,0 +1,44 @@
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' <Makefile >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

60
dosfsck/README Normal file
View File

@ -0,0 +1,60 @@
dosfsck, 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 <roman@hodek.net>
BTW, version 2 isn't ALPHA anymore :-)

370
dosfsck/boot.c Normal file
View File

@ -0,0 +1,370 @@
/* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#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: */

13
dosfsck/boot.h Normal file
View File

@ -0,0 +1,13 @@
/* 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

861
dosfsck/check.c Normal file
View File

@ -0,0 +1,861 @@
/* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#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: */

23
dosfsck/check.h Normal file
View File

@ -0,0 +1,23 @@
/* 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

107
dosfsck/common.c Normal file
View File

@ -0,0 +1,107 @@
/* common.c - Common functions */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#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: */

46
dosfsck/common.h Normal file
View File

@ -0,0 +1,46 @@
/* common.h - Common functions */
/* Written 1993 by Werner Almesberger */
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
# define __KERNEL__
# include <asm/types.h>
# 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

143
dosfsck/dosfsck.8 Normal file
View File

@ -0,0 +1,143 @@
.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 <werner.almesberger@lrc.di.epfl.ch>
Extensions (FAT32, VFAT) by and current maintainer:
Roman Hodek <roman@hodek.net>

183
dosfsck/dosfsck.c Normal file
View File

@ -0,0 +1,183 @@
/* dosfsck.c - User interface */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include "../version.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <getopt.h>
#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: */

164
dosfsck/dosfsck.h Normal file
View File

@ -0,0 +1,164 @@
/* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#ifndef _DOSFSCK_H
#define _DOSFSCK_H
#include <sys/types.h>
#define _LINUX_STAT_H /* hack to avoid inclusion of <linux/stat.h> */
#define _LINUX_STRING_H_ /* hack to avoid inclusion of <linux/string.h>*/
#define _LINUX_FS_H /* hack to avoid inclusion of <linux/fs.h> */
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
# define __KERNEL__
# include <asm/types.h>
# include <asm/byteorder.h>
# undef __KERNEL__
#endif
#include <linux/msdos_fs.h>
#undef CF_LE_W
#undef CF_LE_L
#undef CT_LE_W
#undef CT_LE_L
#if __BYTE_ORDER == __BIG_ENDIAN
#include <byteswap.h>
#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: */

361
dosfsck/fat.c Normal file
View File

@ -0,0 +1,361 @@
/* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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: */

64
dosfsck/fat.h Normal file
View File

@ -0,0 +1,64 @@
/* 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

250
dosfsck/file.c Normal file
View File

@ -0,0 +1,250 @@
/* file.c - Additional file attributes */
/* Written 1993 by Werner Almesberger */
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#define _LINUX_STAT_H /* hack to avoid inclusion of <linux/stat.h> */
#define _LINUX_STRING_H_ /* hack to avoid inclusion of <linux/string.h>*/
#define _LINUX_FS_H /* hack to avoid inclusion of <linux/fs.h> */
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
# define __KERNEL__
# include <asm/types.h>
# undef __KERNEL__
#endif
#include <linux/msdos_fs.h>
#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: */

55
dosfsck/file.h Normal file
View File

@ -0,0 +1,55 @@
/* 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

191
dosfsck/io.c Normal file
View File

@ -0,0 +1,191 @@
/* io.c - Virtual disk input/output */
/* Written 1993 by Werner Almesberger */
/*
* Thu Feb 26 01:15:36 CET 1998: Martin Schulze <joey@infodrom.north.de>
* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fd.h>
#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: */

53
dosfsck/io.h Normal file
View File

@ -0,0 +1,53 @@
/* 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 <Roman.Hodek@informatik.uni-erlangen.de> */
#ifndef _IO_H
#define _IO_H
#include <sys/types.h> /* 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

477
dosfsck/lfn.c Normal file
View File

@ -0,0 +1,477 @@
/* lfn.c - Functions for handling VFAT long filenames */
/* Written 1998 by Roman Hodek */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <time.h>
#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: */

20
dosfsck/lfn.h Normal file
View File

@ -0,0 +1,20 @@
/* 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

1
mkdosfs/.cvsignore Normal file
View File

@ -0,0 +1 @@
mkdosfs

41
mkdosfs/ANNOUNCE Normal file
View File

@ -0,0 +1,41 @@
Announcing 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

347
mkdosfs/COPYING Normal file
View File

@ -0,0 +1,347 @@
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
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.
<signature of Ty Coon>, 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.

18
mkdosfs/ChangeLog Normal file
View File

@ -0,0 +1,18 @@
28th 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

31
mkdosfs/Makefile Normal file
View File

@ -0,0 +1,31 @@
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:

50
mkdosfs/README Normal file
View File

@ -0,0 +1,50 @@
mkdosfs - 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 <roman@hodek.net>

View File

@ -0,0 +1,19 @@
Begin3
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 <dave@humbug.demon.co.uk>
Maintained-by: H. Peter Anvin <hpa@yggdrasil.com>
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

212
mkdosfs/mkdosfs.8 Normal file
View File

@ -0,0 +1,212 @@
.\" -*- 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 - <dave@humbug.demon.co.uk>; modified by Peter Anvin
<hpa@yggdrasil.com>. Fixes and additions by Roman Hodek
<roman@hodek.net> for Debian/GNU Linux.
.SH ACKNOWLEDGEMENTS
.B mkdosfs
is based on code from
.BR mke2fs
(written by Remy Card - <card@masi.ibp.fr>) which is itself based on
.BR mkfs
(written by Linus Torvalds - <torvalds@cs.helsinki.fi>).
.SH SEE ALSO
.BR dosfsck (8),
.BR mkfs (8)

1734
mkdosfs/mkdosfs.c Normal file

File diff suppressed because it is too large Load Diff

8
version.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _version_h
#define _version_h
#define VERSION "2.11"
#define VERSION_DATE "12 Mar 2005"
#endif /* _version_h */