Project import generated by Copybara.

NOKEYCHECK=True
GitOrigin-RevId: dd4616cae31aadc16ba5141b557f97feb0092bc7
diff --git a/ANNOUNCE b/ANNOUNCE
new file mode 100644
index 0000000..1f02ea2
--- /dev/null
+++ b/ANNOUNCE
@@ -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
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..8857e53
--- /dev/null
+++ b/COPYING
@@ -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.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..c428a50
--- /dev/null
+++ b/ChangeLog
@@ -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
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..d24842f
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+COPYING
\ No newline at end of file
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..e8fc313
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,18 @@
+name: "mkdosfs"
+description:
+    "Utility for creating/manipulating dos (FAT) file systems."
+    "Was merged into dosfstools."
+
+third_party: {
+  url {
+    type: GIT
+    value: "https://github.com/dosfstools/dosfstools"
+  }
+  version: "ba6774ae1dd5199a733dfaeaf438dff095284de7"
+  last_upgrade_date {
+    year: 2021
+    month: 3
+    day: 4
+  }
+}
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1a9365e
--- /dev/null
+++ b/Makefile
@@ -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:
diff --git a/README b/README
new file mode 100644
index 0000000..ae93ada
--- /dev/null
+++ b/README
@@ -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>
diff --git a/mkdosfs-ygg-0.3b.lsm b/mkdosfs-ygg-0.3b.lsm
new file mode 100644
index 0000000..9f78af7
--- /dev/null
+++ b/mkdosfs-ygg-0.3b.lsm
@@ -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
diff --git a/mkdosfs.8 b/mkdosfs.8
new file mode 100644
index 0000000..614b5fc
--- /dev/null
+++ b/mkdosfs.8
@@ -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)
diff --git a/mkdosfs.c b/mkdosfs.c
new file mode 100644
index 0000000..775e976
--- /dev/null
+++ b/mkdosfs.c
@@ -0,0 +1,1729 @@
+/*
+   Filename:     mkdosfs.c
+   Version:      0.3b (Yggdrasil)
+   Author:       Dave Hudson
+   Started:      24th August 1994
+   Last Updated: 7th May 1998
+   Updated by:   Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+   Target O/S:   Linux (2.x)
+
+   Description: Utility to allow an MS-DOS filesystem to be created
+   under Linux.  A lot of the basic structure of this program has been
+   borrowed from Remy Card's "mke2fs" code.
+
+   As far as possible the aim here is to make the "mkdosfs" command
+   look almost identical to the other Linux filesystem make utilties,
+   eg bad blocks are still specified as blocks, not sectors, but when
+   it comes down to it, DOS is tied to the idea of a sector (512 bytes
+   as a rule), and not the block.  For example the boot block does not
+   occupy a full cluster.
+
+   Fixes/additions May 1998 by Roman Hodek
+   <Roman.Hodek@informatik.uni-erlangen.de>:
+   - Atari format support
+   - New options -A, -S, -C
+   - Support for filesystems > 2GB
+   - FAT32 support
+   
+   Copying:     Copyright 1993, 1994 David Hudson (dave@humbug.demon.co.uk)
+
+   Portions copyright 1992, 1993 Remy Card (card@masi.ibp.fr)
+   and 1991 Linus Torvalds (torvalds@klaava.helsinki.fi)
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+/* Include the header files */
+
+#include <fcntl.h>
+#include <linux/hdreg.h>
+#include <linux/fs.h>
+#include <linux/fd.h>
+#include <endian.h>
+#include <mntent.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)
+# define __KERNEL__
+# include <asm/types.h>
+# undef __KERNEL__
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+
+#include <asm/byteorder.h>
+#ifdef __le16_to_cpu
+/* ++roman: 2.1 kernel headers define these function, they're probably more
+ * efficient then coding the swaps machine-independently. */
+#define CF_LE_W	__le16_to_cpu
+#define CF_LE_L	__le32_to_cpu
+#define CT_LE_W	__cpu_to_le16
+#define CT_LE_L	__cpu_to_le32
+#else
+#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff))
+#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \
+               (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24))
+#define CT_LE_W(v) CF_LE_W(v)
+#define CT_LE_L(v) CF_LE_L(v)
+#endif /* defined(__le16_to_cpu) */
+    
+#else
+
+#define CF_LE_W(v) (v)
+#define CF_LE_L(v) (v)
+#define CT_LE_W(v) (v)
+#define CT_LE_L(v) (v)
+
+#endif /* __BIG_ENDIAN */
+
+/* In earlier versions, an own llseek() was used, but glibc lseek() is
+ * sufficient (or even better :) for 64 bit offsets in the meantime */
+#define llseek lseek
+
+/* Constant definitions */
+
+#define TRUE 1			/* Boolean constants */
+#define FALSE 0
+
+#define TEST_BUFFER_BLOCKS 16
+#define HARD_SECTOR_SIZE   512
+#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE )
+
+
+/* Macro definitions */
+
+/* Report a failure message and return a failure error code */
+
+#define die( str ) fatal_error( "%s: " str "\n" )
+
+
+/* Mark a cluster in the FAT as bad */
+
+#define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD )
+
+/* Compute ceil(a/b) */
+
+static inline int
+cdiv (int a, int b)
+{
+  return (a + b - 1) / b;
+}
+
+/* MS-DOS filesystem structures -- I included them here instead of
+   including linux/msdos_fs.h since that doesn't include some fields we
+   need */
+
+#define ATTR_RO      1		/* read-only */
+#define ATTR_HIDDEN  2		/* hidden */
+#define ATTR_SYS     4		/* system */
+#define ATTR_VOLUME  8		/* volume label */
+#define ATTR_DIR     16		/* directory */
+#define ATTR_ARCH    32		/* archived */
+
+#define ATTR_NONE    0		/* no attribute bits */
+#define ATTR_UNUSED  (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN)
+	/* attribute bits that are copied "as is" */
+
+/* FAT values */
+#define FAT_EOF      (atari_format ? 0x0fffffff : 0x0ffffff8)
+#define FAT_BAD      0x0ffffff7
+
+#define MSDOS_EXT_SIGN 0x29	/* extended boot sector signature */
+#define MSDOS_FAT12_SIGN "FAT12   "	/* FAT12 filesystem signature */
+#define MSDOS_FAT16_SIGN "FAT16   "	/* FAT16 filesystem signature */
+#define MSDOS_FAT32_SIGN "FAT32   "	/* FAT32 filesystem signature */
+
+#define BOOT_SIGN 0xAA55	/* Boot sector magic number */
+
+#define MAX_CLUST_12	((1 << 12) - 16)
+#define MAX_CLUST_16	((1 << 16) - 16)
+#define MIN_CLUST_32    65529
+/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong
+ * to the cluster number. So the max. cluster# is based on 2^28 */
+#define MAX_CLUST_32	((1 << 28) - 16)
+
+#define FAT12_THRESHOLD	4085
+
+#define OLDGEMDOS_MAX_SECTORS	32765
+#define GEMDOS_MAX_SECTORS	65531
+#define GEMDOS_MAX_SECTOR_SIZE	(16*1024)
+
+#define BOOTCODE_SIZE		448
+#define BOOTCODE_FAT32_SIZE	420
+
+/* __attribute__ ((packed)) is used on all structures to make gcc ignore any
+ * alignments */
+
+struct msdos_volume_info {
+  __u8		drive_number;	/* BIOS drive number */
+  __u8		RESERVED;	/* Unused */
+  __u8		ext_boot_sign;	/* 0x29 if fields below exist (DOS 3.3+) */
+  __u8		volume_id[4];	/* Volume ID number */
+  __u8		volume_label[11];/* Volume label */
+  __u8		fs_type[8];	/* Typically FAT12 or FAT16 */
+} __attribute__ ((packed));
+
+struct msdos_boot_sector
+{
+  __u8	        boot_jump[3];	/* Boot strap short or near jump */
+  __u8          system_id[8];	/* Name - can be used to special case
+				   partition manager volumes */
+  __u8          sector_size[2];	/* bytes per logical sector */
+  __u8          cluster_size;	/* sectors/cluster */
+  __u16         reserved;	/* reserved sectors */
+  __u8          fats;		/* number of FATs */
+  __u8          dir_entries[2];	/* root directory entries */
+  __u8          sectors[2];	/* number of sectors */
+  __u8          media;		/* media code (unused) */
+  __u16         fat_length;	/* sectors/FAT */
+  __u16         secs_track;	/* sectors per track */
+  __u16         heads;		/* number of heads */
+  __u32         hidden;		/* hidden sectors (unused) */
+  __u32         total_sect;	/* number of sectors (if sectors == 0) */
+  union {
+    struct {
+      struct msdos_volume_info vi;
+      __u8	boot_code[BOOTCODE_SIZE];
+    } __attribute__ ((packed)) _oldfat;
+    struct {
+      __u32	fat32_length;	/* sectors/FAT */
+      __u16	flags;		/* bit 8: fat mirroring, low 4: active fat */
+      __u8	version[2];	/* major, minor filesystem version */
+      __u32	root_cluster;	/* first cluster in root directory */
+      __u16	info_sector;	/* filesystem info sector */
+      __u16	backup_boot;	/* backup boot sector */
+      __u16	reserved2[6];	/* Unused */
+      struct msdos_volume_info vi;
+      __u8	boot_code[BOOTCODE_FAT32_SIZE];
+    } __attribute__ ((packed)) _fat32;
+  } __attribute__ ((packed)) fstype;
+  __u16		boot_sign;
+} __attribute__ ((packed));
+#define fat32	fstype._fat32
+#define oldfat	fstype._oldfat
+
+struct fat32_fsinfo {
+  __u32		reserved1;	/* Nothing as far as I can tell */
+  __u32		signature;	/* 0x61417272L */
+  __u32		free_clusters;	/* Free cluster count.  -1 if unknown */
+  __u32		next_cluster;	/* Most recently allocated cluster.
+				 * Unused under Linux. */
+  __u32		reserved2[4];
+};
+
+struct msdos_dir_entry
+  {
+    char	name[8], ext[3];	/* name and extension */
+    __u8        attr;			/* attribute bits */
+    __u8	lcase;			/* Case for base and extension */
+    __u8	ctime_ms;		/* Creation time, milliseconds */
+    __u16	ctime;			/* Creation time */
+    __u16	cdate;			/* Creation date */
+    __u16	adate;			/* Last access date */
+    __u16	starthi;		/* high 16 bits of first cl. (FAT32) */
+    __u16	time, date, start;	/* time, date and first cluster */
+    __u32	size;			/* file size (in bytes) */
+  } __attribute__ ((packed));
+
+/* The "boot code" we put into the filesystem... it writes a message and
+   tells the user to try again */
+
+char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 };
+
+char dummy_boot_jump_m68k[2] = { 0x60, 0x1c };
+
+#define MSG_OFFSET_OFFSET 3
+char dummy_boot_code[BOOTCODE_SIZE] =
+  "\x0e"			/* push cs */
+  "\x1f"			/* pop ds */
+  "\xbe\x5b\x7c"		/* mov si, offset message_txt */
+				/* write_msg: */
+  "\xac"			/* lodsb */
+  "\x22\xc0"			/* and al, al */
+  "\x74\x0b"			/* jz key_press */
+  "\x56"			/* push si */
+  "\xb4\x0e"			/* mov ah, 0eh */
+  "\xbb\x07\x00"		/* mov bx, 0007h */
+  "\xcd\x10"			/* int 10h */
+  "\x5e"			/* pop si */
+  "\xeb\xf0"			/* jmp write_msg */
+				/* key_press: */
+  "\x32\xe4"			/* xor ah, ah */
+  "\xcd\x16"			/* int 16h */
+  "\xcd\x19"			/* int 19h */
+  "\xeb\xfe"			/* foo: jmp foo */
+				/* message_txt: */
+
+  "This is not a bootable disk.  Please insert a bootable floppy and\r\n"
+  "press any key to try again ... \r\n";
+
+#define MESSAGE_OFFSET 29	/* Offset of message in above code */
+
+/* Global variables - the root of all evil :-) - see these and weep! */
+
+static char *program_name = "mkdosfs";	/* Name of the program */
+static char *device_name = NULL;	/* Name of the device on which to create the filesystem */
+static int atari_format = 0;	/* Use Atari variation of MS-DOS FS format */
+static int check = FALSE;	/* Default to no readablity checking */
+static int verbose = 0;		/* Default to verbose mode off */
+static long volume_id;		/* Volume ID number */
+static time_t create_time;	/* Creation time */
+static char volume_name[] = "           "; /* Volume name */
+static unsigned long long blocks;	/* Number of blocks in filesystem */
+static int sector_size = 512;	/* Size of a logical sector */
+static int sector_size_set = 0; /* User selected sector size */
+static int backup_boot = 0;	/* Sector# of backup boot sector */
+static int reserved_sectors = 0;/* Number of reserved sectors */
+static int badblocks = 0;	/* Number of bad blocks in the filesystem */
+static int nr_fats = 2;		/* Default number of FATs to produce */
+static int size_fat = 0;	/* Size in bits of FAT entries */
+static int size_fat_by_user = 0; /* 1 if FAT size user selected */
+static int dev = -1;		/* FS block device file handle */
+static int  ignore_full_disk = 0; /* Ignore warning about 'full' disk devices */
+static off_t currently_testing = 0;	/* Block currently being tested (if autodetect bad blocks) */
+static struct msdos_boot_sector bs;	/* Boot sector data */
+static int start_data_sector;	/* Sector number for the start of the data area */
+static int start_data_block;	/* Block number for the start of the data area */
+static unsigned char *fat;	/* File allocation table */
+static unsigned char *info_sector;	/* FAT32 info sector */
+static struct msdos_dir_entry *root_dir;	/* Root directory */
+static int size_root_dir;	/* Size of the root directory in bytes */
+static int sectors_per_cluster = 0;	/* Number of sectors per disk cluster */
+static int root_dir_entries = 0;	/* Number of root directory entries */
+static char *blank_sector;		/* Blank sector - all zeros */
+static int hidden_sectors = 0;		/* Number of hidden sectors */
+
+
+/* Function prototype definitions */
+
+static void fatal_error (const char *fmt_string) __attribute__((noreturn));
+static void mark_FAT_cluster (int cluster, unsigned int value);
+static void mark_FAT_sector (int sector, unsigned int value);
+static long do_check (char *buffer, int try, off_t current_block);
+static void alarm_intr (int alnum);
+static void check_blocks (void);
+static void get_list_blocks (char *filename);
+static int valid_offset (int fd, loff_t offset);
+static unsigned long long count_blocks (char *filename);
+static void check_mount (char *device_name);
+static void establish_params (int device_num, int size);
+static void setup_tables (void);
+static void write_tables (void);
+
+
+/* The function implementations */
+
+/* Handle the reporting of fatal errors.  Volatile to let gcc know that this doesn't return */
+
+static void
+fatal_error (const char *fmt_string)
+{
+  fprintf (stderr, fmt_string, program_name, device_name);
+  exit (1);			/* The error exit code is 1! */
+}
+
+
+/* Mark the specified cluster as having a particular value */
+
+static void
+mark_FAT_cluster (int cluster, unsigned int value)
+{
+  switch( size_fat ) {
+    case 12:
+      value &= 0x0fff;
+      if (((cluster * 3) & 0x1) == 0)
+	{
+	  fat[3 * cluster / 2] = (unsigned char) (value & 0x00ff);
+	  fat[(3 * cluster / 2) + 1] = (unsigned char) ((fat[(3 * cluster / 2) + 1] & 0x00f0)
+						 | ((value & 0x0f00) >> 8));
+	}
+      else
+	{
+	  fat[3 * cluster / 2] = (unsigned char) ((fat[3 * cluster / 2] & 0x000f) | ((value & 0x000f) << 4));
+	  fat[(3 * cluster / 2) + 1] = (unsigned char) ((value & 0x0ff0) >> 4);
+	}
+      break;
+
+    case 16:
+      value &= 0xffff;
+      fat[2 * cluster] = (unsigned char) (value & 0x00ff);
+      fat[(2 * cluster) + 1] = (unsigned char) (value >> 8);
+      break;
+
+    case 32:
+      value &= 0xfffffff;
+      fat[4 * cluster] =       (unsigned char)  (value & 0x000000ff);
+      fat[(4 * cluster) + 1] = (unsigned char) ((value & 0x0000ff00) >> 8);
+      fat[(4 * cluster) + 2] = (unsigned char) ((value & 0x00ff0000) >> 16);
+      fat[(4 * cluster) + 3] = (unsigned char) ((value & 0xff000000) >> 24);
+      break;
+
+    default:
+      die("Bad FAT size (not 12, 16, or 32)");
+  }
+}
+
+
+/* Mark a specified sector as having a particular value in it's FAT entry */
+
+static void
+mark_FAT_sector (int sector, unsigned int value)
+{
+  int cluster;
+
+  cluster = (sector - start_data_sector) / (int) (bs.cluster_size) /
+	    (sector_size/HARD_SECTOR_SIZE);
+  if (cluster < 0)
+    die ("Invalid cluster number in mark_FAT_sector: probably bug!");
+
+  mark_FAT_cluster (cluster, value);
+}
+
+
+/* Perform a test on a block.  Return the number of blocks that could be read successfully */
+
+static long
+do_check (char *buffer, int try, off_t current_block)
+{
+  long got;
+
+  if (llseek (dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */
+      != current_block * BLOCK_SIZE)
+    die ("seek failed during testing for blocks");
+
+  got = read (dev, buffer, try * BLOCK_SIZE);	/* Try reading! */
+  if (got < 0)
+    got = 0;
+
+  if (got & (BLOCK_SIZE - 1))
+    printf ("Unexpected values in do_check: probably bugs\n");
+  got /= BLOCK_SIZE;
+
+  return got;
+}
+
+
+/* Alarm clock handler - display the status of the quest for bad blocks!  Then retrigger the alarm for five senconds
+   later (so we can come here again) */
+
+static void
+alarm_intr (int alnum)
+{
+  if (currently_testing >= blocks)
+    return;
+
+  signal (SIGALRM, alarm_intr);
+  alarm (5);
+  if (!currently_testing)
+    return;
+
+  printf ("%lld... ", (unsigned long long)currently_testing);
+  fflush (stdout);
+}
+
+
+static void
+check_blocks (void)
+{
+  int try, got;
+  int i;
+  static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS];
+
+  if (verbose)
+    {
+      printf ("Searching for bad blocks ");
+      fflush (stdout);
+    }
+  currently_testing = 0;
+  if (verbose)
+    {
+      signal (SIGALRM, alarm_intr);
+      alarm (5);
+    }
+  try = TEST_BUFFER_BLOCKS;
+  while (currently_testing < blocks)
+    {
+      if (currently_testing + try > blocks)
+	try = blocks - currently_testing;
+      got = do_check (blkbuf, try, currently_testing);
+      currently_testing += got;
+      if (got == try)
+	{
+	  try = TEST_BUFFER_BLOCKS;
+	  continue;
+	}
+      else
+	try = 1;
+      if (currently_testing < start_data_block)
+	die ("bad blocks before data-area: cannot make fs");
+
+      for (i = 0; i < SECTORS_PER_BLOCK; i++)	/* Mark all of the sectors in the block as bad */
+	mark_sector_bad (currently_testing * SECTORS_PER_BLOCK + i);
+      badblocks++;
+      currently_testing++;
+    }
+
+  if (verbose)
+    printf ("\n");
+
+  if (badblocks)
+    printf ("%d bad block%s\n", badblocks,
+	    (badblocks > 1) ? "s" : "");
+}
+
+
+static void
+get_list_blocks (char *filename)
+{
+  int i;
+  FILE *listfile;
+  unsigned long blockno;
+
+  listfile = fopen (filename, "r");
+  if (listfile == (FILE *) NULL)
+    die ("Can't open file of bad blocks");
+
+  while (!feof (listfile))
+    {
+      fscanf (listfile, "%ld\n", &blockno);
+      for (i = 0; i < SECTORS_PER_BLOCK; i++)	/* Mark all of the sectors in the block as bad */
+	mark_sector_bad (blockno * SECTORS_PER_BLOCK + i);
+      badblocks++;
+    }
+  fclose (listfile);
+
+  if (badblocks)
+    printf ("%d bad block%s\n", badblocks,
+	    (badblocks > 1) ? "s" : "");
+}
+
+
+/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it
+   isn't valid or TRUE if it is */
+
+static int
+valid_offset (int fd, loff_t offset)
+{
+  char ch;
+
+  if (llseek (fd, offset, SEEK_SET) < 0)
+    return FALSE;
+  if (read (fd, &ch, 1) < 1)
+    return FALSE;
+  return TRUE;
+}
+
+
+/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */
+
+static unsigned long long
+count_blocks (char *filename)
+{
+  off_t high, low;
+  int fd;
+
+  if ((fd = open (filename, O_RDONLY)) < 0)
+    {
+      perror (filename);
+      exit (1);
+    }
+
+  /* first try SEEK_END, which should work on most devices nowadays */
+  if ((low = llseek(fd, 0, SEEK_END)) <= 0) {
+      low = 0;
+      for (high = 1; valid_offset (fd, high); high *= 2)
+	  low = high;
+      while (low < high - 1) {
+	  const loff_t mid = (low + high) / 2;
+	  if (valid_offset (fd, mid))
+	      low = mid;
+	  else
+	      high = mid;
+      }
+      ++low;
+  }
+
+  close (fd);
+  return low / BLOCK_SIZE;
+}
+
+
+/* Check to see if the specified device is currently mounted - abort if it is */
+
+static void
+check_mount (char *device_name)
+{
+  FILE *f;
+  struct mntent *mnt;
+
+  if ((f = setmntent (MOUNTED, "r")) == NULL)
+    return;
+  while ((mnt = getmntent (f)) != NULL)
+    if (strcmp (device_name, mnt->mnt_fsname) == 0)
+      die ("%s contains a mounted file system.");
+  endmntent (f);
+}
+
+
+/* Establish the geometry and media parameters for the device */
+
+static void
+establish_params (int device_num,int size)
+{
+  long loop_size;
+  struct hd_geometry geometry;
+  struct floppy_struct param;
+
+  if ((0 == device_num) || ((device_num & 0xff00) == 0x0200))
+    /* file image or floppy disk */
+    {
+      if (0 == device_num)
+	{
+	  param.size = size/512;
+	  switch(param.size)
+	    {
+	    case 720:
+	      param.sect = 9 ;
+	      param.head = 2;
+	      break; 
+	    case 1440:
+	      param.sect = 9;
+	      param.head = 2;
+	      break;
+	    case 2400:
+	      param.sect = 15;
+	      param.head = 2;
+	      break;
+	    case 2880:
+	      param.sect = 18;
+	      param.head = 2;
+	      break;
+	    case 5760:
+	      param.sect = 36;
+	      param.head = 2;
+	      break;
+	    default:
+	      /* fake values */
+	      param.sect = 32;
+	      param.head = 64;
+	      break;
+	    }
+	  
+	}
+      else 	/* is a floppy diskette */
+	{
+	  if (ioctl (dev, FDGETPRM, &param))	/*  Can we get the diskette geometry? */
+	    die ("unable to get diskette geometry for '%s'");
+	}
+      bs.secs_track = CT_LE_W(param.sect);	/*  Set up the geometry information */
+      bs.heads = CT_LE_W(param.head);
+      switch (param.size)	/*  Set up the media descriptor byte */
+	{
+	case 720:		/* 5.25", 2, 9, 40 - 360K */
+	  bs.media = (char) 0xfd;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 112;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 1440:		/* 3.5", 2, 9, 80 - 720K */
+	  bs.media = (char) 0xf9;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 112;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 2400:		/* 5.25", 2, 15, 80 - 1200K */
+	  bs.media = (char) 0xf9;
+	  bs.cluster_size = (char)(atari_format ? 2 : 1);
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+	  
+	case 5760:		/* 3.5", 2, 36, 80 - 2880K */
+	  bs.media = (char) 0xf0;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 2880:		/* 3.5", 2, 18, 80 - 1440K */
+	floppy_default:
+	  bs.media = (char) 0xf0;
+	  bs.cluster_size = (char)(atari_format ? 2 : 1);
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	default:		/* Anything else */
+	  if (0 == device_num)
+	      goto def_hd_params;
+	  else
+	      goto floppy_default;
+	}
+    }
+  else if ((device_num & 0xff00) == 0x0700) /* This is a loop device */
+    {
+      if (ioctl (dev, BLKGETSIZE, &loop_size)) 
+	die ("unable to get loop device size");
+
+      switch (loop_size)  /* Assuming the loop device -> floppy later */
+	{
+	case 720:		/* 5.25", 2, 9, 40 - 360K */
+	  bs.secs_track = CF_LE_W(9);
+	  bs.heads = CF_LE_W(2);
+	  bs.media = (char) 0xfd;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 112;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 1440:		/* 3.5", 2, 9, 80 - 720K */
+	  bs.secs_track = CF_LE_W(9);
+	  bs.heads = CF_LE_W(2);
+	  bs.media = (char) 0xf9;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 112;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 2400:		/* 5.25", 2, 15, 80 - 1200K */
+	  bs.secs_track = CF_LE_W(15);
+	  bs.heads = CF_LE_W(2);
+	  bs.media = (char) 0xf9;
+	  bs.cluster_size = (char)(atari_format ? 2 : 1);
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+	  
+	case 5760:		/* 3.5", 2, 36, 80 - 2880K */
+	  bs.secs_track = CF_LE_W(36);
+	  bs.heads = CF_LE_W(2);
+	  bs.media = (char) 0xf0;
+	  bs.cluster_size = (char) 2;
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	case 2880:		/* 3.5", 2, 18, 80 - 1440K */
+	  bs.secs_track = CF_LE_W(18);
+	  bs.heads = CF_LE_W(2);
+	  bs.media = (char) 0xf0;
+	  bs.cluster_size = (char)(atari_format ? 2 : 1);
+	  bs.dir_entries[0] = (char) 224;
+	  bs.dir_entries[1] = (char) 0;
+	  break;
+
+	default:		/* Anything else: default hd setup */
+	  printf("Loop device does not match a floppy size, using "
+		 "default hd params\n");
+	  bs.secs_track = CT_LE_W(32); /* these are fake values... */
+	  bs.heads = CT_LE_W(64);
+	  goto def_hd_params;
+	}
+    }
+  else
+    /* Must be a hard disk then! */
+    {
+      /* Can we get the drive geometry? (Note I'm not too sure about */
+      /* whether to use HDIO_GETGEO or HDIO_REQ) */
+      if (ioctl (dev, HDIO_GETGEO, &geometry)) {
+	printf ("unable to get drive geometry, using default 255/63");
+        bs.secs_track = CT_LE_W(63);
+        bs.heads = CT_LE_W(255);
+      }
+      else {
+        bs.secs_track = CT_LE_W(geometry.sectors);	/* Set up the geometry information */
+        bs.heads = CT_LE_W(geometry.heads);
+      }
+    def_hd_params:
+      bs.media = (char) 0xf8; /* Set up the media descriptor for a hard drive */
+      bs.dir_entries[0] = (char) 0;	/* Default to 512 entries */
+      bs.dir_entries[1] = (char) 2;
+      if (!size_fat && blocks*SECTORS_PER_BLOCK > 1064960) {
+	  if (verbose) printf("Auto-selecting FAT32 for large filesystem\n");
+	  size_fat = 32;
+      }
+      if (size_fat == 32) {
+	  /* For FAT32, try to do the same as M$'s format command:
+	   * fs size < 256M: 0.5k clusters
+	   * fs size <   8G: 4k clusters
+	   * fs size <  16G: 8k clusters
+	   * fs size >= 16G: 16k clusters
+	   */
+	  unsigned long sz_mb =
+	      (blocks+(1<<(20-BLOCK_SIZE_BITS))-1) >> (20-BLOCK_SIZE_BITS);
+	  bs.cluster_size = sz_mb >= 16*1024 ? 32 :
+			    sz_mb >=  8*1024 ? 16 :
+			    sz_mb >=     256 ?  8 :
+					        1;
+      }
+      else {
+	  /* FAT12 and FAT16: start at 4 sectors per cluster */
+	  bs.cluster_size = (char) 4;
+      }
+    }
+}
+
+
+/* Create the filesystem data tables */
+
+static void
+setup_tables (void)
+{
+  unsigned num_sectors;
+  unsigned cluster_count = 0, fat_length;
+  unsigned fatdata;			/* Sectors for FATs + data area */
+  struct tm *ctime;
+  struct msdos_volume_info *vi = (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi);
+  
+  if (atari_format)
+      /* On Atari, the first few bytes of the boot sector are assigned
+       * differently: The jump code is only 2 bytes (and m68k machine code
+       * :-), then 6 bytes filler (ignored), then 3 byte serial number. */
+    strncpy( bs.system_id-1, "mkdosf", 6 );
+  else
+    strcpy (bs.system_id, "mkdosfs");
+  if (sectors_per_cluster)
+    bs.cluster_size = (char) sectors_per_cluster;
+  if (size_fat == 32)
+    {
+      /* Under FAT32, the root dir is in a cluster chain, and this is
+       * signalled by bs.dir_entries being 0. */
+      bs.dir_entries[0] = bs.dir_entries[1] = (char) 0;
+      root_dir_entries = 0;
+    }
+  else if (root_dir_entries)
+    {
+      /* Override default from establish_params() */
+      bs.dir_entries[0] = (char) (root_dir_entries & 0x00ff);
+      bs.dir_entries[1] = (char) ((root_dir_entries & 0xff00) >> 8);
+    }
+  else
+    root_dir_entries = bs.dir_entries[0] + (bs.dir_entries[1] << 8);
+
+  if (atari_format) {
+    bs.system_id[5] = (unsigned char) (volume_id & 0x000000ff);
+    bs.system_id[6] = (unsigned char) ((volume_id & 0x0000ff00) >> 8);
+    bs.system_id[7] = (unsigned char) ((volume_id & 0x00ff0000) >> 16);
+  }
+  else {
+    vi->volume_id[0] = (unsigned char) (volume_id & 0x000000ff);
+    vi->volume_id[1] = (unsigned char) ((volume_id & 0x0000ff00) >> 8);
+    vi->volume_id[2] = (unsigned char) ((volume_id & 0x00ff0000) >> 16);
+    vi->volume_id[3] = (unsigned char) (volume_id >> 24);
+  }
+
+  if (!atari_format) {
+    memcpy(vi->volume_label, volume_name, 11);
+  
+    memcpy(bs.boot_jump, dummy_boot_jump, 3);
+    /* Patch in the correct offset to the boot code */
+    bs.boot_jump[1] = ((size_fat == 32 ?
+			(char *)&bs.fat32.boot_code :
+			(char *)&bs.oldfat.boot_code) -
+		       (char *)&bs) - 2;
+
+    if (size_fat == 32) {
+	int offset = (char *)&bs.fat32.boot_code -
+		     (char *)&bs + MESSAGE_OFFSET + 0x7c00;
+	if (dummy_boot_code[BOOTCODE_FAT32_SIZE-1])
+	  printf ("Warning: message too long; truncated\n");
+	dummy_boot_code[BOOTCODE_FAT32_SIZE-1] = 0;
+	memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE);
+	bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff;
+	bs.fat32.boot_code[MSG_OFFSET_OFFSET+1] = offset >> 8;
+    }
+    else {
+	memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE);
+    }
+    bs.boot_sign = CT_LE_W(BOOT_SIGN);
+  }
+  else {
+    memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2);
+  }
+  if (verbose >= 2)
+    printf( "Boot jump code is %02x %02x\n",
+	    bs.boot_jump[0], bs.boot_jump[1] );
+
+  if (!reserved_sectors)
+      reserved_sectors = (size_fat == 32) ? 32 : 1;
+  else {
+      if (size_fat == 32 && reserved_sectors < 2)
+	  die("On FAT32 at least 2 reserved sectors are needed.");
+  }
+  bs.reserved = CT_LE_W(reserved_sectors);
+  if (verbose >= 2)
+    printf( "Using %d reserved sectors\n", reserved_sectors );
+  bs.fats = (char) nr_fats;
+  if (!atari_format || size_fat == 32)
+    bs.hidden = CT_LE_L(hidden_sectors);
+  else {
+    /* In Atari format, hidden is a 16 bit field */
+    __u16 hidden = CT_LE_W(hidden_sectors);
+    if (hidden_sectors & ~0xffff)
+      die("#hidden doesn't fit in 16bit field of Atari format\n");
+    memcpy( &bs.hidden, &hidden, 2 );
+  }
+
+  num_sectors = (long long)blocks*BLOCK_SIZE/sector_size;
+  if (!atari_format) {
+    unsigned fatlength12, fatlength16, fatlength32;
+    unsigned maxclust12, maxclust16, maxclust32;
+    unsigned clust12, clust16, clust32;
+    int maxclustsize;
+    
+    fatdata = num_sectors - cdiv (root_dir_entries * 32, sector_size) -
+	      reserved_sectors;
+
+    if (sectors_per_cluster)
+      bs.cluster_size = maxclustsize = sectors_per_cluster;
+    else
+      /* An initial guess for bs.cluster_size should already be set */
+      maxclustsize = 128;
+
+    if (verbose >= 2)
+      printf( "%d sectors for FAT+data, starting with %d sectors/cluster\n",
+	      fatdata, bs.cluster_size );
+    do {
+      if (verbose >= 2)
+	printf( "Trying with %d sectors/cluster:\n", bs.cluster_size );
+
+      /* The factor 2 below avoids cut-off errors for nr_fats == 1.
+       * The "nr_fats*3" is for the reserved first two FAT entries */
+      clust12 = 2*((long long) fatdata *sector_size + nr_fats*3) /
+	(2*(int) bs.cluster_size * sector_size + nr_fats*3);
+      fatlength12 = cdiv (((clust12+2) * 3 + 1) >> 1, sector_size);
+      /* Need to recalculate number of clusters, since the unused parts of the
+       * FATS and data area together could make up space for an additional,
+       * not really present cluster. */
+      clust12 = (fatdata - nr_fats*fatlength12)/bs.cluster_size;
+      maxclust12 = (fatlength12 * 2 * sector_size) / 3;
+      if (maxclust12 > MAX_CLUST_12)
+	maxclust12 = MAX_CLUST_12;
+      if (verbose >= 2)
+	printf( "FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		clust12, fatlength12, maxclust12, MAX_CLUST_12 );
+      if (clust12 > maxclust12-2) {
+	clust12 = 0;
+	if (verbose >= 2)
+	  printf( "FAT12: too much clusters\n" );
+      }
+
+      clust16 = ((long long) fatdata *sector_size + nr_fats*4) /
+	((int) bs.cluster_size * sector_size + nr_fats*2);
+      fatlength16 = cdiv ((clust16+2) * 2, sector_size);
+      /* Need to recalculate number of clusters, since the unused parts of the
+       * FATS and data area together could make up space for an additional,
+       * not really present cluster. */
+      clust16 = (fatdata - nr_fats*fatlength16)/bs.cluster_size;
+      maxclust16 = (fatlength16 * sector_size) / 2;
+      if (maxclust16 > MAX_CLUST_16)
+	maxclust16 = MAX_CLUST_16;
+      if (verbose >= 2)
+	printf( "FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		clust16, fatlength16, maxclust16, MAX_CLUST_16 );
+      if (clust16 > maxclust16-2) {
+	if (verbose >= 2)
+	  printf( "FAT16: too much clusters\n" );
+	clust16 = 0;
+      }
+      /* The < 4078 avoids that the filesystem will be misdetected as having a
+       * 12 bit FAT. */
+      if (clust16 < FAT12_THRESHOLD && !(size_fat_by_user && size_fat == 16)) {
+	if (verbose >= 2)
+	  printf( clust16 < FAT12_THRESHOLD ?
+		  "FAT16: would be misdetected as FAT12\n" :
+		  "FAT16: too much clusters\n" );
+	clust16 = 0;
+      }
+
+      clust32 = ((long long) fatdata *sector_size + nr_fats*8) /
+	((int) bs.cluster_size * sector_size + nr_fats*4);
+      fatlength32 = cdiv ((clust32+2) * 4, sector_size);
+      /* Need to recalculate number of clusters, since the unused parts of the
+       * FATS and data area together could make up space for an additional,
+       * not really present cluster. */
+      clust32 = (fatdata - nr_fats*fatlength32)/bs.cluster_size;
+      maxclust32 = (fatlength32 * sector_size) / 4;
+      if (maxclust32 > MAX_CLUST_32)
+	maxclust32 = MAX_CLUST_32;
+      if (clust32 && clust32 < MIN_CLUST_32 && !(size_fat_by_user && size_fat == 32)) {
+       clust32 = 0;
+       if (verbose >= 2)
+         printf( "FAT32: not enough clusters (%d)\n", MIN_CLUST_32);
+      }
+      if (verbose >= 2)
+	printf( "FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n",
+		clust32, fatlength32, maxclust32, MAX_CLUST_32 );
+      if (clust32 > maxclust32) {
+	clust32 = 0;
+	if (verbose >= 2)
+	  printf( "FAT32: too much clusters\n" );
+      }
+
+      if ((clust12 && (size_fat == 0 || size_fat == 12)) ||
+	  (clust16 && (size_fat == 0 || size_fat == 16)) ||
+	  (clust32 && size_fat == 32))
+	break;
+
+      bs.cluster_size <<= 1;
+    } while (bs.cluster_size && bs.cluster_size <= maxclustsize);
+
+    /* Use the optimal FAT size if not specified;
+     * FAT32 is (not yet) choosen automatically */
+    if (!size_fat) {
+	size_fat = (clust16 > clust12) ? 16 : 12;
+	if (verbose >= 2)
+	  printf( "Choosing %d bits for FAT\n", size_fat );
+    }
+
+    switch (size_fat) {
+      case 12:
+	cluster_count = clust12;
+	fat_length = fatlength12;
+	bs.fat_length = CT_LE_W(fatlength12);
+	memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8);
+	break;
+
+      case 16:
+	if (clust16 < FAT12_THRESHOLD) {
+	    if (size_fat_by_user) {
+		fprintf( stderr, "WARNING: Not enough clusters for a "
+			 "16 bit FAT! The filesystem will be\n"
+			 "misinterpreted as having a 12 bit FAT without "
+			 "mount option \"fat=16\".\n" );
+	    }
+	    else {
+		fprintf( stderr, "This filesystem has an unfortunate size. "
+			 "A 12 bit FAT cannot provide\n"
+			 "enough clusters, but a 16 bit FAT takes up a little "
+			 "bit more space so that\n"
+			 "the total number of clusters becomes less than the "
+			 "threshold value for\n"
+			 "distinction between 12 and 16 bit FATs.\n" );
+		die( "Make the file system a bit smaller manually." );
+	    }
+	}
+	cluster_count = clust16;
+	fat_length = fatlength16;
+	bs.fat_length = CT_LE_W(fatlength16);
+	memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8);
+	break;
+
+      case 32:
+	cluster_count = clust32;
+	fat_length = fatlength32;
+	bs.fat_length = CT_LE_W(0);
+	bs.fat32.fat32_length = CT_LE_L(fatlength32);
+	memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8);
+	break;
+	
+      default:
+	die("FAT not 12, 16 or 32 bits");
+    }
+  }
+  else {
+    unsigned clusters, maxclust;
+      
+    /* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on
+     * hard disks. So use 12 bit if the size of the file system suggests that
+     * this fs is for a floppy disk, if the user hasn't explicitly requested a
+     * size.
+     */
+    if (!size_fat)
+      size_fat = (num_sectors == 1440 || num_sectors == 2400 ||
+		  num_sectors == 2880 || num_sectors == 5760) ? 12 : 16;
+    if (verbose >= 2)
+      printf( "Choosing %d bits for FAT\n", size_fat );
+
+    /* Atari format: cluster size should be 2, except explicitly requested by
+     * the user, since GEMDOS doesn't like other cluster sizes very much.
+     * Instead, tune the sector size for the FS to fit.
+     */
+    bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2;
+    if (!sector_size_set) {
+      while( num_sectors > GEMDOS_MAX_SECTORS ) {
+	num_sectors >>= 1;
+	sector_size <<= 1;
+      }
+    }
+    if (verbose >= 2)
+      printf( "Sector size must be %d to have less than %d log. sectors\n",
+	      sector_size, GEMDOS_MAX_SECTORS );
+
+    /* Check if there are enough FAT indices for how much clusters we have */
+    do {
+      fatdata = num_sectors - cdiv (root_dir_entries * 32, sector_size) -
+		reserved_sectors;
+      /* The factor 2 below avoids cut-off errors for nr_fats == 1 and
+       * size_fat == 12
+       * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries
+       */
+      clusters = (2*((long long)fatdata*sector_size - 2*nr_fats*size_fat/8)) /
+		 (2*((int)bs.cluster_size*sector_size + nr_fats*size_fat/8));
+      fat_length = cdiv( (clusters+2)*size_fat/8, sector_size );
+      /* Need to recalculate number of clusters, since the unused parts of the
+       * FATS and data area together could make up space for an additional,
+       * not really present cluster. */
+      clusters = (fatdata - nr_fats*fat_length)/bs.cluster_size;
+      maxclust = (fat_length*sector_size*8)/size_fat;
+      if (verbose >= 2)
+	printf( "ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n",
+		sector_size, clusters, fat_length, maxclust );
+      
+      /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd);
+       * first two numbers are reserved */
+      if (maxclust <= (size_fat == 32 ? MAX_CLUST_32 : (1<<size_fat)-0x10) &&
+	  clusters <= maxclust-2)
+	break;
+      if (verbose >= 2)
+	printf( clusters > maxclust-2 ?
+		"Too many clusters\n" : "FAT too big\n" );
+
+      /* need to increment sector_size once more to  */
+      if (sector_size_set)
+	  die( "With this sector size, the maximum number of FAT entries "
+	       "would be exceeded." );
+      num_sectors >>= 1;
+      sector_size <<= 1;
+    } while( sector_size <= GEMDOS_MAX_SECTOR_SIZE );
+    
+    if (sector_size > GEMDOS_MAX_SECTOR_SIZE)
+      die( "Would need a sector size > 16k, which GEMDOS can't work with");
+
+    cluster_count = clusters;
+    if (size_fat != 32)
+	bs.fat_length = CT_LE_W(fat_length);
+    else {
+	bs.fat_length = 0;
+	bs.fat32.fat32_length = CT_LE_L(fat_length);
+    }
+  }
+
+  bs.sector_size[0] = (char) (sector_size & 0x00ff);
+  bs.sector_size[1] = (char) ((sector_size & 0xff00) >> 8);
+
+  if (size_fat == 32)
+    {
+      /* set up additional FAT32 fields */
+      bs.fat32.flags = CT_LE_W(0);
+      bs.fat32.version[0] = 0;
+      bs.fat32.version[1] = 0;
+      bs.fat32.root_cluster = CT_LE_L(2);
+      bs.fat32.info_sector = CT_LE_W(1);
+      if (!backup_boot)
+	backup_boot = (reserved_sectors >= 7) ? 6 :
+		      (reserved_sectors >= 2) ? reserved_sectors-1 : 0;
+      else
+	{
+	  if (backup_boot == 1)
+	    die("Backup boot sector must be after sector 1");
+	  else if (backup_boot >= reserved_sectors)
+	    die("Backup boot sector must be a reserved sector");
+	}
+      if (verbose >= 2)
+	printf( "Using sector %d as backup boot sector (0 = none)\n",
+		backup_boot );
+      bs.fat32.backup_boot = CT_LE_W(backup_boot);
+      memset( &bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2) );
+    }
+  
+  if (atari_format) {
+      /* Just some consistency checks */
+      if (num_sectors >= GEMDOS_MAX_SECTORS)
+	  die( "GEMDOS can't handle more than 65531 sectors" );
+      else if (num_sectors >= OLDGEMDOS_MAX_SECTORS)
+	  printf( "Warning: More than 32765 sector need TOS 1.04 "
+		  "or higher.\n" );
+  }
+  if (num_sectors >= 65536)
+    {
+      bs.sectors[0] = (char) 0;
+      bs.sectors[1] = (char) 0;
+      bs.total_sect = CT_LE_L(num_sectors);
+    }
+  else
+    {
+      bs.sectors[0] = (char) (num_sectors & 0x00ff);
+      bs.sectors[1] = (char) ((num_sectors & 0xff00) >> 8);
+      if (!atari_format)
+	  bs.total_sect = CT_LE_L(0);
+    }
+
+  if (!atari_format)
+    vi->ext_boot_sign = MSDOS_EXT_SIGN;
+
+  if (!cluster_count)
+    {
+      if (sectors_per_cluster)	/* If yes, die if we'd spec'd sectors per cluster */
+	die ("Too many clusters for file system - try more sectors per cluster");
+      else
+	die ("Attempting to create a too large file system");
+    }
+
+  
+  /* The two following vars are in hard sectors, i.e. 512 byte sectors! */
+  start_data_sector = (reserved_sectors + nr_fats * fat_length) *
+		      (sector_size/HARD_SECTOR_SIZE);
+  start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) /
+		     SECTORS_PER_BLOCK;
+
+  if (blocks < start_data_block + 32)	/* Arbitrary undersize file system! */
+    die ("Too few blocks for viable file system");
+
+  if (verbose)
+    {
+      printf("%s has %d head%s and %d sector%s per track,\n",
+	     device_name, CF_LE_W(bs.heads), (CF_LE_W(bs.heads) != 1) ? "s" : "",
+	     CF_LE_W(bs.secs_track), (CF_LE_W(bs.secs_track) != 1) ? "s" : ""); 
+      printf("logical sector size is %d,\n",sector_size);
+      printf("using 0x%02x media descriptor, with %d sectors;\n",
+	     (int) (bs.media), num_sectors);
+      printf("file system has %d %d-bit FAT%s and %d sector%s per cluster.\n",
+	     (int) (bs.fats), size_fat, (bs.fats != 1) ? "s" : "",
+	     (int) (bs.cluster_size), (bs.cluster_size != 1) ? "s" : "");
+      printf ("FAT size is %d sector%s, and provides %d cluster%s.\n",
+	      fat_length, (fat_length != 1) ? "s" : "",
+	      cluster_count, (cluster_count != 1) ? "s" : "");
+      if (size_fat != 32)
+	printf ("Root directory contains %d slots.\n",
+		(int) (bs.dir_entries[0]) + (int) (bs.dir_entries[1]) * 256);
+      printf ("Volume ID is %08lx, ", volume_id &
+	      (atari_format ? 0x00ffffff : 0xffffffff));
+      if ( strcmp(volume_name, "           ") )
+	printf("volume label %s.\n", volume_name);
+      else
+	printf("no volume label.\n");
+    }
+
+  /* Make the file allocation tables! */
+
+  if ((fat = (unsigned char *) malloc (fat_length * sector_size)) == NULL)
+    die ("unable to allocate space for FAT image in memory");
+
+  memset( fat, 0, fat_length * sector_size );
+
+  mark_FAT_cluster (0, 0xffffffff);	/* Initial fat entries */
+  mark_FAT_cluster (1, 0xffffffff);
+  fat[0] = (unsigned char) bs.media;	/* Put media type in first byte! */
+  if (size_fat == 32) {
+    /* Mark cluster 2 as EOF (used for root dir) */
+    mark_FAT_cluster (2, FAT_EOF);
+  }
+
+  /* Make the root directory entries */
+
+  size_root_dir = (size_fat == 32) ?
+		  bs.cluster_size*sector_size :
+		  (((int)bs.dir_entries[1]*256+(int)bs.dir_entries[0]) *
+		   sizeof (struct msdos_dir_entry));
+  if ((root_dir = (struct msdos_dir_entry *) malloc (size_root_dir)) == NULL)
+    {
+      free (fat);		/* Tidy up before we die! */
+      die ("unable to allocate space for root directory in memory");
+    }
+
+  memset(root_dir, 0, size_root_dir);
+  if ( memcmp(volume_name, "           ", 11) )
+    {
+      struct msdos_dir_entry *de = &root_dir[0];
+      memcpy(de->name, volume_name, 11);
+      de->attr = ATTR_VOLUME;
+      ctime = localtime(&create_time);
+      de->time = CT_LE_W((unsigned short)((ctime->tm_sec >> 1) +
+			  (ctime->tm_min << 5) + (ctime->tm_hour << 11)));
+      de->date = CT_LE_W((unsigned short)(ctime->tm_mday +
+					  ((ctime->tm_mon+1) << 5) +
+					  ((ctime->tm_year-80) << 9)));
+      de->ctime_ms = 0;
+      de->ctime = de->time;
+      de->cdate = de->date;
+      de->adate = de->date;
+      de->starthi = CT_LE_W(0);
+      de->start = CT_LE_W(0);
+      de->size = CT_LE_L(0);
+    }
+
+  if (size_fat == 32) {
+    /* For FAT32, create an info sector */
+    struct fat32_fsinfo *info;
+    
+    if (!(info_sector = malloc( sector_size )))
+      die("Out of memory");
+    memset(info_sector, 0, sector_size);
+    /* fsinfo structure is at offset 0x1e0 in info sector by observation */
+    info = (struct fat32_fsinfo *)(info_sector + 0x1e0);
+
+    /* Info sector magic */
+    info_sector[0] = 'R';
+    info_sector[1] = 'R';
+    info_sector[2] = 'a';
+    info_sector[3] = 'A';
+
+    /* Magic for fsinfo structure */
+    info->signature = CT_LE_L(0x61417272);
+    /* We've allocated cluster 2 for the root dir. */
+    info->free_clusters = CT_LE_L(cluster_count - 1);
+    info->next_cluster = CT_LE_L(2);
+
+    /* Info sector also must have boot sign */
+    *(__u16 *)(info_sector + 0x1fe) = CT_LE_W(BOOT_SIGN);
+  }
+  
+  if (!(blank_sector = malloc( sector_size )))
+      die( "Out of memory" );
+  memset(blank_sector, 0, sector_size);
+}
+
+
+/* Write the new filesystem's data tables to wherever they're going to end up! */
+
+#define error(str)				\
+  do {						\
+    free (fat);					\
+    if (info_sector) free (info_sector);	\
+    free (root_dir);				\
+    die (str);					\
+  } while(0)
+
+#define seekto(pos,errstr)						\
+  do {									\
+    loff_t __pos = (pos);						\
+    if (llseek (dev, __pos, SEEK_SET) != __pos)				\
+	error ("seek to " errstr " failed whilst writing tables");	\
+  } while(0)
+
+#define writebuf(buf,size,errstr)			\
+  do {							\
+    int __size = (size);				\
+    if (write (dev, buf, __size) != __size)		\
+	error ("failed whilst writing " errstr);	\
+  } while(0)
+
+
+static void
+write_tables (void)
+{
+  int x;
+  int fat_length;
+
+  fat_length = (size_fat == 32) ?
+	       CF_LE_L(bs.fat32.fat32_length) : CF_LE_W(bs.fat_length);
+
+  seekto( 0, "start of device" );
+  /* clear all reserved sectors */
+  for( x = 0; x < reserved_sectors; ++x )
+    writebuf( blank_sector, sector_size, "reserved sector" );
+  /* seek back to sector 0 and write the boot sector */
+  seekto( 0, "boot sector" );
+  writebuf( (char *) &bs, sizeof (struct msdos_boot_sector), "boot sector" );
+  /* on FAT32, write the info sector and backup boot sector */
+  if (size_fat == 32)
+    {
+      seekto( CF_LE_W(bs.fat32.info_sector)*sector_size, "info sector" );
+      writebuf( info_sector, 512, "info sector" );
+      if (backup_boot != 0)
+	{
+	  seekto( backup_boot*sector_size, "backup boot sector" );
+	  writebuf( (char *) &bs, sizeof (struct msdos_boot_sector),
+		    "backup boot sector" );
+	}
+    }
+  /* seek to start of FATS and write them all */
+  seekto( reserved_sectors*sector_size, "first FAT" );
+  for (x = 1; x <= nr_fats; x++)
+    writebuf( fat, fat_length * sector_size, "FAT" );
+  /* Write the root directory directly after the last FAT. This is the root
+   * dir area on FAT12/16, and the first cluster on FAT32. */
+  writebuf( (char *) root_dir, size_root_dir, "root directory" );
+
+  if (blank_sector) free( blank_sector );
+  if (info_sector) free( info_sector );
+  free (root_dir);   /* Free up the root directory space from setup_tables */
+  free (fat);  /* Free up the fat table space reserved during setup_tables */
+}
+
+
+/* Report the command usage and return a failure error code */
+
+void
+usage (void)
+{
+  fatal_error("\
+Usage: mkdosfs [-A] [-c] [-C] [-v] [-I] [-l bad-block-file] [-b backup-boot-sector]\n\
+       [-m boot-msg-file] [-n volume-name] [-i volume-id]\n\
+       [-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs]\n\
+       [-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors]\n\
+       /dev/name [blocks]\n");
+}
+
+/*
+ * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant
+ * of MS-DOS filesystem by default.
+ */
+static void check_atari( void )
+{
+#ifdef __mc68000__
+    FILE *f;
+    char line[128], *p;
+
+    if (!(f = fopen( "/proc/hardware", "r" ))) {
+	perror( "/proc/hardware" );
+	return;
+    }
+
+    while( fgets( line, sizeof(line), f ) ) {
+	if (strncmp( line, "Model:", 6 ) == 0) {
+	    p = line + 6;
+	    p += strspn( p, " \t" );
+	    if (strncmp( p, "Atari ", 6 ) == 0)
+		atari_format = 1;
+	    break;
+	}
+    }
+    fclose( f );
+#endif
+}
+
+/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible
+   way.  In the event that some/all of the options are invalid we need to tell the user so that something can be done! */
+
+int
+main (int argc, char **argv)
+{
+  int c;
+  char *tmp;
+  char *listfile = NULL;
+  FILE *msgfile;
+  struct stat statbuf;
+  int i = 0, pos, ch;
+  int create = 0;
+  unsigned long long cblocks;
+  
+  if (argc && *argv) {		/* What's the program name? */
+    char *p;
+    program_name = *argv;
+    if ((p = strrchr( program_name, '/' )))
+	program_name = p+1;
+  }
+
+  time(&create_time);
+  volume_id = (long)create_time;	/* Default volume ID = creation time */
+  check_atari();
+
+  while ((c = getopt (argc, argv, "AbcCf:F:Ii:l:m:n:r:R:s:S:h:v")) != EOF)
+    /* Scan the command line for options */
+    switch (c)
+      {
+      case 'A':		/* toggle Atari format */
+	atari_format = !atari_format;
+	break;
+
+      case 'b':		/* b : location of backup boot sector */
+	backup_boot = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || backup_boot < 2 || backup_boot > 0xffff)
+	  {
+	    printf ("Bad location for backup boot sector : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+	
+      case 'c':		/* c : Check FS as we build it */
+	check = TRUE;
+	break;
+
+      case 'C':		/* C : Create a new file */
+	create = TRUE;
+	break;
+
+      case 'f':		/* f : Choose number of FATs */
+	nr_fats = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || nr_fats < 1 || nr_fats > 4)
+	  {
+	    printf ("Bad number of FATs : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+
+      case 'F':		/* F : Choose FAT size */
+	size_fat = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32))
+	  {
+	    printf ("Bad FAT type : %s\n", optarg);
+	    usage ();
+	  }
+	size_fat_by_user = 1;
+	break;
+
+      case 'h':        /* h : number of hidden sectors */
+	hidden_sectors = (int) strtol (optarg, &tmp, 0);
+	if ( *tmp || hidden_sectors < 0 )
+	  {
+	    printf("Bad number of hidden sectors : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+
+      case 'I':
+	ignore_full_disk = 1;
+	break;
+
+      case 'i':		/* i : specify volume ID */
+	volume_id = strtoul(optarg, &tmp, 16);
+	if ( *tmp )
+	  {
+	    printf("Volume ID must be a hexadecimal number\n");
+	    usage();
+	  }
+	break;
+
+      case 'l':		/* l : Bad block filename */
+	listfile = optarg;
+	break;
+
+      case 'm':		/* m : Set boot message */
+	if ( strcmp(optarg, "-") )
+	  {
+	    msgfile = fopen(optarg, "r");
+	    if ( !msgfile )
+	      perror(optarg);
+	  }
+	else
+	  msgfile = stdin;
+
+	if ( msgfile )
+	  {
+	    /* The boot code ends at offset 448 and needs a null terminator */
+	    i = MESSAGE_OFFSET;
+	    pos = 0;		/* We are at beginning of line */
+	    do
+	      {
+		ch = getc(msgfile);
+		switch (ch)
+		  {
+		  case '\r':	/* Ignore CRs */
+		  case '\0':	/* and nulls */
+		    break;
+
+		  case '\n':	/* LF -> CR+LF if necessary */
+		    if ( pos )	/* If not at beginning of line */
+		      {
+			dummy_boot_code[i++] = '\r';
+			pos = 0;
+		      }
+		    dummy_boot_code[i++] = '\n';
+		    break;
+
+		  case '\t':	/* Expand tabs */
+		    do
+		      {
+			dummy_boot_code[i++] = ' ';
+			pos++;
+		      }
+		    while ( pos % 8 && i < BOOTCODE_SIZE-1 );
+		    break;
+
+		  case EOF:
+		    dummy_boot_code[i++] = '\0'; /* Null terminator */
+		    break;
+
+		  default:
+		    dummy_boot_code[i++] = ch; /* Store character */
+		    pos++;	/* Advance position */
+		    break;
+		  }
+	      }
+	    while ( ch != EOF && i < BOOTCODE_SIZE-1 );
+
+	    /* Fill up with zeros */
+	    while( i < BOOTCODE_SIZE-1 )
+		dummy_boot_code[i++] = '\0';
+	    dummy_boot_code[BOOTCODE_SIZE-1] = '\0'; /* Just in case */
+	    
+	    if ( ch != EOF )
+	      printf ("Warning: message too long; truncated\n");
+	    
+	    if ( msgfile != stdin )
+	      fclose(msgfile);
+	  }
+	break;
+
+      case 'n':		/* n : Volume name */
+	sprintf(volume_name, "%-11.11s", optarg);
+	break;
+
+      case 'r':		/* r : Root directory entries */
+	root_dir_entries = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768)
+	  {
+	    printf ("Bad number of root directory entries : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+
+      case 'R':		/* R : number of reserved sectors */
+	reserved_sectors = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff)
+	  {
+	    printf ("Bad number of reserved sectors : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+	
+      case 's':		/* s : Sectors per cluster */
+	sectors_per_cluster = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2
+		     && sectors_per_cluster != 4 && sectors_per_cluster != 8
+		   && sectors_per_cluster != 16 && sectors_per_cluster != 32
+		&& sectors_per_cluster != 64 && sectors_per_cluster != 128))
+	  {
+	    printf ("Bad number of sectors per cluster : %s\n", optarg);
+	    usage ();
+	  }
+	break;
+
+      case 'S':		/* S : Sector size */
+	sector_size = (int) strtol (optarg, &tmp, 0);
+	if (*tmp || (sector_size != 512 && sector_size != 1024 &&
+		     sector_size != 2048 && sector_size != 4096 &&
+		     sector_size != 8192 && sector_size != 16384 &&
+		     sector_size != 32768))
+	  {
+	    printf ("Bad logical sector size : %s\n", optarg);
+	    usage ();
+	  }
+	sector_size_set = 1;
+	break;
+
+      case 'v':		/* v : Verbose execution */
+	++verbose;
+	break;
+	
+      default:
+	printf( "Unknown option: %c\n", c );
+	usage ();
+      }
+  if (optind < argc)
+    {
+      device_name = argv[optind];  /* Determine the number of blocks in the FS */
+      if (!create)
+         cblocks = count_blocks (device_name); /*  Have a look and see! */
+    }
+  if (optind == argc - 2)	/*  Either check the user specified number */
+    {
+      blocks = strtoull (argv[optind + 1], &tmp, 0);
+      if (!create && blocks != cblocks)
+	{
+	  fprintf (stderr, "Warning: block count mismatch: ");
+	  fprintf (stderr, "found %llu but assuming %llu.\n",cblocks,blocks);
+	}
+    }
+  else if (optind == argc - 1)	/*  Or use value found */
+    {
+      if (create)
+	die( "Need intended size with -C." );
+      blocks = cblocks;
+      tmp = "";
+    }
+  else
+    {
+      fprintf (stderr, "No device specified!\n");
+      usage ();
+    }
+  if (*tmp)
+    {
+      printf ("Bad block count : %s\n", argv[optind + 1]);
+      usage ();
+    }
+
+  if (check && listfile)	/* Auto and specified bad block handling are mutually */
+    die ("-c and -l are incompatible");		/* exclusive of each other! */
+
+  if (!create) {
+    check_mount (device_name);	/* Is the device already mounted? */
+    dev = open (device_name, O_RDWR);	/* Is it a suitable device to build the FS on? */
+    if (dev < 0)
+      die ("unable to open %s");
+  }
+  else {
+      off_t offset = blocks*BLOCK_SIZE - 1;
+      char null = 0;
+      /* create the file */
+      dev = open( device_name, O_RDWR|O_CREAT|O_TRUNC, 0666 );
+      if (dev < 0)
+	die("unable to create %s");
+      /* seek to the intended end-1, and write one byte. this creates a
+       * sparse-as-possible file of appropriate size. */
+      if (llseek( dev, offset, SEEK_SET ) != offset)
+	die( "seek failed" );
+      if (write( dev, &null, 1 ) < 0)
+	die( "write failed" );
+      if (llseek( dev, 0, SEEK_SET ) != 0)
+	die( "seek failed" );
+  }
+  
+  if (fstat (dev, &statbuf) < 0)
+    die ("unable to stat %s");
+  if (!S_ISBLK (statbuf.st_mode)) {
+    statbuf.st_rdev = 0;
+    check = 0;
+  }
+  else
+    /*
+     * Ignore any 'full' fixed disk devices, if -I is not given.
+     * On a MO-disk one doesn't need partitions.  The filesytem can go
+     * directly to the whole disk.  Under other OSes this is known as
+     * the 'superfloppy' format.  As I don't know how to find out if
+     * this is a MO disk I introduce a -I (ignore) switch.  -Joey
+     */
+    if (!ignore_full_disk && (
+	(statbuf.st_rdev & 0xff3f) == 0x0300 || /* hda, hdb */
+	(statbuf.st_rdev & 0xff0f) == 0x0800 || /* sd */
+	(statbuf.st_rdev & 0xff3f) == 0x0d00 || /* xd */
+	(statbuf.st_rdev & 0xff3f) == 0x1600 )  /* hdc, hdd */
+	)
+      die ("Will not try to make filesystem on full-disk device '%s' (use -I if wanted)");
+
+  establish_params (statbuf.st_rdev,statbuf.st_size);	
+                                /* Establish the media parameters */
+
+  setup_tables ();		/* Establish the file system tables */
+
+  if (check)			/* Determine any bad block locations and mark them */
+    check_blocks ();
+  else if (listfile)
+    get_list_blocks (listfile);
+
+  write_tables ();		/* Write the file system tables away! */
+
+  exit (0);			/* Terminate with no errors! */
+}
+
+
+/* That's All Folks */
+/* Local Variables: */
+/* tab-width: 8     */
+/* End:             */