aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorcc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
committercc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60>2023-05-11 14:38:47 +0000
commitf5450bfd35a6410528d124f534c2b1a958cafe51 (patch)
treea808b12d6ad5343fabdec7b8918df6b4d844e03f
parent5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff)
downloaddmsdosnow-dmsdos-0.9.2.2.tar.gz
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
-rw-r--r--BUGS72
-rw-r--r--COPYING17
-rw-r--r--GPL339
-rw-r--r--INSTALL.TXT715
-rw-r--r--LGPL481
-rw-r--r--Makefile31
-rw-r--r--NEWS18
-rw-r--r--PORT_TO_WIN32113
-rw-r--r--README28
-rw-r--r--README.md1
-rw-r--r--conf.sh110
-rw-r--r--dmsdos.lsm39
-rw-r--r--doc/FAQ215
-rw-r--r--doc/cvf-fat.doc4
-rw-r--r--doc/cvfinfo.doc372
-rw-r--r--doc/dmsdos.doc1152
-rw-r--r--doc/ioctl.doc199
-rw-r--r--doc/libdmsdos.doc614
-rw-r--r--doc/messages.doc871
-rw-r--r--doc/patent.doc101
-rw-r--r--doc/testing.doc68
-rw-r--r--doc/troubleshooting.doc290
-rw-r--r--in-kernel-uninstall.sh21
-rw-r--r--in-kernel.sh87
-rw-r--r--magic.sh25
-rw-r--r--man/Makefile21
-rw-r--r--man/cvflist.124
-rw-r--r--man/cvftest.145
-rw-r--r--man/dmsdosd.166
-rw-r--r--man/dmsdosfsck.841
-rw-r--r--man/dutil.1175
-rw-r--r--man/mcdmsdos.147
-rw-r--r--mkdist.sh16
-rw-r--r--patch.sh126
-rw-r--r--patches/DIFFS.TXT260
-rw-r--r--patches/cvf-uvfat.diff-2.0.33110
-rw-r--r--patches/cvf.c147
-rw-r--r--patches/cvf.c-2.1.128147
-rw-r--r--patches/cvf.c.README70
-rw-r--r--patches/cvf.diff-2.0.331056
-rw-r--r--patches/cvf.diff-2.0.33+fat32-0.2.81074
-rw-r--r--patches/cvf.diff-2.0.341074
-rw-r--r--patches/fat-truncate-bugfix.diff13
-rw-r--r--patches/fat_cvf.txt210
-rw-r--r--patches/fat_cvf.txt-2.1.128210
-rw-r--r--patches/in-kernel-2.0.35.diff62
-rw-r--r--patches/linux-2.2.13-dmsdos.patch-116
-rw-r--r--patches/linux-2.3.30-pre6-dmsdos.patch-1284
-rw-r--r--patches/magic16
-rw-r--r--patches/msdos-rmdir-bugfix.diff19
-rw-r--r--patches/vfat-brelse-bugfix.diff15
-rw-r--r--src/Config.in82
-rw-r--r--src/Configure438
-rw-r--r--src/Configure.help365
-rw-r--r--src/Makefile242
-rw-r--r--src/Makefile.cygwinb20103
-rw-r--r--src/Makefile.kernel54
-rw-r--r--src/Makefile.module28
-rw-r--r--src/Makefile.old215
-rw-r--r--src/Menuconfig1187
-rw-r--r--src/cctest.c14
-rw-r--r--src/check.sh92
-rw-r--r--src/cmpdisk.README3
-rw-r--r--src/cmpdisk.cpp131
-rw-r--r--src/conf-wrapper.sh14
-rw-r--r--src/config.debug62
-rw-r--r--src/config.high-traffic-write64
-rw-r--r--src/config.low-traffic-write64
-rw-r--r--src/config.small-module63
-rw-r--r--src/cvflist.c264
-rw-r--r--src/cvftest.c120
-rw-r--r--src/daemon_actions.c301
-rw-r--r--src/dblspace_alloc.c710
-rw-r--r--src/dblspace_buffer.c195
-rw-r--r--src/dblspace_chk.c559
-rw-r--r--src/dblspace_compr.c711
-rw-r--r--src/dblspace_dec.c671
-rw-r--r--src/dblspace_fileops.c42
-rw-r--r--src/dblspace_fileops.c-2.0.33660
-rw-r--r--src/dblspace_fileops.c-2.1.80722
-rw-r--r--src/dblspace_fileops.c-2.3.10563
-rw-r--r--src/dblspace_interface.c1147
-rw-r--r--src/dblspace_ioctl.c971
-rw-r--r--src/dblspace_methsq.c1256
-rw-r--r--src/dblspace_tables.c760
-rw-r--r--src/dblspace_virtual.c989
-rw-r--r--src/dcread.c344
-rw-r--r--src/dmsdos-config.default65
-rw-r--r--src/dmsdos-config.h.default24
-rw-r--r--src/dmsdos.h716
-rw-r--r--src/dmsdosfsck.c674
-rw-r--r--src/dstacker_alloc.c477
-rw-r--r--src/dstacker_compr.c1234
-rw-r--r--src/dstacker_dec.c824
-rw-r--r--src/dutil.c419
-rw-r--r--src/lib_interface.c726
-rw-r--r--src/lib_interface.h189
-rw-r--r--src/listmsg.sh21
-rw-r--r--src/mcdmsdos.c409
-rw-r--r--src/msdos.fsck-wrapper126
-rw-r--r--src/msdos.fsck-wrapper2101
-rw-r--r--src/my_break.h30
-rw-r--r--src/prepare_dos_8.327
-rw-r--r--src/putdos18
-rw-r--r--src/putdos.cfg19
-rw-r--r--src/putdos_helper14
-rw-r--r--src/remove_comments.awk36
-rw-r--r--src/win32_msc50.bat65
108 files changed, 31746 insertions, 1 deletions
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..135f0e1
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,72 @@
+
+Known bugs:
+-----------
+
+- Support for 2.3.XX is broken now. It is uder development and is targeted
+ to 2.3.30 and higher. MMAP support for 2.3.XX is missing and MMAP will
+ lead to Oops.
+
+- In some 2.1.XX kernels umsdos support is broken. Get at least 2.1.94
+ if you want to use umsdos-style long filenames in CVFs. For 2.0.xx
+ and 2.2.x kernels everything is okay.
+
+- Advanced memory allocation (XMALLOC) may crash a 2.1.xx/2.2.xx system.
+ I once got a report of a serious crash with old dmsdosfs. The code
+ for XMALLOC has never been changed, but the kernel keeps changing...
+ It is known to work without problems under 2.0.xx.
+
+- Writable mmap support must be turned *on* for newer 2.1.xx and all 2.2.xx
+ kernels. The code fails to compile if it is switched off, so you'll see it
+ quite early :)
+
+- Mounting with the cvf_format=xxx mount option may succeed even if the
+ specified format xxx is not present. The CVF-FAT interface does auto-
+ detection if an invalid format is specified. This was meant as a feature
+ but seems to cause confusion. In rare cases it may lead to a CVF accessed
+ by the plain FAT driver (which does not complain on mount but it will
+ scream loudly when, for example, a 'ls' is done on the mount point).
+ This bug is fixed by updating the file cvf.c as described in INSTALL.TXT.
+
+
+Kernel bugs related to using dmsdos
+-----------------------------------
+
+- vfat brelse bug
+ Priority: Serious.
+ Affected kernels: 2.2.0, 2.2.1, 2.2.2, probably late 2.1.xx.
+ Fixed since: --not yet fixed, hopefully fixed in 2.2.3 or 2.2.4--
+ Symptom: Kernel panics with "VFS: LRU list corrupted" and hangs.
+ Fix: See file patches/DIFFS.TXT for a patch.
+
+- fat truncate bug
+ Priority: Minor.
+ Affected kernels: all 2.0.xx, all 2.1.xx, 2.2.0, 2.2.1, 2.2.2.
+ Fixed since: --not yet fixed, hopefully fixed in 2.2.3 or 2.2.4--
+ Symptom: Kernel can write to read-only mounted CVF and corrupt the FAT.
+ Happens very rarely and requires a slightly damaged filesystem
+ to provoke this bug.
+ Fix: See file patches/DIFFS.TXT for a patch.
+
+- msdos rmdir bug
+ Priority: Minor.
+ Affected kernels: 2.0.34, 2.0.35, FAT32-patched 2.0.33 or earlier,
+ some 2.1.xx (unknown which ones)
+ Fixed since: 2.0.36, unknown for 2.1.xx
+ Symptom: Removing non-empty directories succeeds but leaves lost cluster
+ chains behind.
+ Fix: See file patches/DIFFS.TXT for a patch.
+
+- fat add cluster / fat extend dir for 2.3.xx
+ Priority: Serious.
+ Affected kernels: 2.2.?-2.2.13+, 2.3.?-2.3.30+
+ Fixed since: --not yet fixed, hopefully fixed in 2.2.15 and 2.3.31--
+ Symptom: File or directory (only dir for 2.3.x) cannot be enlargered.
+ Fix: See file patches/DIFFS.TXT for a patch.
+
+- fat read super
+ Priority: Serious.
+ Affected kernels: 2.3.?-2.3.30+
+ Fixed since: --not yet fixed, hopefully fixed in 2.3.31--
+ Symptom: Oops during CVF mount phase.
+ Fix: See file patches/DIFFS.TXT for a patch.
+
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..da037af
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,17 @@
+
+The dmsdos package is distributed under the terms and conditions of the
+GNU General Public License. The original text of this License can be found
+in file GPL. This means, in short words, you may use, modify and/or
+redistribute the package given that you include full source code, the
+names of all authors, and the original License text.
+
+For the dmsdos library there's an exception: you may, at your choice, use
+it under the terms and conditions of the GNU Library General Public License
+instead. The original text of this License can be found in file LGPL.
+The difference is, in short words, the LGPL allows you to distribute a
+program linked with the library under your own license terms.
+
+If you do not accept the License, the software is subject to Copyright Law.
+This means you may still use it, but you may not modify or redistribute it.
+
+Please read the files GPL and LGPL for details.
diff --git a/GPL b/GPL
new file mode 100644
index 0000000..a43ea21
--- /dev/null
+++ b/GPL
@@ -0,0 +1,339 @@
+ 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/INSTALL.TXT b/INSTALL.TXT
new file mode 100644
index 0000000..57ad4b7
--- /dev/null
+++ b/INSTALL.TXT
@@ -0,0 +1,715 @@
+DMSDOS + CVF-FAT installation
+-----------------------------
+
+*** These are installation instructions for use under LINUX. If you want to
+ install dmsdos in a Dos/Win32 environment see file PORT_TO_WIN32. ***
+
+
+If you want to install dmsdos without bothering all the details, please
+read at least the chapter 'Easy Installation'. If that does not work for
+you, try 'Manual Installation'.
+
+*** FAT DRIVER BUG WARNING:
+
+ * FAT32 kernels earlier than 2.0.36 have a serious rmdir bug
+ that causes filesystem corruption in a msdos filesystem.
+ See file patches/DIFFS.TXT where to find a patch to fix this
+ (patches/msdos-rmdir-bugfix.diff).
+
+ * All current kernels from the 2.0, 2.1 and 2.2 series have a bug that
+ can do write access to a FAT filesystem though it is mounted read-only.
+ Though it only occurs in a rare situation, this bug can especially be
+ triggered by dmsdos. Also see file patches/DIFFS.TXT where to find a
+ patch to fix this (patches/fat-truncate-bugfix.diff).
+
+ * All current 2.2 kernels (this means at least up to 2.2.2; 2.2.3 has
+ not been released yet at the time of writing) (and maybe late 2.1
+ kernels too) have a serious bug in the vfat driver that can cause
+ system hang ("kernel panic: VFS: LRU list corrupted") due to filesystem
+ buffer list corruption. Also see file patches/DIFFS.TXT where to find a
+ patch to fix this (patches/vfat-brelse-bugfix.diff). IT IS VITAL FOR
+ USING DMSDOS IN A VFAT PARTITION TO HAVE THIS BUG FIXED.
+
+ These bugs are not fixed automatically during installation. If you are
+ in doubt, please check the diff files and your kernel source. You can use
+ dmsdos without these bugfixes, but you may crash or hang your system or
+ lose data. YOU HAVE BEEN WARNED.
+
+
+A. Easy Installation
+====================
+
+ Dmsdos comes with a set of scripts for automatic installation. If you do not
+ like some scripts poke around in your system, see chapter 'Manual
+ Installation' :)
+
+ Okay, just a small warning: automatic installation can never be perfect.
+ For a 2.0.xx kernel I recommend to make a backup copy of your kernel sources
+ if you cannot restore it easily. Dmsdos needs to patch the 2.0.xx kernels
+ a bit. This is not a problem if you are using unmodified kernel sources
+ from 2.0.29 to 2.0.36 (others are currently not tested), but if your kernel
+ is highly hacked or patched the patch might go wrong and need manual
+ correction. If you are not familiar with hacking around in failed patches,
+ I think, you will be lucky to restore the old state :)
+
+ 2.1 and 2.2 kernels do not need a patch, but you may choose to update the
+ CVF-FAT interface (though it still works with the old one). The updated
+ files have not been included into the official kernel yet (at the time of
+ writing, current=2.2.2). I hope they go into 2.2.3 or 2.2.4. :) A
+ description of how and what to update can be found in patches/cvf.c.README.
+
+ And, of course, you should ensure you have fixed the kernel FAT driver bugs
+ listed above :)
+
+
+ 1. Prepare automatic installation:
+ ----------------------------------
+
+ You need, of course, full kernel sources in /usr/src/linux (which may be a
+ symbolic link to the real kernel source directory). No discussion here,
+ I say you _need_ or automatic installation refuses to start. You should
+ also ensure that the kernel version that you are currently running and the
+ sources in /usr/src/linux do match. Dmsdos may install the module at a
+ wrong place if they don't match, which does no harm but somewhat confuses
+ both the module subsystem and the system administrator :)
+
+ * If you have installed a previous dmsdos version:
+
+ Check the version number. If it's 0.8.x or lower you have a problem.
+ You must completely uninstall that version before. How to do so, see the
+ documentation of that old version (it should have some sripts for that
+ purpose). If you really can't uninstall it, throw away your old kernel
+ sources and get fresh ones and remove the old dmsdos binaries that may
+ be lying around in directories in your path.
+
+ Dmsdos 0.9.0 and newer need not be uninstalled prior to an upgrade.
+
+
+ 2. Beginning installation:
+ --------------------------
+
+ Go into the directory where the dmsdos sources untarred and enter a
+ 'make'. That should do it. The installation script will ask some
+ questions. The script always waits for your confirmation before it
+ changes anything in your kernel sources or your system. You can exit
+ with CTRL-C at any stage (and continue with manual installation).
+ Be also prepared that the script calls kernel configuration and
+ recompiles your kernel (that depends on your system setup).
+
+ * If problems arise:
+
+ Automatic installation stops if it finds that something has gone wrong
+ (i.e. a patch failed).
+
+ * If the script calls kernel configuration:
+
+ This means something that is necessary for dmsdos to work correctly
+ has been turned off in your kernel configuration. For dmsdos you need:
+
+ - Module support
+ - Loopback block device (may be compiled as module)
+ *** NOTE: This one can be found under 'Additional block devices'.
+ It has nothing in common with the network loopback interface
+ so please don't mix them up :)
+ - FAT filesystem (may be compiled as module)
+ - MSDOS or VFAT filesystem (may be compiled as module)
+ - since 2.0.34 also NLS support (but this is already required by FAT)
+
+ * If the sript stops and asks for kernel and module installation and
+ for reboot:
+
+ Well I hate scripts that install new kernels and reboot automatically.
+ So the dmsdos installation refuses to do that. Usually, the script wants
+ you to do
+
+ cd /usr/src/linux
+ make zlilo
+ make modules_install
+ reboot
+
+ or similar commands that have the same effect. You can do that
+ immediately, but you can also continue without rebooting. But, please
+ remember to do that before _using_ dmsdos the first time :)
+
+ YOU SHOULD KNOW WHAT YOU ARE DOING. If not, read for example
+ the KERNEL-HOWTO or related documentation.
+
+ Note that dmsdos installation is not complete yet. Rerun 'make' in the
+ directory where the dmsdos sources untarred.
+
+ * If it asks strange questions about dmsdos configuration:
+
+ Consult the "online" help or accept the default settings if you don't
+ understand what you are doing. You can rerun the dmsdos configuration
+ script again at any time later if you want to play with the options.
+ See 'Manual instalation' section 'Compiling the sources' for details.
+
+
+ 3. Completing installation
+ --------------------------
+
+ Okay, if all has completed without errors, it's time for the first test.
+ If the script asked for kernel and modules installation and for reboot
+ but you haven't done it yet, please do it now.
+
+ The first test and everything beyond that is described in part
+ 'C. The first test'.
+
+
+B. Manual Installation
+======================
+
+ 1. Preparing installation
+ -------------------------
+
+ * Upgrade from dmsdosfs 0.8.x(.y) or earlier: *** WARNING ***
+
+ If you have an earlier version of the dmsdos filesystem installed (i.e.
+ dmsdos 0.8.x), you'd better throw away your kernel sources and get fresh
+ ones. You must remove all old dmsdos sources, especially header files, and
+ executables or modules. The new code doesn't install the dmsdos module in
+ the kernel source tree, so this is very important. To be sure, do a
+ 'find / -name "*dmsdos*" -print', or check your path for old executables
+ and your /lib/modules/* directories for old modules. Okay, I promise that
+ this kind of cleanup will no longer be necessary for further upgrades :)
+
+ * Upgrade from 0.9.0(.x) or higher:
+
+ You don't need to reconfigure or recompile your kernel. You don't
+ need to patch the kernel again. Just compile the new dmsdos module
+ and the updated dmsdos utilities.
+
+ * Fresh installation:
+
+ If you have never used an older version of dmsdos before, you don't
+ have to remove anything :) But, depending on your kernel version, you
+ may have to patch your kernel sources. You may also have to reconfigure
+ and recompile your kernel even if there's no patch needed.
+
+ *** Please ensure to have the kernel FAT driver bugs fixed (the bugs are
+ listed at the beginning of this ducument).
+
+
+ 2. Check kernel for CVF-FAT and install it if it is missing
+ -----------------------------------------------------------
+
+ Make sure you have a kernel version 2.0.29 or newer from the 2.0 series
+ or a kernel version 2.1.94 or newer from the 2.1 development series or
+ any 2.2.x kernel. Older kernels may work too, but this has never been
+ tested. If you are using older kernels and run into problems please don't
+ expect me to fix them - it's too much work to keep a multi-kernel dmsdos
+ package work with all old and new kernels :)
+
+ Dmsdos version 0.9.0 and higher depend on the CVF-FAT interface in the
+ kernel. This interface is already present in kernels 2.1.80 and newer,
+ and it is also compatible with UMSDOS in 2.1.xx since 2.1.94.
+ So if you have 2.1.80 or newer you do not need a patch. But you may want
+ to update the CVF-FAT interface. The new interface has been suggested to
+ be included in newer kernels, but at the time of writing this it has not
+ yet gone in. See patches/cvf.c.README for instructions how and what to
+ update. Note that you do _not_ have to update - the old interface still
+ works (but it has a minor bug and lacks kerneld or kmod support).
+
+ For 2.0.xx kernels you need to apply at least one of the CVF-FAT patches.
+ Dmsdos comes with a collection of patches for different setups. If you
+ are brave you can enter a 'make patch'. This starts a script that tries
+ to find out automatically whether you need a patch and which one, and it
+ installs the patches it thinks you need. Be warned, the script is not
+ perfect. It is known to work if you do not have an unusual setup (well,
+ it works for a plain 2.0.33 kernel, also with a fat32 patched 2.0.33
+ kernel and with the newer 2.0.34 up to 2.0.36).
+
+ If automatic patching fails or if your setup is a highly patched or hacked
+ kernel you might want to take a look at further instructions. Please read
+ the file patches/DIFFS.TXT to find out which patch you need and how to
+ install it.
+
+
+ 3. Check kernel configuration
+ -----------------------------
+
+ For dmsdos you need:
+
+ - Module support
+ - Loopback block device (may be compiled as module)
+ *** NOTE: This one can be found under 'Additional block devices'.
+ It has nothing in common with the network loopback interface
+ so please don't mix them up :)
+ - FAT filesystem (may be compiled as module)
+ - MSDOS or VFAT filesystem (may be compiled as module)
+ - since 2.0.34 also NLS support (but this is already required by FAT)
+
+ If you don't know whether all of them are present you can enter a
+ 'make kernelconf'. This calls a script that checks your kernel
+ configuration and calls the interactive kernel 'Configure' script if
+ something is missing. In that case, just go through the questions,
+ mostly accepting the defaults, but be sure to say Y for module support,
+ Y or M for loopback block device, Y or M for FAT filesystem support,
+ and Y or M for the MSDOS or VFAT filesystem. Then the script tries to
+ compile your kernel. In some cases the script tends to recompile your
+ kernel though it isn't really necessary. So don't be surprised - it's
+ a dumb script. :) Note that the script does not *install* the new kernel
+ and the new modules.
+
+ Refer to the kernel documentation for details about kernel configuration.
+
+ If you do not want to let the dmsdos Makefile call the script but you
+ want to do it 'by hand' you can do something like this:
+
+ cd /usr/src/linux
+ make config (or 'make menuconfig' if you prefer)
+ make dep
+ make clean
+ make zImage (or 'make bzImage' if you need it)
+ make modules
+
+ When the kernel and the modules have been compiled, they must be
+ installed. This is usually done with these commands:
+
+ cd /usr/src/linux
+ make zlilo
+ make modules_install
+ reboot
+
+
+ 4. Compile the dmsdos sources
+ -----------------------------
+
+ Go into the directory where the dmsdos package untarred and do
+
+ cd src
+ make config (or 'make menuconfig' if you prefer)
+
+ Yes, there's a configuration script that asks some questions now.
+ You very likely know this procedure from the Linux kernel. In fact,
+ the scripts have been copied from the kernel and a bit hacked :-)
+ Well, there's no xconfig (I think this is overkill).
+
+ If you have no clue what to answer to the questions, consult the online
+ help or accept the defaults (which should be safe and work for all
+ systems). As I have received a lot of mail about that, the dmsdos
+ configuration has now a normal and an expert mode. In normal mode,
+ only the most important questions (which should be understood by a
+ novice without problems) show up.
+
+ For convenience, there are some pre-configured files which can be loaded
+ with the 'Load an Alternate Configuration File' menu item during
+ 'make menuconfig'. Currently, these files are available:
+
+ config.small-module - for a small dmsdos module (e.g. for bootdisk)
+ config.low-traffic-write - for low-traffic write access to a CVF
+ config.high-traffic-write - for high-traffic write access to a CVF
+ config.debug - for safe low-level dmsdos debugging
+
+ After loading the alternate config file you'd better check the settings
+ (just walk through all the menus). Especially if you want a small module
+ exclude the CVF format drivers you don't need.
+
+ The text-based 'make config' can also use a pre-configured file. Just copy
+ the config.what-you-like to .config and run 'make config'.
+
+ Then continue with these steps:
+
+ make dep
+ make clean
+ make
+
+ The last line starts compiling the sources.
+
+ There were problems with some old versions of binutils reported (it's a
+ bug in old GNU assembler versions). Have a look at the Makefile for some
+ hacks.
+
+ If you want a shared dmsdos library instead of a static one you have
+ to edit the Makefile. Using the shared library is currently strongly
+ discouraged (well _not_ because of bugs). See the documentation for
+ the reasons.
+
+ If you run into strange problems, you can try to compile each part of
+ dmsdos separately:
+
+ make dmsdos.o - compiles the dmsdos module dmsdos.o (required)
+ make dutil - compiles the dmsdos utility dutil (useful)
+ make dmsdosd - compiles the external dmsdos daemon (nice)
+ make libdmsdos.a - compiles the dmsdos library
+ make dcread - compiles a sample program using libdmsdos
+ make dmsdosfsck - Ahh... it's there :)
+ make mcdmsdos - compiles a mc external fs interface tool
+
+ If you can't get the external daemon compiled try the internal daemon
+ (though most people never need the daemon). It can be enabled during
+ configuration ('make config').
+
+ If you can't get it compiled at all, have a look at the Makefile in the
+ src directory. It has some switches to work around some problems (gas
+ bugs, unusual systems, switch off cpu specific optimization, switch off
+ inline assembler statements, etc). This should not be necessary, but some
+ systems might need it.
+
+ If you still can't get it compiled, please send a bug report including a
+ detailed description of your system (version of kernel, gcc, binutils etc)
+ and what error messages are shown. If you know how to fix the problem
+ please also let me know :) A checklist about the details I usually need
+ for tracing down a problem can be found in doc/dmsdos.doc.
+
+
+C. The first test
+=================
+
+ 1. Check whether the module loads correctly:
+ --------------------------------------------
+
+ Try a
+
+ insmod -m dmsdos > /tmp/dmsdos.map
+
+ Check the file /tmp/dmsdos.map for error messages.
+ Depending on your modutils version, you may have to use depmod or
+ modprobe instead of insmod. (Please forgive me, I never understood the
+ difference. Maybe I should read the documentation some day :) )
+
+ If the module fails to load, make sure you are using the right insmod
+ version (there are lots of modutils versions for 2.1.xx - most don't
+ work correctly under all kernel versions; some are even said not to work
+ at all). If it still fails, try to load the fat module (and since kernel
+ 2.0.34 also the nls module) before. If it complains about a missing symbol,
+ try to find out which part of the kernel the symbol belongs to, and load
+ the appropriate module before or compile that part into your kernel.
+ If it continues to fail this is a bug. If it prints out strange errors
+ like "kernel_version needed ...blah..." and you are using a 2.2.x kernel
+ get the latest module utilities for 2.1.xx kernels (I ran into this
+ problem too :) ).
+
+ Note that, for a mixed 2.0/2.2 system, I need modutils-2.1.121 (compiled
+ against 2.2.x headers) but I must keep the kerneld binary from
+ modutils-2.0.0 (compiled against 2.0.x headers). Ughly, but the newer
+ kerneld provided with modutils-2.1.121 breaks my 2.0.x system :(
+
+ Please always load the module with the -m flag and redirect the
+ output, which contains a symbol table, to a file. This file is needed
+ in case there is a crash with a register dump. Please use this symbol
+ table to find the function where the problem occured. (Unfortunately,
+ this table usually differs from one insmod time to another depending on
+ actually free memory pages, so you can't just create the table once and
+ then use it for all dumps later.) If you don't know how to interpret a
+ register dump, please have a look at Linus' excellent instructions for
+ that in the file README in /usr/src/linux. It is really important that
+ *you* try to interpret the register dump since it is absolutely system
+ dependent. THIS IS NOT DIFFICULT. YOU DO NOT HAVE TO KNOW C PROGRAMMING
+ FOR THAT.
+
+ Let me repeat this. I just cannot track down register dumps since they
+ are just some numbers, even to me :) Those numbers only make sense in
+ conjunction with the symbol table that was printed by insmod when the
+ module was loaded the last time before the crash. Thus, bug reports
+ without your interpretation by means of the symbol table are Really
+ Worthless and will be ignored.
+
+
+ 2. Test whether dmsdos can read a CVF
+ -------------------------------------
+
+ For the first test be sure to have the loop module, the fat module,
+ the dmsdos module and the msdos or vfat module loaded. If you are using
+ kerneld you might not have to care about most of them. Just the fat
+ module may have to be loaded manually in that case in order to load
+ the dmsdos module.
+
+ Mount your Dos/Win95 partition that is the host for a compressed drive
+ as usual (if you haven't done this already).
+
+ Assuming the large Compressed Volume File (CVF) is /DOS/dblspace.001 and
+ in doublespace format and you want to mount it under /mnt, try this
+ (it mounts READ-ONLY):
+
+ mount -t msdos -o ro,loop,cvf_format=dblspace /DOS/dblspace.001 /mnt
+
+ Please also use the "cvf_format=dblspace" option for drivespace drives.
+ If the drive is in stacker format and the CVF is /DOS/stacvol.001 the
+ command looks like this:
+
+ mount -t msdos -o ro,loop,cvf_format=stacker /DOS/stacvol.001 /mnt
+
+ If the _compressed_ filesystem contains long filenames, you may want
+ to use the VFAT driver instead of the MSDOS driver:
+
+ mount -t vfat -o ro,loop,cvf_format=dblspace /DOS/dblspace.001 /mnt
+
+ Note that you can leave out the "cvf_format=xxx" option. In that case,
+ dmsdos automatically detects the CVF format. But it is recommended to
+ specify always a "cvf_format=xxx" option.
+
+ (If you leave out the "cvf_format=xxx" option there can be a strange
+ error condition that is not immediately visible and causes awful
+ trouble some time later:
+
+ If you leave out the "cvf_format=xxx" option and the dmsdos module
+ has not been loaded the plain FAT driver gets the compressed
+ filesystem, which it cannot handle. Unfortunately, a compressed FAT
+ filesystem looks very similar to an uncompressed one, so the FAT
+ driver may not refuse to mount the filesystem with an error message.
+
+ *** This means, the mount command may succeed even if the dmsdos
+ module is not present. In that case, the FAT driver - wrongly -
+ assumes the mounted filesystem is _not_ compressed and it can
+ crash, hang or destroy the filesystem later when you try to
+ access it.
+
+ So, *please*, always specify a "cvf_format=xxx" option for safety,
+ as it just prints an error if the dmsdos module is not loaded.)
+
+ Now you can go into the /mnt directory and do some read access tests.
+ If there are some problems, the dmsdos driver becomes very noisy and logs
+ a lot of messages in the syslog. Not all of them are real errors, though.
+ Some are just information about the kind of CVF detected and the results
+ of some probing functions. Yes, there are different kinds of CVF formats
+ throughout the Dos/Win95 versions :)
+
+ Note that, at mount time, for all doublespace and drivespace format CVFs
+ one (and only one) loop device error like "access beyond end of device"
+ or similar appears. This is normal and results from dmsdos working around
+ a limitation in the loop driver. This is *not* a dmsdos bug.
+
+
+D. Permanent installation
+=========================
+
+ *** WARNING ***
+ If you are upgrading from a dmsdos release in the range 0.9.0 to 0.9.1.1,
+ be sure to remove the old dmsdos module from the module path. The default
+ location has changed from /lib/modules/misc/ to /lib/modules/`uname -r`/fs/.
+ So the old module will not be overwritten and insmod may load the wrong
+ module if there are more than one dmsdos module lying around :)
+ Note that the default location where the module is installed may be a bad
+ choice if you switch kernel versions often. You may have to compile a
+ seperate module for each kernel version. If you prefer the module to be
+ installed at a different location feel free to edit the Makefile :)
+
+ If you are sure it works, you can install it permanently on your system
+ by doing
+
+ make install
+
+ This copies the module, binaries and manpages to standard system
+ directories (usually under /lib/modules and /usr/local/[bin|man] - check
+ the Makefile for the locations). All the files that are copied by
+ 'make install' are removed by 'make uninstall'.
+
+
+D. Other things to notice
+=========================
+
+ 1. Some BUG WARNINGS important to know for installation
+ -------------------------------------------------------
+
+ * UMSDOS related bugs:
+
+ a) *** IN SOME 2.1.XX KERNELS UMSDOS SUPPORT IS COMPLETELY BROKEN ***
+ Please avoid kernels from 2.1.0 to 2.1.93. Use 2.1.94 or newer.
+ Even better: Upgrade to 2.2.
+
+ b) If the UMSDOS module fails to load and complains about two
+ missing symbols 'fat_readpage' and 'fat_dir_ioctl':
+ I forgot to add some symbols to the export list in older cvf.diffs for
+ kernel 2.0.33. This bug also went in 2.1.80 when CVF-FAT was included.
+ In order to fix the problem, edit file linux/fs/fat/fatfs_syms.c and
+ append the missing symbols 'fat_readpage' and 'fat_dir_ioctl' to the
+ export list. Look how the other symbols are listed there and put in the
+ missing ones in the same way. (You don't need to know C programming for
+ that. Just use your common sense. You'll know what to do when you see
+ what's in that the file.) Then recompile the fat module (or the kernel
+ if your fat support isn't modular).
+
+ I have seen that the problem is fixed in 2.1.128, but I did not verify
+ older kernels. Well that shouldn't matter as you always should use the
+ latest kernel if you want a development kernel :))
+
+ * FAT driver bugs:
+
+ See the list above at the beginning of this document.
+
+
+ 2. Using kerneld or kmod to load dmsdos
+ ---------------------------------------
+
+ This is an installation tip for the convenient system hacker. It is
+ not the default installation, and it is only recommended for dmsdos
+ versions that are known to be stable.
+
+ Please note that in latest 2.1.xx kernels kerneld has been replaced by
+ kmod. This does not make a difference for dmsdos.
+
+ In dmsdos-0.9.2.0 kerneld-autoload behaviour again has been changed. The
+ new behaviour is now believed to be the "right" way :-) If you are using
+ a CVF-FAT patch from an older dmsdos version (0.9.2.0-pre-3 and older) or
+ a kernel 2.2.2 or older you should update the CVF-FAT interface
+ (see patches/cvf.c.README) - otherwise it may not work. The updated files
+ have been suggested to be included into the kernel, but have not yet
+ gone in.
+
+ If you do not know what kerneld or kmod is and what it does, please read
+ the KERNELD-MINI-HOWTO, for example, or refer to the documentation that
+ comes with the Linux kernel sources (for kmod).
+
+ CVF-FAT now triggers kerneld or kmod to load missing CVF modules
+ according to the following rules:
+
+ 1. If a fat based filesystem is mounted with the mount option
+ "cvf_format=autoload", a module with the name "cvf_autoload" is
+ requested. Well, such a module never exists. This does however
+ make sense together with a special modules configuration.
+
+ 2. If a fat based filesystem is mounted with the mount option
+ "cvf_format=xxx" (where xxx has to be replaced with the name of
+ the format) a module "xxx" is requested.
+
+ Usually there is a difference between the CVF format name and the
+ module name. This can be corrected with 'alias' statements in the
+ modules configuration file /etc/modules.conf (or /etc/conf.modules,
+ both file names are common).
+
+ For dmsdos, you need the lines
+
+ alias stacker dmsdos
+ alias dblspace dmsdos
+ alias cvf_autoload dmsdos
+
+ in the configuration file. Note that kerneld needs a signal SIGHUP in
+ order to re-read the configuration file. Also note that this setup
+ directly loads the module and does not generate a symbol table (which is
+ required for debugging, for example to analyse a crash somewhere in the
+ module).
+
+ If you need a symbol table from CVF module loading, you are out of luck.
+ Load the modules manually in that case, please.
+
+ If you want to add support for loading other CVF modules (do they exist?
+ Let me know) with auto-detection (cvf_format=autoload), add "pre-install"
+ lines for them, e.g.
+
+ pre-install cvf_autoload insmod -k cvf_mod1
+ pre-install cvf_autoload insmod -k cvf_mod2
+ ...
+ (untested, should work in theory. I do not know other CVF modules.).
+
+ Problems with automatic loading of CVF modules should be solved by
+ hacking /etc/modules.conf. It is not recommended to fix them by changing
+ the dmsdos or kernel sources. If it does not work at all try another
+ kerneld version. Not every kerneld version runs correctly under
+ every kernel version. (Well this was probably one of the inofficial
+ reasons to replace it with kmod in latest 2.1.xx kernels...)
+
+
+ 3. Using dmsdos as external filesystem for Midnight Commander
+ -------------------------------------------------------------
+
+ Dmsdos comes with an interface utility that accepts standard Midnight
+ Commander commands for reading archive files. The utility is named
+ 'mcdmsdos' and is compiled during usual dmsdos compile process.
+ The utility is currently read-only. It follows the specification of
+ Midnight Commander version 3.0.
+
+ Please refer to the documentation of Midnight Commander for further
+ information on how to configure an external filesystem.
+
+ You may want to write a small shell script as a wrapper around mcdmsdos
+ in order to suppress output to stderr distorting the screen. For
+ example, this (in the same directory where mcdmsdos resides), as it
+ does not need to know the absolute path:
+
+ #!/bin/bash
+ MCDMSDOS=`dirname $0`/mcdmsdos
+ $MCDMSDOS $* 2>/dev/null
+
+
+ 4. Compiling dmsdos as a fix part of the kernel
+ -----------------------------------------------
+
+ For some purpose it may be desired to compile dmsdos as a fix part of
+ the kernel (i.e. not as a loadable kernel module) so dmsdos is present
+ immediately at bootup. This would probably be useful for a boot disk or
+ an emergency rescue disk, for example. (This saves you all the work to
+ setup an initrd disk which is somewhat complex.)
+
+ BE WARNED. This is not the default installation. This is not well tested.
+ The patches for that setup have been created under Linux 2.0.35 and may
+ fail for other kernels (that's unlikely because they are small, just a
+ few lines, and it's easy to repair manually if they do fail.)
+
+ Okay, if you really want to do this, just install dmsdos as usual.
+ Test whether it works. I'd also recommend to _write_ _down_ the dmsdos
+ configuration settings, especially if you use expert mode (because the
+ default values are not available later on when you might need them -
+ due to my lazyness).
+
+ After that, run the shell script 'in-kernel.sh' which just copies the
+ dmsdos sources into the kernel source tree and patches some files to
+ hang dmsdos in (have a look at the in-kernel*.diff if you want to know
+ exactly what is changed). You _very_ likely want to create a backup of
+ kernel sources before :)
+
+ Then reconfigure your kernel. Answer 'Y' for loopback block device and
+ for the fat filesystem (also for NLS since Linux 2.0.34). Then enable
+ DMSDOS support. You'll see the usual dmsdos configuration after that, but
+ you won't have any default values (this is a limitation of the 2.0.xx
+ configuration scripts and I am too lazy to create a workaround diff).
+ Don't forget to say 'Y' for the MSDOS or VFAT driver after that.
+
+ Just some further warnings about that setup:
+
+ * There's no easy way back. If you installed dmsdos as a fix part of the
+ kernel the best way to get rid of it is getting fresh kernel sources.
+ Well, you can disable it in kernel configuration. You can also try
+ to run the script 'in-kernel-uninstall.sh', but no promise.
+
+ * dmsdos may no longer compile in its own directory due to macro
+ redefinitions in the kernel include files. But dmsdos can now be
+ compiled as module in the same way like all other kernel modules
+ (i.e. run kernel configuration, answer 'M' to DMSDOS support, do
+ 'make modules' and 'make modules_install').
+
+ * This setup is not recommended at all. YOU HAVE BEEN WARNED :)
+
+
+ 5. Mounting CVFs from /etc/fstab at boot time
+ ---------------------------------------------
+
+ It may be a bit tricky to get CVFs mounted at boot time. An /etc/fstab
+ entry for a CVF usually looks like this:
+
+ /DOS/drvspace.001 /DOSF msdos loop,cvf_format=dblspace 1 0
+ or
+ /DOS/stacvol.001 /DOSF msdos loop,cvf_format=stacker 1 0
+
+ It is really important that the sixth field (the 'pass number') is zero
+ in order to prevent the filesystem from being checked at boot time.
+ (If you really want to check dos partitions *and* CVFs at boot time you
+ very likely must hack your bootup scripts. This is not recommended,
+ however, unless you are an experienced system administrator and know in
+ detail how your bootup scripts work. This is not explained in the dmsdos
+ documentation.)
+
+ In the usual one-pass 'mount -a' procedure, I noticed a strangeness
+ regarding the order in /etc/fstab: The filesystems seem to be
+ mounted in reverse order. Thus
+
+ /DOS/drvspace.001 /DOSF msdos loop,cvf_format=dblspace 1 0
+ /dev/hda1 /DOS msdos defaults 1 0
+
+ works on my system while
+
+ /dev/hda1 /DOS msdos defaults 1 0
+ /DOS/drvspace.001 /DOSF msdos loop,cvf_format=dblspace 1 0
+
+ doesn't. So just be prepared to play around a bit with the fstab.
+
+
+ 6. Further information
+ ----------------------
+
+ The main dmsdos documentation is in doc/dmsdos.doc. Some other files in
+ the doc directory may be useful, too. Take a look at them if you want to
+ know more details or technical information on how dmsdos works. Please
+ also take a look at the documentation if you run into problems before
+ sending a bug report. Yes, there is a list with common problems and their
+ solutions :)
diff --git a/LGPL b/LGPL
new file mode 100644
index 0000000..eb685a5
--- /dev/null
+++ b/LGPL
@@ -0,0 +1,481 @@
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 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.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, 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 library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, 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 companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, 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 library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete 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 distribute a copy of this License along with the
+Library.
+
+ 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 Library or any portion
+of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+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 Library, 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 Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you 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.
+
+ If distribution of 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 satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. 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.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library 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.
+
+ 9. 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 Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+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.
+
+ 11. 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 Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library 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 Library.
+
+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.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library 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.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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 Library
+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 Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+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
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "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
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. 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 LIBRARY 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
+LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. 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 library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; 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.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7dd2fb8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+
+all: patch compile
+
+patch:
+ sh patch.sh
+
+kernelconf: patch
+ sh conf.sh
+
+compile: kernelconf
+ make -C src
+
+install:
+ make -C src
+ make -C src install
+ make -C man install
+ sh magic.sh
+
+uninstall:
+ make -C src uninstall
+ make -C man uninstall
+
+mrproper:
+ rm -f *~
+ rm -f doc/*~
+ rm -f man/*~
+ rm -f patches/*~
+ make -C src mrproper
+
+dist:
+ sh mkdist.sh
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..365f2f7
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,18 @@
+New in this release (since dmsdos 0.9.2.1):
+
+* THIS RELEASE IS EXPERIMENTAL AND ITS USAGE WITH 2.3.x
+ is DANGEROUS.
+
+* Experimental WIP to support 2.3.x kernels. It is DANGEROUS, because
+ mmap support is missing for 2.3.x. Basic functionality for 2.3.30
+ was obtained.
+ Found bug and provided patch to solve write after end of file or dir
+ cluster allocations fails in newer 2.2.x kernels (2.2.13 tested).
+
+New in this release (since dmsdos 0.9.2.0):
+
+* Small changes for dmsdos to compile under 2.2.x kernels. WARNING: A serious
+ bug in the vfat filesystem can hang the system when using dmsdos under
+ Linux kernel version 2.2.0, 2.2.1 and 2.2.2. This bug is triggered by
+ dmsdos. At the time of writing, 2.2.3 has not been released yet.
+ See file INSTALL.TXT how to fix this bug.
diff --git a/PORT_TO_WIN32 b/PORT_TO_WIN32
new file mode 100644
index 0000000..ca7f92c
--- /dev/null
+++ b/PORT_TO_WIN32
@@ -0,0 +1,113 @@
+Porting dmsdos to Win32
+-----------------------
+
+If you want to use dmsdos under a 32 bit Microsoft Windows system such as
+Win95/98 or Win NT 4.0 there are two possibilities: try it at user level
+(easy) or at driver level (difficult but more interesting).
+
+Don't try a 16 bit environment. You won't get it work as dmsdos needs more
+than 1MB of temporary memory. Buy the appropriate MS-DOS or Stacker version
+instead. They're also faster because they are said to contain hand-optimized
+assembler code.
+
+At least, there's no Win NT version of Doublespace, Drivespace or Stacker.
+This makes dmsdos somewhat interesting for Win32 :)
+
+Well you can also try a 32 bit extended Dos environment such as DJGPP.
+
+
+A. User Level
+-------------
+
+I tried, and I found Win32 programming is not fun. At least if you are
+used to programming in a Unix environment.
+
+It _should_ be easy to compile libdmsdos and, for example, dcread under any
+32 bit environment. Well, I tried Win NT 4.0 with Cygnus' Win32 B19 version
+of gcc, but almost nothing worked. The configure script, which runs under
+pure bash, didn't run with the bash found in that package. So what, I then
+configured dmsdos under Linux and copied the config files. With a lot of
+hacking, I finally managed to compile libdmsdos, but it was of no use.
+Whatever I did, when linking any dmsdos utility against the library I just
+managed to crash the linker :((
+I have really no idea what stupid things I may have done...
+Looks like this gcc port is not yet ready for daily use. Sigh. Though it's
+really an impressive work: all gnu tools under NT...
+
+Okay, now Cygnus have released B20 version of their Win32 gcc port, and -
+guess what - it _worked_, almost out of the box. Even the Configure script
+runs correctly now. There is a special Makefile, Makefile.cygwinb20,
+that should be used for this compiler. One thing, though, is bad: the
+compiler does neither support Unix flock nor Win32 sopen. This means the
+generated code does not do file locking. Be sure that there is never
+more than one process that is using a CVF through libdmsdos.
+
+The next chance I gave to Microsoft Visual C++ 5.0. Again almost nothing
+worked (and yes, the IDE also crashed awaking Dr. Watson when I just clicked
+around in the help in order to learn how to use the IDE...). No need to say
+that I had to configure dmsdos under Linux. The complete Makefile was even
+rejected by nmake. So what. I finally wrote a script win32_msc50.bat
+that compiles the necessary things in a Win32 _console_. Currently, the
+only useful application is mcdmsdos: it can extract files from a CVF in
+a Win32 environment. (Just tested: the executable runs even under wine in
+an xterm :-) )
+
+I did not try other compiler versions. It may work with the DJGPP Dos port
+of gcc and the EMX OS/2 port of gcc. But you may find that the long filenames
+the dmsdos sources use cause a lot of problems there...
+Now there's a script called "prepare_dos_8.3" in the src directory that
+translates all relevant dmsdos files to use 8.3 names. There's no further
+documentation about this. You are expected to take a look at the script.
+You are also expected to write your own Makefile for compilation in such an
+environment. There are lots of problems that may occur in a DOS environment.
+Sorry, but I can't test everything.
+
+Okay, that's what should work under every compiler:
+
+* Configure, compile and install dmsdos under Linux.
+* Copy the whole directory where it compiled to a vfat partition.
+* Boot a Win32 system and edit the script win32_msc50.bat (on my system
+ I had to setup include and library paths there). If your compiler is not
+ Microsoft Visual C++ 5.0 this may not work and you may have to write
+ a new script from scratch.
+* Run the script with errors directed into a file.
+* Hack the source where it fails. Please be sure to surround your changes
+ with appropriate #ifdef's (unless they are real bugs) so I can integrate
+ them into next dmsdos release. Let me know what you had to change and why.
+
+
+B. Driver Level
+---------------
+
+That's indeed possible. At least, there has been some discussion about using
+libdmsdos for writing a Win95/98 VXD or a Win NT 4.0 FS driver.
+
+Well, I refuse to do that. I'm not a Win32 driver hacker and my knowledge
+about the Win95/98 or Win NT driver interface is almost zero. But some
+notes may be interesting, though.
+
+Note that libdmsdos is *not* reentrant. If the Win32 system does not ensure
+exclusive execution of the driver code you must write your own *exclusive*
+locking interface around all libdmsdos calls, even in read-only mode.
+
+Beyond that, you will have to write a full-featured FAT filesystem
+driver that calls the libdmsdos cluster read and write routines, and link it
+against libdmsdos. That's almost all. That's not difficult.
+As FAT is a simple filesystem layout, and the CVFs are FAT12 or FAT16 but
+never FAT32, each experienced Dos programmer should be able to write that
+from scratch, at least for 8.3 filenames which would be enough for
+functionality :) The first trial is always read-only, and that should not be
+too much work.
+
+Note that libdmsdos references some standard C library functions that are
+not available at driver level. You must write an emulation for all these
+functions and link it to the driver instead of the standard C library. The
+emulation needn't be full-featured like the C library. Look at the source
+if you are in doubt. Well, the read/write/lseek emulation is actually all
+the low-level disk access :)
+
+See doc/libdmsdos.doc for further information about the dmsdos library
+(including a list of referrenced C library functions and a detailed
+description of the dmsdos functions the library exports).
+
+Let me know if you are a Win32 hacker and want to start such a project :)
diff --git a/README b/README
new file mode 100644
index 0000000..77fb964
--- /dev/null
+++ b/README
@@ -0,0 +1,28 @@
+This is the README for the DMSDOS CVF-FAT filesystem extensions.
+
+CVF-FAT is a generic modular Compressed Volume Files interface for the FAT
+filesystems.
+
+DMSDOS is a module which provides similar functionality to the former dmsdos
+filesystem: support for Microsoft's doublespace/drivespace and for Stac's
+stacker. DMSDOS uses the CVF-FAT extensions and can be considered as a
+sample implementation for using the CVF-FAT extensions.
+
+CVF-FAT is the official successor of the dmsdos filesystem. DMSDOS itself
+is still continued, but it now is a child of CVF-FAT and not a filesystem
+any more.
+
+List of files to read:
+----------------------
+
+README this file
+INSTALL.TXT how to install CVF-FAT and dmsdos
+patches/DIFFS.TXT description of kernel patches ("when do I use which one")
+PORT_TO_WIN32 if you want to run dmsdos utilities in a Win32 environment
+
+Other directories:
+
+doc/ various documentation
+man/ man pages for dmsdos utilities
+src/ complete source code for dmsdos
+patches/ kernel patches and updates for the CVF-FAT interface
diff --git a/README.md b/README.md
deleted file mode 100644
index 1367e2a..0000000
--- a/README.md
+++ /dev/null
@@ -1 +0,0 @@
-fork of dmsdos project
diff --git a/conf.sh b/conf.sh
new file mode 100644
index 0000000..1e89d9a
--- /dev/null
+++ b/conf.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+if [ ! -d /usr/src/linux ];
+then
+ echo "Linux sources not found in /usr/src/linux."
+ echo "Sorry, I cannot determine automatically what needs to be done."
+ echo "Please read the installation instructions for details."
+ exit 1
+fi
+
+CT=config
+if [ -f /usr/src/linux/scripts/lxdialog/lxdialog ];
+then
+CT=menuconfig
+fi
+
+if [ ! -f /usr/src/linux/.config ];
+then
+ echo "Your kernel seems not configured."
+ echo "I'm going to call kernel configuration now."
+ echo "If you are very sure this need not be done press CTRL-C now"
+ echo "and install dmsdos manually. To continue, press Enter."
+ read junk
+ ( cd /usr/src/linux; make $CT )
+fi
+
+CONFIG_MODULES=n
+CONFIG_BLK_DEV_LOOP=n
+CONFIG_FAT_FS=n
+
+source /usr/src/linux/.config
+
+if [ "$CONFIG_MODULES" != "n" -a "$CONFIG_BLK_DEV_LOOP" != "n" -a "$CONFIG_FAT_FS" != "n" -a -f /usr/src/linux/fs/fat/cvf.o ];
+then
+ echo "This really looks like you have already configured and compiled your"
+ echo "kernel correctly for CVF-FAT. I hope I'm not wrong here :-)"
+ exit 0
+fi
+
+while [ "$CONFIG_MODULES" = "n" -o "$CONFIG_BLK_DEV_LOOP" = "n" -o "$CONFIG_FAT_FS" = "n" ];
+do
+ echo "Your kernel is not configured correctly for CVF-FAT."
+ echo "The following feature(s) is(are) missing:"
+if [ "$CONFIG_MODULES" = "n" ];
+then
+ echo " - loadable module support"
+fi
+if [ "$CONFIG_BLK_DEV_LOOP" = "n" ];
+then
+ echo " - loopback block device"
+fi
+if [ "$CONFIG_FAT_FS" = "n" ];
+then
+ echo " - fat filesystem"
+fi
+ echo "Please correct this now. I'm going to call kernel configuration now."
+ echo "If you don't want me to do this, press CTRL-C now. To continue, press Enter."
+ read junk
+ ( cd /usr/src/linux; make $CT )
+
+ CONFIG_MODULES=n
+ CONFIG_BLK_DEV_LOOP=n
+ CONFIG_FAT_FS=n
+
+ source /usr/src/linux/.config
+done
+
+echo
+echo "I'm going to recompile your kernel and your modules now."
+echo "If you are very sure this need not be done, press CTRL-C now"
+echo "and install dmsdos manually. "
+echo
+echo "Note that I will *not* install the new kernel and the new modules."
+echo "I'll just compile them. For safety, installation is up to you :)"
+echo
+echo "To continue, press Enter."
+read junk
+
+make -C /usr/src/linux dep
+make -C /usr/src/linux clean
+make -C /usr/src/linux zImage || make -C /usr/src/linux bzImage
+if [ "$?" != "0" ];
+then
+ echo "kernel recompile FAILED!!! I must give up now."
+ echo "Go ahead and try to find out what's wrong here."
+ exit 1
+fi
+make -C /usr/src/linux modules
+if [ "$?" != "0" ];
+then
+ echo "module recompile FAILED!!! I must give up now."
+ echo "Go ahead and try to find out what's wrong here."
+ exit 1
+fi
+
+echo
+echo "Okay, that seems to have succeeded. Just remember to install your new"
+echo "kernel and modules with 'make zlilo' and 'make modules_install' or"
+echo "similar commands in /usr/src/linux, and reboot. I'm just a dumb script"
+echo "and the person who wrote me made me refuse to do this. :-)"
+echo
+echo "So, the following error is intended. Just do what needs to be done and then"
+echo "retry. The next time I should recognize your new setup and continue with"
+echo "dmsdos compilation."
+echo
+echo "You can even continue without installing the new kernel and the new modules"
+echo "and without rebooting. Dmsdos will compile in spite of that, but it may"
+echo "not work correctly or not run at all unless you have rebooted with the new"
+echo "kernel and the new modules."
+exit 1
diff --git a/dmsdos.lsm b/dmsdos.lsm
new file mode 100644
index 0000000..8911051
--- /dev/null
+++ b/dmsdos.lsm
@@ -0,0 +1,39 @@
+Begin3
+Title: dmsdos
+Version: 0.9.2.2
+Entered-date: 04DEC1999
+Description: dmsdos: reads and writes compressed dos filesystems (CVF-FAT)
+ * read and write access to compressed partitions (files like
+ dblspace.xxx, drvspace.xxx and stacvol.xxx). The following
+ configurations are supported:
+ - DoubleSpace / DriveSpace (msdos 6.x)
+ - DoubleSpace / DriveSpace (older win95 releases)
+ - DriveSpace 3 (win95 with Plus! pack or newer win95)
+ - Stacker 3
+ - Stacker 4
+ * Works with FAT32, NLS, codepages etc. (tested with fat32
+ patches version 0.2.8 under Linux 2.0.33 and with fat32 in
+ standard 2.1.xx kernels and 2.0.[34-36], also tested with
+ 2.2.[0-2] kernels). Tested with patched 2.2.13 and 2.3.30.
+ * Dmsdos can run together with vfat or umsdos for long
+ filenames.
+ * Dmsdos has been redesigned to be ready for SMP.
+ * Dmsdos compiles both under libc5 and libc6.
+ * The dmsdos library and some dmsdos tools compile even in a
+ 32 bit Dos/Windows environment.
+Keywords: filesystem compression doublespace drivespace stacker
+Author: gockel@sent13.uni-duisburg.de (Frank Gockel)
+ pisa@cvlinux.felk.cvut.cz (Pavel Pisa)
+Maintained-by: gockel@sent13.uni-duisburg.de (Frank Gockel)
+ pisa@cvlinux.felk.cvut.cz (Pavel Pisa)
+Primary-site: sunsite.unc.edu /pub/Linux/system/Filesystems/dosfs
+ 262 kB dmsdos-0.9.2.1.tgz
+Alternate-site: ftp.uni-stuttgart.de /pub/systems/linux/local/system
+ 262 kB dmsdos-0.9.2.1.tgz
+ fb9nt.uni-duisburg.de /pub/linux/dmsdos
+ 262 kB dmsdosfs-0.9.2.1.tgz
+Original-site:
+Platforms: Linux 2.0.29 up to 2.0.36, 2.1.94+, 2.2.0+, 2.2.13+,
+ msdos 6.x, msdos 7.x, win95, winnt 4.0
+Copying-policy: GPL
+End
diff --git a/doc/FAQ b/doc/FAQ
new file mode 100644
index 0000000..56f6e0b
--- /dev/null
+++ b/doc/FAQ
@@ -0,0 +1,215 @@
+This file gives answers to frequently asked questions about dmsdos.
+
+
+What is dmsdos ?
+
+Dmsdos is a Linux kernel module that allows access to msdos/win95 compressed
+disk partitions. It supports different doublespace, drivespace and stacker
+versions.
+See file dmsdos.doc for an actual list of supported dos/win95 configurations.
+
+
+
+How do I mount a compressed partition?
+
+You need to mount the uncompressed host partition as usual. In this
+partition, you can see the compressed partiton as large file (the CVF,
+Compressed Volume File). It usually has names like 'dblspace.NNN',
+'drvspace.NNN' or 'stacvol.XXX'. The whole compressed partition is inside
+this file.
+
+You need the loopback block device driver (enable the loopback device during
+kernel configuration):
+
+ *
+ * Additional Block Devices
+ *
+ Loopback device support (CONFIG_BLK_DEV_LOOP) [M/n/y/?] Y or M
+
+The loopback block device allows mounting a file as a filesystem (read some
+Linux docs about this for more information). To avoid confusion, note
+that this has absolutely nothing in common with the well-known network
+loopback interface.
+
+Example: 'mount -t msdos -o loop /DOS/dblspace.001 /mnt'
+
+This mounts your compressed filesystem that in fact resides in the file
+/DOS/dblspace.001 under /mnt.
+
+
+
+It doesn't work!
+
+Well, nothing simply doesn't work. It fails at a specific point and gives
+some errors or warnings before failure which are usually an important hint
+for finding out what exactly went wrong. Please have a look at file
+troubleshooting.doc. If you still cannot solve the problem you can send an
+email to the current dmsdos maintainer. The email address and a checklist
+what to include in a bug report can be found in file dmsdos.doc.
+
+
+
+I have compressed partitions, but I'm using umsdos. Can I use dmsdos without
+losing umsdos features?
+
+No problem. It should work out of the box. If you want to use umsdos style
+long filenames inside the CVF, you can even mount it as type umsdos instead
+of msdos. (Uhh. Use kernel 2.0.xx or get at least 2.1.94 for that).
+
+
+
+My compressed partition has long filenames under Win95. How can I see
+them with dmsdos ?
+
+Mount as type vfat instead of msdos.
+
+
+
+Can I compress my Linux data under dmsdos?
+
+You can (via umsdos), but it's not recommended.
+
+Just to let you know: There's some other compression software available that
+may be a better choice for Linux data. See file dmsdos.doc.
+
+
+
+Can I boot Linux from a compressed partition?
+
+No. (Older dmsdos versions supported it, but this feature has been given up
+when the CVF-FAT interface was introduced. Well, the documentation always
+warned ... )
+
+
+
+Uhhh. Write access is so ssssssslow.....
+
+What can I say. You decided to use compressed partitions - now you are
+experiencing one of their greatest disadvantages. But dmsdos has some
+special tricks for you in this case. You can... (in recommended order)
+
+ - run the dmsdos daemon which does delayed compression in the background
+ when the system is idle, or use the internal daemon
+
+ - set speedup bit #2 (allocation code skips exact search)
+
+ - set speedup bit #6 (allocation code doesn't search at all but takes
+ any hole free hole) (Really Not Recommended if you don't know what
+ you are doing). Read the comments in the documentation, please.
+
+See the dmsdos documentation for details and a discussion about their
+advantages and disadvantages.
+
+
+
+Is it safe to use dmsdos?
+
+This question is something like that one: 'is it safe to drive a car?'.
+
+There's no warrenty. I really can't promise that there aren't any bugs in
+the driver code. I think you just like to feel safe:
+
+ - If you don't have to write to your compressed filesystems, mount
+ read-only.
+
+ - Run the dos filesystem checker every time you boot dos and backup from
+ time to time.
+
+ - Backup your compressed filesystem before trying to mount read-write.
+
+
+
+How do I enable/disable long filename support?
+
+For long filename support use a filesystem type that knows long filenames,
+e.g. vfat instead of msdos.
+
+
+
+What happens if the compressed filesystem gets full during write access?
+
+Don't ask. That has become too complex. :(
+
+Well, in the worst case the same thing happens as under Dos: you get a
+strange error (Dos: "sector not found") and might lose data. It is difficult
+because some applications do not check the return values of write access
+functions (AAARGHHH!!!). On a compressed filesystem not only cluster
+allocation, but also usual write access may fail (i.e. when you replace
+data that compress well by data that don't compress well and thus need more
+physical space). So just keep in mind that you shouldn't fill a compressed
+partition up to the last byte :)
+
+
+
+What's the difference between win95 doublespace and drivespace 3 ?
+
+Please note that win95 has two different drivespace programs. One of them
+is included in win95 and it's in fact an old dos doublespace version. I
+still refer to it as 'win95 doublespace' though M$ call it 'drivespace'.
+This one is supported without restrictions.
+
+The other one is sold seperately from win95 in a 'M$ Plus' package (is this
+still true?) M$ have called it Drivespace 3. Meanwhile it should also
+be supported quite well.
+
+If you aren't sure, watch the filename of the large, hidden CVF. If it is
+something like 'dblspace.xxx' it's the old version. If it's 'drvspace.xxx'
+instead and you are very sure you created it under win95, it's Drivespace 3.
+Dmsdos also tells when it detects a drivespace 3 format CVF.
+
+The main difference between these two versions is that drivespace 3 uses a
+larger cluster size (32KB instead of 8KB) and can compress up to 2GB of data
+instead of only 512KB. It has also a more advanced compression scheme
+(SQ-0-0).
+
+
+
+When do I have to defragment my compressed partitions?
+
+Watch the dutil output (see file dmsdos.doc for a dutil description). It
+displays a fragmentation value. You should keep it below 75% to feel safe.
+When dutil even displays a warning about high fragmentation, you should
+immediately boot Dos and defragment the CVF.
+
+
+
+Does dmsdos work with FAT32 filesystems?
+
+Yes. All earlier problems have been avoided by moving dmsdos one layer down
+in the filesystem interface. Well, it does not support *compressed* FAT32
+filesystems. Win95 doesn't do either :) But, if some day they show up to
+exist, I might be persuaded to implement it also.
+
+
+
+What about the relationship between dmsdos and software patents ?
+
+I'm still receiving mails about this question. At my best knowledge dmsdos
+does not violate any software patents. (Well, I don't hope so, but my best
+knowledge might be wrong. I'm not a lawyer.) If you are interested in
+details take a look at file patent.doc.
+
+
+
+Do I have to recompile dmsdos when I upgrade my kernel ?
+
+The safe answer is yes. It's best to recompile *all* modules if you change
+something in your kernel though not always necessary. But who knows.
+
+Some clever people invented a 'module version information' trick. You can
+enable it during kernel configuration. This makes your modules less kernel
+version dependent e.g. a module compiled under kernel 2.0.33 may also work
+under kernel 2.0.34. In that case, you may share one precompiled dmsdos
+module with several kernels. But please don't overuse it. You should not,
+for example, use the same compiled object code of a dmsdos module for
+kernel 2.0.34 and 2.1.105 - this is very likely to cause problems :)
+
+
+
+Where does the name "DMSDOS" come from ?
+
+It has historical reasons. The first piece of dmsdos code was written in
+times of MSDos 6.2 when Doublespace became very popular. Thus, dmsdos was
+meant as something like "doublespaced msdos". Today, it supports a lot of
+more compressed Dos filesystems, and a better name would be "cmsdos" for
+"compressed msdos". But you never change the name of a successful project :)
diff --git a/doc/cvf-fat.doc b/doc/cvf-fat.doc
new file mode 100644
index 0000000..ef064f2
--- /dev/null
+++ b/doc/cvf-fat.doc
@@ -0,0 +1,4 @@
+
+Documentation for the CVF-FAT interface is part of the Linux kernel
+(either directly in 2.1.xx or after applying the CVF-FAT patch for 2.0.xx).
+It can be found in file linux/Documentation/filesystems/fat_cvf.txt.
diff --git a/doc/cvfinfo.doc b/doc/cvfinfo.doc
new file mode 100644
index 0000000..3ab66a5
--- /dev/null
+++ b/doc/cvfinfo.doc
@@ -0,0 +1,372 @@
+This file contains some information about the compressed filesystem layout.
+
+ The CVF Hacker's Guide :-)
+ ==============================
+
+WARNING: This is not official M$ specs. In fact, it's a hacker's document.
+ I don't know M$ specs, so this file may contain incorrect
+ information. Use at your own risk (see the GPL for details).
+
+WARNING 2: Several parts of the compressed filesystem internals are still
+ unknown to me. If this document is inaccurate in some details, it's
+ because I don't know it more exactly. Feel free to add your
+ knowledge.
+
+
+CVF format overview
+-------------------
+
+version compression SPC(*) max. size
+dos 6.0/6.2 doublespace DS-0-2 16 512MB
+dos 6.22 drivespace JM-0-0 16 512MB
+win95 doublespace/drivespace DS-0-0 16 512MB
+win95 drivespace 3 JM-0-0,JM-0-1,SQ-0-0 64 2GB
+
+ (*)=Sectors Per Cluster
+
+General filesystem layout
+-------------------------
+
+Superblock (1 sector)
+BITFAT (several sectors)
+MDFAT (~ twice as large as FAT)
+Bootblock (1 sector)
+FAT (only one) (several sectors)
+Root directory (some sectors)
+Data area (many sectors)
+Final sector (1 sector)
+
+There's some slack (or "reserved space") between some filesystem structures,
+but I don't know what it is good for. Perhaps M$ don't know either.
+
+Sector counting
+---------------
+
+The Superblock is referred as sector 0. The rest of the sectors are counted
+appropriately.
+
+Superblock layout
+-----------------
+
+Byte positions are counted beginning with 0 for the first byte. Integers are
+in low byte first order. Only important fields are listed here, usual dos
+fields are omitted.
+
+Pos. 3-10: string: signature "MSDBL6.0" or "MSDSP6.0"
+Pos. 45,46: *signed* integer: dcluster offset for MDFAT lookups
+Pos. 36,37: first sector of MDFAT minus 1
+Pos. 17,18: number of entries in root directory
+Pos. 13: sectors per cluster
+Pos. 39,40: sector number of Bootblock
+Pos. 14,15: sector offset of FAT start (relative to Bootblock). I.e. to
+ obtain the sector number of the first FAT sector add Pos. 14,15
+ to Pos. 39,40.
+Pos. 41,42: sector offset of root directory start (relative to Bootblock). To
+ obtain the sector number of the first root directory sector add
+ Pos. 41,42 to Pos. 39,40.
+Pos. 43,44: sector offset of Data area minus 2 (relative to Bootblock). To
+ obtain the sector number of the first Data area sector add
+ Pos. 43,44 to Pos. 39,40 and finally add 2.
+Pos. 51: version flag (0=dos 6.0/6.2 or win95 doublespace, 1=??,
+ 2=dos 6.22 drivespace, 3 or 0 ??=win95 drivespace 3)
+ Hint: drivespace 3 format can be recognized safely by watching
+ the sectors per cluster value. The version flag seems to lie
+ for drivespace 3.
+Pos. 57-60: usually string "12 " or "16 " as the rest of "FAT12 " and
+ "FAT16 " (the spaces are important), but here seems to be a bug
+ in some doublespace versions. PLEASE IGNORE THIS VALUE, IT
+ SOMETIMES LIES. Use the Bootblock's value instead.
+Pos. 62-63: Maximum size of the CVF in Megabytes.
+Pos. 32-35: Faked total number of sectors (it is something like the real
+ number of sectors in the data area multiplied with the
+ compression ratio). This value is important because it determines
+ the maximum cluster number that is currently allowed for the
+ CVF according to this formula (don't ask me why):
+
+ (Pos.33-35)-(Pos.22,23)-(Pos.14,15)-(Pos.17,18)/16
+ max_cluster=--------------------------------------------------- + 1
+ (Pos.13)
+
+ (rounded down). Be sure not to exceed the limits due to FAT/MDFAT
+ size or CVF size here. Since this formula has been found by
+ trial and error, it may not be true in all screwy cases.
+
+BITFAT layout
+-------------
+
+The BITFAT is a sector allocation map. Consider it as a list of bits each of
+which represents one sector in the Data area. If a bit is set, the
+appropriate sector contains data - if the bit is clear, the sector is free.
+
+The first bit matches the first sector in the data area (and so on). The
+bits are counted *wordwise* beginning with the most significant bit of the
+word (where "word" means two bytes at once, low byte first).
+
+So substract the number of the first data sector from the number of the data
+sector you want to lookup information in the bitfat. Keep the result in
+memory. Divide the resulting number by 16, round down, multiply with 2. Get
+the two bytes at this position in the bitfat (counted from its beginning)
+and store them as word. Now watch the least 4 bits of the previosly
+memorized result - they represent the bit number (counted from the most
+significant bit) in the word. This bit corresponds to the data sector.
+
+WARNING: The BITFAT sometimes is incorrect due to a missing system shutdown
+ under dos. If you want to write to the filesystem, be sure to
+ check (and, if necessary, repair) the BITFAT before. See below
+ how to do this.
+
+MDFAT layout
+------------
+
+MDFAT is organised as a stream of long integers (4 bytes, for drivespace 3:
+5 bytes). The data are sector-aligned - this means for drivespace 3 that the
+last two bytes of a sector are slack. Consider the bytes in usual order
+(low byte first).
+
+The MDFAT contains additional information about a cluster:
+
+ 3322222222221111111111 (doublespace/drivespace)
+ 10987654321098765432109876543210
+ uchhhhllll?sssssssssssssssssssss
+
+ 333333333322222222221111111111 (drivespace 3)
+ 9876543210987654321098765432109876543210
+ uchhhhhhllllllf?ssssssssssssssssssssssss
+
+u=1: The cluster is used, u=0: the cluster is unused. In the latter case the
+ whole entry should be zerod. An unused cluster contains per definition
+ only zeros ( C notation: '\0'). This is important if a program insists
+ on reading unused clusters!
+c=1: The cluster is not compressed, c=0: the cluster is compressed.
+h: Size of decompressed cluster minus 1 (measured in units of 512 bytes).
+ E.g. 3 means (3+1)*512 bytes.
+l: Size of compressed cluster data minus 1 (measured in units of 512
+ bytes). If the cluster is not compressed according to the c bit, this
+ value is identical to h.
+f: fragmented bit for drivespace 3. If it is set the cluster is fragmented
+ and needs some special treatment on read and write access.
+?: Unknown. Seems to contain random garbage.
+s: starting sector minus 1. I.e. if you want to read the cluster, read (l+1)
+ sectors beginning with sector (s+1). If the c bit is zero, the data must
+ be decompressed now.
+ Important: if the cluster on disk is shorter than the filesystem's
+ sectors per cluster value, the missing rest at the end has to be treated
+ as if it was zerod out.
+
+To lookup information in the MDFAT, take the cluster number, add the
+dcluster offset (which may be negative!) and take the appropriate entry
+counted from the beginning of the MDFAT. Don't ignore the sector alignment
+for drivespace 3.
+
+Bootblock layout
+----------------
+
+Emulates normal dos filesystem super block. Most dos fields are identical
+to the Superblock except for the FAT16 or FAT12 string. The FAT bitsize string
+that can be found in the Bootblock is correct while the one in the
+Superblock may be garbage. Take a disk viewer and compare Bootblock and
+Superblock yourself. There are slight differences, but I don't know exactly
+where and why. You'd better never change anything in these blocks...
+
+FAT layout
+----------
+
+No need to explain. It's the same like in a normal dos filesystem. It may be
+12 or 16 bit according to the Bootblock, but *not* to the Superblock. This
+seems to be a bug in doublespace - the Superblock's FAT bit size information
+is sometimes wrong, so use the Bootblock's information.
+
+Root directory
+--------------
+
+The same as in a normal dos filesystem. (The root directory is never
+compressed.)
+
+Data area
+---------
+
+Well, that's the actual space for the data.
+
+Final sector
+------------
+
+Contains the signature "MDR". Must not be used by data. To find it you must
+know the size of the CVF file. There's no pointer in the Superblock that
+points to this sector.
+
+Compressed clusters
+-------------------
+
+Compressed data (when the c bit is 0 in the MDFAT entry of a cluster) are
+identified by a compression header. The header consists of 4 bytes which are
+at the beginning of the compressed cluster data. The headers consist of two
+bytes specifying the compression scheme and two bytes version number, and
+usually look like this:
+
+'D', 'S', 0x00, 0x02, I write it as 'DS-0-2'
+'J', 'M', 0x00, 0x00
+'S', 'Q', 0x00, 0x00
+
+The version number seems to be ignored though M$ claim that, for example,
+'High' (JM-0-1) compresses better than 'Normal' (JM-0-0). That's nonsense
+from the compressed format point of view, the format is in fact the same.
+Maybe the original M$ software uses different *compression algorithms*
+which may be more or less efficient, but they're not using not different
+*compression schemes*. So in fact there are three schemes: DS, JM, and SQ.
+DS and JM are quite similar, for a decompression algorithm see the dmsdos
+or thsfs sources (both are GPL code, you may reuse it).
+
+As far as I know, dos 6.x versions of doublespace/drivespace never compress
+directories and never cut them off (if only the first sectors of the cluster
+are used, it is in fact possible to cut the cluster since the unused slack
+is, per definition, to be treated as if it was zerod out). It is unknown
+whether these versions can read compressed or shortened directories, but it
+is sure they never compress or shorten them. So I just recommend not to do it
+either. drivespace 3 usually cuts off directories and sometimes even
+compresses them though compression of directories is a great performance loss.
+win95 doublespace/drivespace (not drivespace 3) never cuts directories but
+also compresses them sometimes.
+
+Fragmented clustes
+------------------
+
+To make things more complex, M$ have invented these strange things.
+Unfortunately, they need some special treatment.
+
+A fragmented cluster can be recognized by watching the 'f' bit in the MDFAT.
+This bit only exists in drivespace 3 format.
+
+The first sector of the cluster contains a fragmentation list. This list
+contains entries each of which use 4 bytes. The first one is the
+fragmentation count - it specifies into how many fragments the cluster is
+devided. It must be > 1 and <=64.
+
+The following entries are pointers to fragments of data like this:
+
+ 3322222222221111111111
+ 10987654321098765432109876543210
+ lllllluussssssssssssssssssssssss
+
+s: start sector minus 1 - the fragment begins at sector (s+1).
+u: unused and zero (?)
+l: sector count minus 1 - the fragment contains (l+1) sectors beginning
+ with sector (s+1). This means raw data if compressed.
+
+The first entry always points to the fragmentation list itself. I.e.
+the s and l fields of the first fragmentation list entry are always the same
+as the ones in the MDFAT entry. The first fragment is not restricted to
+contain *only* the fragmentation list, however.
+
+Now it becomes slightly difficult because the data are stored differently
+depending on whether the cluster is compressed or not. If the cluster is
+compressed the raw (compressed) data begin immediately after the last entry
+of the fragmentation list. The byte position can be calculated by multiplying
+the fragmentation count with 4. Further raw data can be found in the other
+fragments in order.
+
+If the cluster is not compressed, the (uncompressed) data begin in the
+sector that follows the sector containing the fragmentation list. If the
+first fragment has only the length of 1 sector the data begin in the second
+fragment. Further data are in the fragments in order.
+
+General rules for cluster access
+--------------------------------
+
+I'm assuming you want to access cluster number x (x!=0 i.e. not root directory
+- this one should be clear without further explanation).
+
+How to read cluster x from the compressed filesystem
+----------------------------------------------------
+
+ * Get and decode the MDFAT entry for the cluster: lookup entry number
+ (x+dcluster). dcluster and start of the MDFAT can be obtained from the
+ Superblock.
+
+ * If the MDFAT entry is unused (u bit clear), just return a cluster full of
+ zeros (0x00).
+
+ * Read (l+1) sectors beginning with sector (s+1).
+
+ * If the cluster is fragmented ... uuhhhhh ... you'd better issue an
+ error and encourage the user to boot win95 and defragment the drive.
+ Otherwise read and interpret the fragmentation list now.
+
+ * If the data are compressed (c bit clear) decompress them.
+
+ * If the cluster is shortened (i.e. h+1 < sectors per cluster) zero out
+ the rest of the cluster in memory. The sector per cluster value can be
+ obtained from the Superblock.
+
+How to write cluster x to the compressed filesystem
+---------------------------------------------------
+
+WARNING: Be sure you can trust your BITFAT, i.e. have it checked before.
+ See below how to do this.
+
+ * Be sure to know whether the cluster may be shortened. The size in
+ sectors minus 1 will become the h value of the MDFAT entry later.
+
+ * If you want, compress the data. Be sure the data really become smaller.
+ Determine the size of the compressed data in sectors and subtract 1 -
+ this will become the l value of the MDFAT entry later. If you don't
+ want to compress the data or the data turn out to be incompressible,
+ set the l to the same value as h and use the uncompressed original data.
+ DON'T ACTUALLY WRITE TO THE MDFAT AT THIS POINT!
+
+ * Delete the old cluster x that may have been written earlier (see below).
+
+ * Search for (l+1) free continuous sectors in the BITFAT. Be prepared for
+ failure here (i.e. if the disk is full or too fragmented). Allocate the
+ sectors by setting the appropriate bits in the BITFAT. Now you can create
+ the MDFAT entry and write it to disk - please note to subtract 1 from the
+ sector number when creating the s value of the MDFAT entry. Also don't
+ forget to set the c bit if the data are not compressed.
+
+ * Write the (l+1) sectors to disk beginning with sector (s+1).
+
+How to delete cluster x in a compressed filesystem
+--------------------------------------------------
+
+WARNING: Be sure you can trust your BITFAT, i.e. have it checked before.
+ See below how to do this.
+
+ * Get the appropriate MDFAT entry (x+dcluster). If it is unused (u bit
+ clear) there's nothing to do.
+
+ * If the cluster is fragmented, scan and check the fragmentation list
+ and free up all the fragments.
+
+ * Otherwise free up (l+1) sectors beginning with sector (s+1) in the BITFAT
+ by clearing the appropriate bits. Be sure to do a range checking before so
+ you don't corrupt the filesystem if there's garbage in the s field of
+ the MDFAT entry.
+
+ * Zero out the MDFAT entry completely. Don't just clear the used bit.
+
+How to check and repair the BITFAT
+----------------------------------
+
+Dos seems to recalculate the BITFAT on each bootup. This points out that
+even M$ programmers didn't trust it, so you shouldn't do either if you plan
+to write to the compressed partition.
+
+It's easy. Just scan the complete MDFAT for used entries (u bit set). You
+get from the l and the s values (don't forget to add 1 in each case) which
+sectors are allocated. Doing this for the whole MDFAT, you get a list of
+which sectors are used and which are free. Then you can compare this list to
+the BITFAT. If you just keep the list in memory in the same bit encoding as
+used in the real BITFAT, you can just write the complete list to disk and
+replace the BITFAT by it. Uhh, yes, you may need up to 512 KB memory for
+the data for this purpose...
+
+If you are using drivespace 3 please keep in mind that you also have to
+take care of fragmented clusters (i.e. check the fragmentation bit and scan
+the fragmentation list if necessary).
+
+Further related documents about compressed filesystems
+------------------------------------------------------
+
+ - thsfs source (sunsite and mirrors)
+ - dmsdosfs source (sunsite and mirrors)
+ - Bill Gates' secret drawers
+ - Murphy's law
diff --git a/doc/dmsdos.doc b/doc/dmsdos.doc
new file mode 100644
index 0000000..50ac6fe
--- /dev/null
+++ b/doc/dmsdos.doc
@@ -0,0 +1,1152 @@
+dmsdos.doc
+
+This is the main documentation for the DMSDOS CVF module. 01MAR1999
+
+------------------------------------------------------------------------------
+
+ This is version 0.9.2.1.
+
+This version has been designed for Linux 2.2.2, but it should run under all
+2.2.x kernels. It also works with kernels from 2.0.29 to 2.0.36. It very
+likely also works with older ones, but it has at least been reported to fail
+with 2.0.0. For the 2.1.xx series you need at least 2.1.80 though it is
+recommended to use 2.1.94 or newer.
+
+Installation notes see file ../INSTALL.TXT.
+
+
+Contents:
+
+1. DMSDOS capabilities
+2. Restrictions
+3. Mount options
+4. Kernel messages [moved to file messages.doc]
+5. Installation [moved to file ../INSTALL.TXT]
+6. Troubleshooting [moved to file troubleshooting.doc]
+7. Defragment procedures
+8. How to contribute to dmsdos development
+9. The external dmsdos utility 'dutil'
+10. The dmsdos daemon 'dmsdosd'
+11. The dmsdos library 'libdmsdos.a'
+12. The dmsdos filesystem checker 'dmsdosfsck'
+13. The dmsdos Midnight Commander external filesystem utility 'mcdmsdos'
+14. Authors and email addresses
+
+------------------------------------------------------------------------------
+
+
+1. DMSDOS capabilities
+------------------------------------------------------------------------------
+
+ * 'mount'
+ - doublespace/drivespace compressed msdos 6.x partitions (read-write),
+ - doublespace and drivespace 3 compressed win95 partitions (read-write),
+ - and stacker 3 and 4 compressed partitions (read-write)
+ under Linux via the loopback block device
+ * supports compressed floppies (yes, and even cdroms[**])
+ * can use umsdos extensions in compressed partitions
+ * when writing, compression level may be selected by user
+ * supports standard text conversion like the msdos driver
+ * simple internal fs check on mount (can be switched off)
+ * dmsdos daemon for compressing data in the background
+ * works with all drivers that base on the fat driver, also with FAT32
+ * stand-alone dmsdos library for CVF related programming
+ * stand-alone dmsdosfsck (experimental)
+ * various other utilities
+
+ [**] No, don't really think of burning a CVF onto a cdrom. Dos cannot read
+ it unless you copy it to your hard disk. REALLY BAD. Linux+dmsdos can.
+
+In detail:
+
+ Dos compressed partitions are always stored inside an uncompressed host
+ partition as large files (CVFs, Compressed Volume Files). In order to
+ access them from Linux, you need
+ * the CVF-FAT interface, which is present in kernels 2.1.80 and newer
+ and must be patched into the 2.0.xx kernel series - the patches are
+ included in this dmsdos release
+ * and the loopback block device.
+ The latter lets you mount a file as a filesystem. You must compile the
+ loopback block device into your kernel or as a loadable module in order
+ to use this feature.
+
+ This version works with compressed hard disks and with compressed floppies.
+ It has been tested successfully with MS-DOS 6.2 Doublespace and MS-DOS 6.22
+ Drivespace compressed formats. win95 doublespace is supported, also the
+ drivespace 3 format from the Micro$ Plus package (rumors tell that this
+ package has been integrated into Win95OSR2). Stacker version 3 and 4
+ are supported too. Dmsdos can even mount CVFs from cdrom (Dos can't).
+
+ Francesco Potorti(pot@foda-devel.cnuce.cnr.it) has reported that dmsdos
+ also mounts Superstore compressed filesystems. I can't test this as I
+ don't have Superstore. They are recognized as old Dos 6.0 Doublespace
+ format. I'm interested in further reports about this. I still can't
+ believe that M$ simply copied (bought?) Superstore and renamed it to
+ Doublespace without further modifications...
+
+ Since version 0.9.0, dmsdos runs *below* the msdos/fat filesystem. This
+ makes it compatible with all fat based filesystems including FAT32, NLS
+ and other things :)
+
+ Dmsdos does a lot of internal caching in order to reach a reasonable speed
+ with a compressed (and really badly organised, but simple) filesystem. As
+ a side effect, this cache may eat a lot of system memory (you can change the
+ size limit).
+
+ Dmsdos was initially inspired by the THS filesystem (once found under
+ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/thsfs.tgz)
+ written 1994 by Thomas Scheuermann (email address out of date), which was
+ the first filesystem available under Linux that could read some CVFs.
+
+ When mounting a compressed filesystem, the dmsdos driver does some quick
+ and simple consistency checking. If it finds a serious filesystem error
+ that might cause further damage on write access, it sets the compressed
+ filesystem to read-only mode. The filesystem check can be switched off if
+ you don't like it.
+
+ The dmsdos daemon included in this release can speed up dmsdos write
+ access on systems that usually run at high processor load using delayed
+ compression in the background when the system is idle. I recommend to
+ use it if you are going to write much to compressed partitions from Linux
+ and need the processor time mostly for other tasks. The daemon also has a
+ significant effect on dmsdos memory management which might be important
+ for systems with little memory (well yes, dmsdos *is* a memory monster
+ that can eat up to 2MB of system memory for caching in the default
+ configuration). See chapter 'The dmsdos daemon' for details.
+
+ The dmsdos library is a stand-alone tool for raw CVF programming.
+ 'Stand-alone' means that it works independently from the dmsdos kernel
+ module, on *raw* CVFs (i.e. those large files lying around in an
+ uncompressed host partition). It's the same source code, though, just
+ compiled in a slightly different way. Meanwhile, it compiles even under
+ Windows :)
+
+
+2. Restrictions
+------------------------------------------------------------------------------
+
+ (See also file BUGS for a list of known bugs.)
+
+ Performance degradiation (well, not really bad in most cases...)
+ ------------------------
+
+ Access to compressed filesystems slows down your system. This is a fact
+ that results from additional time needed to decompress or compress the
+ data and to maintain additional filesystem structures. It's an untrue
+ rumor (sometimes spread by compressed filesystem software sellers) that
+ compressed filesystems speeded up slow ISA bus systems because of the
+ smaller amount of data to be transferred through the slow ISA bus -
+ there's too much overhead otherwhere.
+
+ Though most time-consuming actions like compression and finding free
+ sectors have been greatly improved since dmsdos 0.8.0, there are still
+ sudden system freezes reported. Reasons for this include (in rising order):
+
+ * Decompressing the data takes some time, but that is supposed to be
+ minimal. You will probably not notice it.
+
+ * Badly written programs issuing many redundant reads. Such programs
+ kill performance on every filesystem, but it's extremely bad under
+ dmsdos because of the filesystem layout.
+
+ * Large files, especially accessing them near their end. Again the
+ filesystem layout is the cause. (How do you find the end of a file
+ in a fat filesystem? Right, read the fat. And if the file is large
+ there are tons of fat access necessary to find the end.)
+
+ * Huge directories. It's again the fat access problem like above. (In
+ 2.1/2.2 kernels it's faster due to the gain from dentry caching.)
+
+ * Compression on write access. Compression speed depends heavily on
+ the data to compress. In general you can say, the better the data
+ compress the faster the compression.
+
+ * Compressing incompressible data (on write access). This only wastes
+ time since the driver throws the compressed data away and uses the
+ uncompressed data if the compressed data turn out to be in fact
+ larger. The only thing you can do against this is disabling
+ compression if you know the data you want to write don't compress
+ any more (e.g. when writing already compressed files).
+
+ * Highly fragmented (at internal MDFAT level) filesystems on write
+ access. This puts the search algorithm that searches for free space
+ in a long loop finding a fitting hole for data to be written. This is
+ probably the most important reason for sudden system freezes. It's
+ also the most annoying one since there's really nothing to do against
+ it. This kind of fragmentation is normal to every compressed
+ filesystem and cannot be avoided. The more you write the worse it
+ becomes. (See below for details on that kind of fragmentation. Note
+ that it's very different from that FAT level fragmentation known from
+ FAT based filesystems.)
+
+ Compression and free sector finding may be fine-tuned with some of
+ the dmsdos options. This is not really explained here, see mount
+ options cf:xx and speedup:xx.
+
+ Dmsdos never freezes the system totally, it periodically unfreezes even on
+ extremely hard disk access. You *can* login into another console and you
+ *can* enter commands there. No keystroke gets lost though you may not see
+ a reaction on the console immediately, but after some seconds you should
+ see something. Just be warned and prepared :)
+
+ Compressed formats
+ ------------------
+
+ The scheme I named the compression methods is simply this: Take the first
+ four bytes of a compressed cluster. For the DS-0-2 method, for example,
+ they are 'D','S',0x00,0x02.
+
+ Format: Used by: Readable: Writable:
+ DS-0-0 win95 doublespace yes yes
+ DS-0-1 never reported to exist works in theory :)
+ DS-0-2 msdos 6.2 doublespace yes yes
+ JM-0-0 msdos 6.22 drivespace yes yes
+ and win95 drivespace 3 yes yes
+ JM-0-1 win95 drivespace 3 'High' yes yes
+ SQ-0-0 win95 drivespace 3 'Ultra' yes yes
+ uncompressed all versions yes yes
+ (no header) Stacker 3 yes yes
+ 0x81-0 Stacker 4 yes yes
+
+ Yes, meanwhile all currently known methods are supported (hmm, why is this
+ still listed under restrictions...)
+
+ Fragmented clusters in drivespace 3 volumes
+ -------------------------------------------
+
+ They can be read since dmsdos 0.8.1pl5. They can be written since
+ 0.9.0pl10, but writing fragmented clusters is supposed extremely
+ experimental. If dmsdos needs to rewrite data that are stored in a
+ fragmented cluster it first deletes the old data and saves the new
+ data in the usual linear order. Fragmentation writes are only done
+ if the data do not fit on the disk in an unfragmented way. Currently,
+ dmsdos becomes VERY noisy when it writes fragmented clusters...
+ This is considered experimental and needs further testing. You can
+ currently switch off this feature by setting speedup bit#8 (this is
+ a mount option, see below).
+
+ Fragmented and suballocated clusters in Stacker volumes
+ -------------------------------------------------------
+
+ Of course, they can be read. But currently, they cannot be written.
+ If dmsdos needs to rewrite data that are stored in a fragmented or
+ suballocated cluster it currently deletes the old data and saves the new
+ data in the usual linear order. Fragmented write may come soon,
+ suballocated write is more complex and currently considered not very
+ important for dmsdos. We'll see...
+
+ Swapping to a file on the compressed partition
+ ----------------------------------------------
+
+ Just No. It doesn't work. Don't bother me with ideas of swapping to a
+ compressed ram disk which would be a memory compressor... bah!
+ That's all gibberish, crap, and dogsh*t. I won't write code to support
+ this. You know very well that swapping has to be extremely fast, so use
+ a partition on your fastest disk for this purpose. Not a swapfile, and
+ never in a fat based filesystem (just imagine what a filesystem driver
+ has to do in order to access the *end* of a large file... yeah, tons
+ of fat access...)
+
+ attention, DOSEmu users
+ -----------------------
+
+ You may have to unmount all fat based partitions before running
+ DOSEmu depending on your configuration. If DOSEmu is configured to use
+ wholedisk or partition access (this is often the case to let DOSEmu access
+ compressed partitions) there's a risk of destroying your compressed
+ partitions or crashing your system because of confused drivers.
+
+ Let me explain why. Dmsdos does a lot of internal caching, but cache
+ contents won't be up to date if DOSEmu directly modifies the disk. This
+ confuses dmsdos and may mess up your data. On the other hand, dos
+ dblspace/drvspace.bin also has a kind of caching mechanism that gets
+ confused by the Linux dmsdos driver's write access. Confusion even
+ occurs if one party mounts read-only and the other mounts read-write.
+
+ Note that it is always safe to redirect the compressed partitions with
+ lredir or emufs.sys. Refer to this table for configurations that are safe
+ (marked with +) and which are dangerous (marked with -):
+
+ DOSEmu: Linux fat-based filesystems:
+ -------------- --------------------------------
+ not mounted mounted
+ mounted ro rw
+ wholedisk ro + + -
+ wholedisk rw + - -
+ partition ro + + -
+ partition rw + - -
+ lredir/emufs.sys ro/rw + +
+
+ Memory mapping
+ --------------
+
+ *Should* work. :^)
+
+ Underlying dos filesystem limitations
+ -------------------------------------
+
+ None. Really, they're gone since the CVF-FAT interface is used.
+
+ Umsdos users warning
+ --------------------
+
+ If you want to use umsdos-style long filenames in compressed filesystems,
+ use kernel 2.0.xx or get at least kernel 2.1.94.
+
+ Win98 compatibility
+ -------------------
+
+ HEY! Windows People !
+ Everyone uses it in the Windows World(TM) :)) Let me know whether
+ it works, please. (I refuse to buy Win98 just for such a test.)
+ Or have you all switched to WinNT yet ?
+
+ WinNT compatibility
+ -------------------
+
+ No, WinNT does not support compressed FAT partitions. But...
+ :))
+ There's meanwhile a small chance to port dmsdos to WinNT. This is
+ somewhat interesting because neither Doublespace nor Stacker runs under
+ WinNT. But I can't do it myself because of lack of knowledge :( For more
+ information, see file PORT_TO_WIN32. The trick is hacking in the dmsdos
+ library so it compiles under that operating system... I just managed to
+ compile one dmsdos utility, mcdmsdos, under WinNT...
+
+ Last but least
+ --------------
+
+ Dmsdos has been written for dos/win95 compatibility. It's just a relict
+ from the time when I programmed some software that had to run both under
+ dos and Linux and I had to compress my drive because hard disk space was
+ rather expensive. Times and prices have changed a lot since then (at least
+ in the country where I live), but dmsdos has still been improved for fun.
+
+ If you don't need dos/win95 compatibility, i.e. if you want to compress
+ Linux-only data, let me say: DON'T DO IT. Clean up your disk, or buy a
+ larger one, but don't compress it. Really. Compress single files (e.g.
+ manpages) with gzip. Throw away the crap you don't need. Shrink your
+ dos partition or remove it completely :)
+
+ If you still want to compress Linux data, dmsdos may not be the software
+ you want. The msdos filesystem is extremely unsuited for fast disk access,
+ umsdos suffers from it too. The CVF structure msdos uses is even more
+ unsuited, and the time required by msdos online compression may kill the
+ rest of Linux' performance on these filesystems.
+
+ Just to let you know about some other online compression software for Linux:
+ There's a package 'double' available on sunsite for Linux online
+ compression. There are also compression patches for the ext2 filesystem
+ available under the name 'hufs' and 'e2compr'. I've also heard about a
+ 'zlibc' project which lets you access gzip compressed files as if they were
+ uncompressed - it works by a patched libc. Look at common Linux ftp sites.
+
+ Like dmsdos, all those packages may have their specific advantages and
+ disadvantages. Some are said to be not 100% stable, but who can claim that
+ his software is really 100% stable :-) Please don't flame at me if the
+ packages don't do what you want - I haven't tested them and I don't know
+ much about their qualilty.
+
+
+3. Mount options
+------------------------------------------------------------------------------
+
+CVF related mount options are usually surrounded by the CVF-FAT interface
+options. These are "cvf_format=xxx" and "cvf_options=yyy". The string "xxx"
+can be "dblspace" for a doublespace or drivespace CVF and "stacker" for a
+stacker CVF. For more information about the "cvf_format=xxx" option,
+especially in cooperation with kerneld and automatic module loading, see
+the dmsdos installation instructions (file INSTALL.TXT) and the generic
+CVF-FAT documentation (file linux/Documentation/filesystems/fat_cvf.txt).
+
+When we speak about dmsdos mount options, we usually mean the string "yyy" in
+"cvf_options=yyy". That's what this chapter describes.
+
+Summary:
+--------
+
+The dmsdos driver currently recognizes the following options:
+
+comp:xxx, cf:xxx, bitfaterrs:xxx, loglevel:xxx, speedup:xxx
+
+(for backwards compatibility also comp=xxx, cf=xxx, bitfaterrs=xxx and
+loglevel=xxx, speedup=xxx are accepted)
+
+The xxx has to be replaced by an appropriate parameter, of course. All
+options have to be specified separated by plus signs (not commas) in a list
+in the mount command:
+'mount -t msdos -o cvf_options=option1+option2+option3 ...'.
+Consider the string 'cvf_options=option1+option2+option3' as *one* FAT
+driver option! For backwards compatibility, also a dot (.) is recognized
+as option separator symbol.
+
+ comp:xxx
+ The comp option selects which compression method to use for compressing
+ files when they are written to a compressed partition. The default value
+ (which is used if no comp option is specified) is hard-coded to guess.
+
+ Don't get confused by this list. Dmsdos usually determines automatically
+ the right and, if there's a choice, the best compression for you (by
+ scanning the filesystem and analysing the compressed data written by
+ dos/win95). But if you explicitely tell dmsdos to use a specific
+ compression, the driver does what you tell it to do. So be careful.
+
+ comp:no Selects no compression for write access i.e. all files written
+ to a compressed partition are stored in fact uncompressed.
+ This option may speed up write access, but doesn't make much
+ sense on a compressed filesystem (except for debugging).
+ comp:ds00 Selects DS-0-0 compressed format for write access. This is
+ win95 native format if you don't have drivespace 3. This
+ format may also be used for Stacker 3 and 4 (though not
+ recommended for them as it's not the native Stacker format).
+ comp:ds01 Obsolete. DS-0-1 has never been reported to exist. So what.
+ comp:ds02 Selects DS-0-2 compressed format for write access. This is
+ dos 6.0-6.2 (*not* 6.22) format. Under dmsdos, it is rather
+ identical to DS-0-0 except for the compression header.
+ comp:jm00 Selects JM-0-0 compressed format for write access. This is
+ dos 6.22 format and win95 drivespace 3 'Normal' format.
+ comp:jm01 Selects JM-0-1 compressed format for write access. This is
+ win95 drivespace 3 'High' format. Note that it is rather
+ identical to JM-0-0 in dmsdos (but not in win95).
+ comp:sq00 Selects SQ-0-0 compressed format for write access. This is
+ win95 drivespace 3 'Ultra' format. Only drivespace 3 is
+ known to be able to handle this format.
+ comp:sd3 Selects SD-3 compressed format for write access. This is
+ Stacker 3 format. It may be used for Stacker 3 and 4.
+ comp:sd4 Selects SD-4 compressed format for write access. This is
+ Stacker 4 standard format. It may only be used for Stacker 4.
+ comp:guess Tries to find out automatically which compression method
+ dos used to create the compressed partition and selects the
+ appropriate format. If guessing fails, no compression is used
+ and a message is printed in the syslog.
+
+ WARNING: You *can* specify senseless options in the command line, for
+ example, mount a doublespace drive and select stacker compression.
+ You may end up in a mess in that case.
+
+ NOTE: It's not true that JM-0-1 (Drivespace 3 'High') compresses better
+ than JM-0-0 (Drivespace 3 'Normal'), at least under dmsdos. Set the
+ compression effectivity with the cf:xx option. In fact, dmsdos uses the
+ same routine to compress DS-0-0, DS-0-1, DS-0-2, JM-0-0 and JM-0-1
+ (they are almost equal, so similar that only, for example, the meaning
+ of one bit is different or some constant offsets differ). It's just
+ true that SQ-0-0 is more powerful than the DS and JM formats, and also
+ SD-4 is more powerful than SD-3. Also, one can say that SD-3 has
+ something in common with the DS and JM formats. Yes, SD-4 and SQ-0-0
+ have some similarities, too, but I really can't say which one is best.
+
+ cf:xx (where xx is a decimal number from 1 to 12)
+ Selects the compression effectivity and speed. Since compression
+ effectivity always runs in concurrence to speed, try some values and
+ watch what happens. 1 is fastest but least efficient compression, 12 is
+ most efficient but slowest compression. The default value can be selected
+ before compiling by 'make config' (note that the value minus 1 must
+ be specified there, i.e. the range is from 0 to 11 there).
+
+ If the external dmsdos daemon is running, this option doesn't have an
+ effect because the daemon doesn't care about this option - it reads the
+ compression level from its command line instead.
+
+ bitfaterrs:xxx
+ Selects what to do with inconsistencies in the internal filesystem
+ sector allocation table. Default is setting the filesystem read-only.
+
+ bitfaterrs:repair
+ Repair BITFAT mismatches if there are any when mounting the
+ filesystem read-write. This verifies the BITFAT and corrects
+ allocation errors. It is highly important for write access
+ that the BITFAT has no errors. For read-only access the
+ BITFAT is not needed. In read-only mode, this option is
+ ignored. This option may be dangerous in case dmsdos has
+ not recognized your filesystem correctly. So try without it
+ the first time until you can be sure.
+ bitfaterrs:ignore
+ Ignores BITFAT mismatches. This is dangerous and can cause
+ awful MDFAT level crosslinks as well as complete data loss.
+ Use this setting only if you know what you are doing.
+ bitfaterrs:setro
+ Sets the filesystem to read-only mode if BITFAT mismatches
+ are detected. This is safe and also the default behaviour.
+ bitfaterrs:nocheck
+ Don't check the filesystem's internal tables when mounting.
+ This speeds up the mount process a lot. This option
+ is *not* recommended, of course, unless you are very sure
+ there are no errors in the filesystem.
+
+ WARNING: Generally, BITFAT mismatches are *severe* filesystem errors.
+ You will destroy your data if you write to a filesystem that contains
+ BITFAT mismatches. Note that Dos seems to check and repair the BITFAT
+ automatically and silently on each bootup, so just booting into Dos may
+ repair those errors.
+
+ loglevel:xxx
+ Sets the driver's loglevel to xxx (a decimal or, if preceeded by 0x,
+ hexadecimal number). The number is a 32 bit field. Each bit represents a
+ family of messages that will be logged when the appropriate bit is set.
+ See file dmsdos.h for details about the message families (watch out for
+ LOG_SomeThing defines and what bits they use). The meanings of the bits
+ are not documented here since they may differ from version to version.
+
+ WARNING: You are strongly encouraged to increase your kernel's log buffer
+ size to at least 64KB (it's in file linux/kernel/printk.c:
+ '#define LOG_BUF_LEN size_in_bytes', please note that the size *must* be
+ a power of 2) - otherwise you are likely to lose messages or receive even
+ complete garbage due to log buffer overruns.
+
+ speedup:xxx
+ Sets the driver's speedup flags to xxx (note: these flags are always the
+ same for all CVFs). xxx is a decimal or (if preceeded by 0x) hexadecimal
+ value. The meanings of these bits are listed below. The speedup is active
+ when the appropriate bit is set.
+
+ WARNING: You should not use this option or change the default value
+ unless you know exactly what you are doing.
+
+ *** Less speedup than the default may result in painfully
+ sluggish filesystem access.
+ More speedup than the default may cause dangerous side
+ effects.
+
+ However, speedup may have to be selectively disabled in order to hunt
+ bugs :)
+
+ bit #0: Leave directories uncompressed
+ Never compress directories for drivespace 3 and stacker
+ (others don't support compressed directories at all).
+ WARNING: Directories are accessed very often, so it's
+ best not to compress them. Usually set this bit.
+
+ bit #1: Leave umsdos EMD file uncompressed
+ This is only for umsdos upon dmsdos: never compress the
+ --linux-.--- special file (it contains extended directory
+ information).
+ WARNING: This file is even more often written than a
+ directory since it contains the access times (the
+ directory contains only the modification times). You will
+ regret compressing this file, even if you own the fastest
+ computer of the world. Don't ask, set this bit.
+
+ bit #2: Skip exact search on BITFAT allocation
+ Search faster but less carefully for free space in bitfat
+ at the cost of more fragmentation. This bit is for
+ sector allocation code. If you set this bit allocation on
+ large CVFs is faster but also causes a little more
+ fragmentation. On the other hand, searching more carefully
+ leads to sudden system freezes for up to some seconds on
+ large partitions. Set this bit if you cannot tolerate
+ them. Usually this bit is cleared.
+
+ bit #3: Fast unmount
+ Write dirty buffers on unmount immediately without
+ compressing them before. This switch defines what happens
+ with unwritten dirty clusters that are in the cache when
+ the filesystem is unmounted. If the bit is clear it means
+ write them back, but compress them before. If it is set,
+ it means write them back without compression.
+ WARNING: COMPRESSION TAKES SOME TIME, YOU'LL NOTICE IT.
+ If you are prepared to wait even some minutes (on an old
+ 386SX16 - on a P100 this should be max. one second) on
+ unmount you can clear this bit. Usually it is set.
+
+ bit #4: Enable write-back cluster caching (instead of write-through)
+ If this bit is set the data in the cluster
+ cache that have been changed by an application and have to
+ be written back to disk are not written back immediately -
+ they are kept some time in memory just in order to save
+ disk access when the application again changes the data.
+ There's a small risk of data loss if the system happens to
+ crash before the data are written back - but since your
+ operating system is Linux a crash is rather unlikely :)
+ This bit is usually set. When hunting bugs, it should be
+ cleared since it may prevent finding a way to reproduce
+ a bug.
+
+ bit #5: Enable cluster read-ahead
+ If this bit is set, this causes the driver to initiate a
+ low-level disk access for some data when it thinks they
+ are most likely needed in a short time later. The trick is
+ that the driver doesn't wait for the disk access to
+ finish. So the disk can position its head (and so on)
+ while the system can do something else. Most likely the
+ disk access has finished when the data are actually needed
+ - this saves some time we otherwise had to wait. Well, at
+ least this is the nice idea of read-ahead. However, since
+ read-ahead relies on a prediction, there may be situations
+ when it is useless or even a loss.
+ This bit is usually set. When hunting bugs, it should be
+ cleared since it may prevent finding a way to reproduce
+ a bug.
+
+ bit #6: Fast BITFAT allocation
+ Switch to very fast sector allocation. This speeds up
+ bitfat allocation because the search algorithm that tries
+ to avoid fragmentation is simply switched off. BE WARNED,
+ it causes *much* fragmentation in very short time. The
+ "filesystem almost full" warning is suppressed. This
+ switch is meant as a last resort if you cannot tolerate
+ system slowdowns at all. Don't fill the compressed
+ filesystem up to more than 3/4 when this switch is set.
+ Write access may fail horribly and cause data loss due to
+ too high fragmentation.
+ This bit is usually cleared. If you are unsure about the
+ dangers let it cleared. Only set it if you really know
+ what you are doing.
+
+ bit #7: Use daemon for background conpression if present
+ Use the daemon for delayed compression in the background.
+ This bit is for users of the internal daemon since the
+ internal daemon cannot be disabled by simply killing it.
+ So clearing this bit is another way to disable the daemon.
+ This bit is usually set. When hunting bugs, it should be
+ cleared since it may prevent finding a way to reproduce
+ a bug. *** This bit only affects daemon *compression*.
+ Daemon-controlled memory management still works
+ regardless of this bit.
+
+ bit #8: Avoid fragmented writes
+ Speedup Bit#8 controls what to do when the filesystem is
+ too fragmented for normal write access. Usually all data
+ for one cluster are stored in contiguous sectors, but if
+ the filesystem is too fragmented there may not be a 'free
+ hole' that is large enough for the data. Speedup Bit#8
+ controls what to do in that case. If this bit is set
+ dmsdos just returns a 'no space left on device' error and
+ refuses to write to the CVF.
+ Drivespace 3 and Stacker know a hack for that situation:
+ they allow storing the data of one cluster in several
+ fragments on the disk. If the bit is clear, the driver
+ tries to write the data in several fragments. Be warned,
+ this makes future filesystem access much slower as it
+ needs more disk head movements to read fragmented
+ clusters.
+ *** Note that for Stacker fragmented writes are currently
+ not implemented. Doublespace and drivespace (version
+ <=2) do not support this at all. So the bit is
+ ignored for them.
+
+ Default speedup (187) is reasonable for normal read and moderate write
+ access. For high-traffic write access you might want to set additionally
+ bit #2 (speedup:191) or even bit #6 (speedup:255). On quite full
+ or highly fragmented CVFs this makes a *great* difference, but it's up
+ to you to decide between safety and speed.
+
+Note:
+ You can use the external utility dutil to setup or change dmsdos options
+ later. Look at the following example commands:
+
+ dutil /mnt setcomp xxx (for changing the comp:xxx option),
+ dutil /mnt setcf xx (for cf:xx option),
+ dutil /mnt setspeedup xxx (for speedup:xxx option) and
+ dutil /mnt setloglevel xxx (for loglevel:xxx option).
+
+ Just replace the directory name '/mnt' with your mount point in the
+ examples. Besides, you must be root to change dmsdos options via dutil.
+ The bitfaterrs:xxx option cannot be changed with dutil since it only
+ makes sense at mount time :)
+
+ The string 'option1+option2+option3...' in the FAT mount option
+ 'cvf_options=option1+option2+option3...' has a hard coded limit of 100
+ characters (this is a limitation of the CVF-FAT interface). The string
+ is silently cut down if it is too long. Don't worry, you won't manage
+ to break the limit without specifying senseless parameters :)
+
+ For those who want to know everything exactly, the CVF-FAT interface
+ has a further FAT driver option (*not* dmsdos option): cvf_format=xxx.
+ The parameters that the dmsdos module allows for 'xxx' are 'dblspace'
+ (for all doublespace and drivespace versions) and 'stacker' (for all
+ stacker versions). Usually you don't care about them since the formats
+ are detected automatically. They may, however, be useful in order to
+ trigger kerneld to load the dmsdos module on demand. For details please
+ look at the dmsdos installation instructions (file INSTALL.TXT) and the
+ generic CVF-FAT interface documentation (file
+ linux/Documentation/filesystems/fat_cvf.txt).
+
+ And now a warning: If you want to mount a CVF *be sure* to have the
+ dmsdos module loaded. The plain FAT driver also mounts some CVFs without
+ complaining (but begins to scream loudly when you, for example, do
+ a 'ls' on the mountpoint). As some CVFs look very similar to normal,
+ uncompressed filesystems, the FAT driver has no chance to detect a
+ CVF at mount time (and e.g. print an error) when the dmsdos module is
+ not loaded.
+
+ *** If you always specify a "cvf_format=something" option you never
+ run into that problem.
+
+Some simple examples:
+ 1. Your msdos partition that contains CVFs is on /dev/hda1. You want to
+ mount it under /DOS and the CVF under /mnt.
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o loop /DOS/dblspace.001 /mnt
+ 2. Like above, but you need umsdos support.
+ mount -t umsdos /dev/hda1 /DOS
+ mount -t msdos -o loop /DOS/dblspace.001 /mnt
+ 3. Like above, but you also have umsdos style long filenames in your CVF
+ mount -t umsdos /dev/hda1 /DOS
+ mount -t umsdos -o loop /DOS/dblspace.001 /mnt
+ 4. Like 1., but you need Win95 long filename support *only inside* the
+ compressed partition.
+ mount -t msdos /dev/hda1 /DOS
+ mount -t vfat -o loop /DOS/dblspace.001 /mnt
+ 5. Like 1., but you want to feel safe and refuse write access to the
+ compressed partitions only (but the uncompressed host should be writable).
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o ro,loop /DOS/dblspace.001 /mnt
+ 6. Like 4., but you need Win95 long filenames also in the uncompressed host
+ partition
+ mount -t vfat /dev/hda1 /DOS
+ mount -t vfat -o loop /DOS/dblspace.001 /mnt
+ 7. You have managed to burn a compressed filesystem onto a CD (This is
+ quite easy - put the large file 'dblspace.001' into an iso9660
+ filesystem and burn it. Be warned, dos can't mount it from CD. Uh...)
+ mount -t iso9660 /dev/cdrom /cdrom
+ mount -t msdos -o loop /cdrom/dblspace.001 /mnt
+ 8. Like 1., but the CVF has been umssynced and contains umsdos long
+ filenames.
+ mount -t msdos /dev/hda1 /DOS
+ mount -t umsdos -o loop /DOS/dblspace.001 /mnt
+
+Some complex examples (I didn't break the long lines herein). The option
+strings might look strange but they are correct:
+ 9. Like 1, but you want the driver to repair bitfat errors automatically.
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o loop,cvf_options=bitfaterrs:repair /DOS/dblspace.001 /mnt
+ 10. Like 1, but you need more speedup due to high-traffic write access
+ (well, please note that this is not recommended, but it works):
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o loop,cvf_options=speedup:255 /DOS/dblspace.001 /mnt
+ 11. Like 9 and 10 (repair bitfat errors and more speedup):
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o loop,cvf_options=bitfaterrs:repair+speedup:255 /DOS/dblspace.001 /mnt
+ 12. Like 1, but disable write access, ignore bitfat error and do debug
+ logging (be careful, this causes tons of debug messages):
+ mount -t msdos /dev/hda1 /DOS
+ mount -t msdos -o loop,ro,cvf_options=bitfaterrs:ignore+loglevel:0xffffffff /DOS/dblspace.001 /mnt
+
+Mounting via /etc/fstab:
+
+ This may be a bit tricky. A typical fstab entry for a CVF looks like this:
+
+ /DOS/drvspace.001 /DOSF msdos loop 1 0
+ /dev/hda1 /DOS msdos defaults 1 0
+
+ Be sure to keep the sixth field (the 'pass number') zero to prevent fsck
+ from checking the CVF (it does not know about CVFs and very likely fails).
+ Also the filesystems in /etc/fstab seem to be mounted in reverse order,
+ but that may depend on your mount or libc version. Try to reorder the
+ lines it it doesn't work.
+
+
+4. Kernel messages
+------------------------------------------------------------------------------
+
+This section has moved to file messages.doc.
+
+
+5. Installation
+------------------------------------------------------------------------------
+
+This section has moved to file INSTALL.TXT in the main dmsdos directory.
+
+
+6. Troubleshooting
+------------------------------------------------------------------------------
+
+This section has moved to file troubleshooting.doc.
+
+
+7. Defragment procedures
+------------------------------------------------------------------------------
+
+There are three possibilities for fragmentation of a compressed partition:
+ - Msdos FAT level: The CVF itself is a file in a msdos partition that
+ might be broken into several fragments.
+ - Internal FAT level: The files stored in the CVF are fragmented in a
+ similar way like FAT level fragmentation on a real dos partition. Unlike
+ in a real msdos partition, filesystem access does *not* suffer from this.
+ There is no need for defragmenting a CVF at this level (except that you
+ want to make the CVF smaller - then it may be necessary).
+ - Internal MDFAT level: Worst fragmentation that can happen in a CVF. All
+ write access (not only file creation and deletion) causes MDFAT level
+ fragmentation. If a CVF is too fragmented at this level, write access
+ even fails as if the disk was full. Defragmenting a CVF usually refers
+ to this kind of fragmentation, which is very different from FAT level
+ fragmentation.
+
+How to defragment a CVF at internal MDFAT level see your dos/win95 or
+stacker documentation. Dmsdos does not support CVF maintainance.
+
+
+8. How to contribute to dmsdos development
+------------------------------------------------------------------------------
+
+First of all, thanks a lot to all alpha testers and all who have sent problem
+reports, found bugs in the sources or documentation, suggested solutions to
+some problems and asked questions due to unclear documentation. Without your
+help dmsdos would not have become what it is.
+
+Also many thanks to all who simply mailed they liked dmsdos.
+
+At this place, I must also say many thanks to Gordon (VFAT fs) and Matija
+(UMSDOS fs) for the help to make dmsdos work with the standard 2.1.xx
+kernels.
+
+If you have a problem that is not covered in the documentation please email
+me. But, *please*, be sure to read the file 'troubleshooting.doc' before.
+If it's not mentioned there, you may have uncovered a bug that
+should be fixed. Please watch your kernel log (/var/log/messages) for
+strange dmsdos messages too. Please include the following information in your
+mail message:
+
+- Linux kernel version, dmsdos version (see kernel messages).
+- If it doesn't compile, gcc version, as version, libc version, make version.
+- MS-DOS version you a) used to create the compressed partition, b) you
+ currently use.
+- Dmsdos kernel messages that are produced when the problem occurs.
+- Information about your disk hardware (hardware sector size is important -
+ there are some hacks for hardware sector sizes != 512 bytes in the code)
+- Size of the real (uncompressed) dos partition.
+- dutil output (run it on the dmsdos CVF directory that causes problems).
+- Everything else, of course, you think I need to know about the problem.
+- A valid email address (I wouldn't write this if I hadn't received some
+ mails I couldn't reply to because the return address was rejected).
+
+
+9. The external dmsdos utility 'dutil'
+------------------------------------------------------------------------------
+
+(For a summary, take a look at the dutil manual page (file src/dutil.1).)
+
+Run dutil on a dmsdos mount point to obtain additional information about the
+compressed partition. Example: 'dutil /mnt'. This utility has
+some additional debugging options. These are not described here since they
+are for debugging only. Run dutil with debug options only when you are asked
+by the dmsdos author/maintainer in a response to any problem report.
+
+Note: Debugging options require running dutil as root. Other users can only
+see the general filesystem information dutil prints on the screen. See file
+dblspace_ioctl.c for details on what can be done only by root.
+
+You can use the external utility dutil to setup or change dmsdos options
+later. Use 'dutil /mnt setcomp xxx' (comp:xxx option) and
+'dutil /mnt setcf xx' (cf:xx option). Note to replace the directory name
+'/mnt' with yours. This also works: 'dutil /mnt setspeedup xxx'
+(speedup:xxx option) and 'dutil /mnt setloglevel xxx' (loglevel:xxx option).
+The dmsdos options can only be changed by root.
+
+To perform a simple filesystem check, use the command
+'dutil /mnt checkfs'. This fs check doesn't do any repairs, but
+immediately reports some fs inconsistencies. There shouldn't be any unless
+the driver complained already at mount time (see kernel log). If there are
+errors and the dmsdos driver didn't find them at mount time, this is a
+serious dmsdos bug (it implies that data have been destroyed by the dmsdos
+driver) and should be reported. If the command complains about errors but
+the dos fs checker says everything is okay, you should also send a bug
+report.
+
+To sync the write-back cache you can use 'dutil /mnt synccache'.
+By default, the command waits until the data have been passed to the kernel's
+filesystem buffering interface. (If you want to ensure that they are really
+physically written issue a 'sync' afterwards which causes the kernel to
+write all dirty buffers to disk.) You can specify an additional argument !=0
+in order to pass the cache contents to the daemon instead of writing directly
+to disk (not useful if you want to ensure that the data are written *now*).
+
+Note that dutil displays two different compression ratios - an internal and
+an external one. The internal one is always lower and is used to estimate
+how much space is left on the device. It depends on which information can
+actually be obtained from the MDFAT without scanning the whole disk (would
+take too much time). If you want to compare different versions of compressed
+filesystems to each other (e.g. doublespace vs. stacker) concerning the
+compression effectivity, always use the external (second) one because it is
+always calculated in the same manner. The external compression ratio is
+calculated according to this formula:
+
+ Space that would be used on an equivalent uncompressed msdos filesystem
+------------------------------------------------------------------------- : 1
+ Space that is used on the disk
+
+'Space' means what file and directory clusters occupy, it does *not* mean:
+FAT, root directory, futher filesystem maintainance information.
+
+
+10. The dmsdos daemon 'dmsdosd'
+-----------------------------------------------------------------------------
+
+The dmsdos daemon is a program that runs in the background and
+does delayed compression when the system is idle. This prevents the dmsdos
+driver from slowing down your system when compressing large amounts of data.
+In newer dmsdos versions, the daemon has also some influence on dmsdos
+memory usage (it shrinks the amount of memory dmsdos eats for caching if
+the dmsdos driver has not much to do). This might be an advantage for
+systems that are short on memory.
+
+Actually, the daemon exists in two variants. The first one is a user-level
+process (dmsdosd) that must be started with a command line
+like 'dmsdosd /mnt' or 'dmsdosd /mnt xx', where /mnt is the mount point
+and 'xx' is the compression level like in the cf:xx mount option. If the
+xx is omitted it defaults to 12 (i.e. maximum compression). Please note
+that this variant of the dmsdos daemon does not care about the dmsdos cf:xx
+option or the dutil setcf command, but uses its own from its command line.
+This variant of the dmsdos daemon is called 'external daemon'. For a summary
+concerning this variant of the dmsdos daemon you can also take a look at the
+dmsdosd manual page (file src/dmsdosd.1).
+
+The second variant is built inside the kernel and starts up automatically
+when it is needed and exits when it is no longer needed. This variant is
+called 'internal daemon' and cannot be killed from user space, not even by
+root. It also obeys the cf:xx option and the dutil setcf command.
+
+You can only use one daemon at a time. You must decide before compiling
+dmsdos which variant to use. During dmsdos configuration ('make config')
+you are asked which one you want. Both daemon variants do the same work, but
+the internal one is more user-friendly (well, you don't have to start it
+manually :-) ) while the external one is more flexible and seems to eat up
+less system time (but has other backdraws - you must kill it before you can
+unmount the filesystem that has been given as a command line argument). If
+you are using the internal daemon, the external one won't start - it always
+exits with a "no permission" error.
+
+If you are going to use the external daemon, please note this: DON'T start
+one daemon for each filesystem. The one single daemon does all the work for
+the filesystem given in its command line and for all other dmsdos
+filesystems you have currently mounted and for all other dmsdos filesystems
+you mount later while the daemon is running. To prevent security problems
+only root can start the external daemon (and even more, the driver refuses
+to talk to a non-root dmsdos daemon via the ioctl interface). The external
+daemon may be killed (by root) at any time, even when it is under hard work.
+Send a SIGINT to it to let it finish its actual work and then exit. Send a
+SIGTERM to it to make it exit immediately. In any case, the daemon catches
+the signals and exits cleanly.
+
+The dmsdos daemon works like this: When it starts up, it tells the dmsdos
+driver not to compress the data when doing write access to the compressed
+filesystem. Instead, the driver writes the *uncompressed* data to the disk
+and maintains a list of clusters that have to be compressed. When the daemon
+has time to do something, it asks the driver for a cluster to compress. Then
+the driver looks up a cluster in its 'clusters-that-must-be-compressed'
+list, reads it from disk and passes it to the daemon. The daemon itself now
+compresses the data. When it has finished compression, it passes the
+compressed cluster back to the driver, which in turn writes it to the disk.
+
+There are two situations the list gets lost and the data that have not yet
+been compressed by the daemon remain uncompressed on the disk:
+
+ 1. Rebooting the system clears the whole list.
+ 2. Unmounting the filesystem clears all the list entries that
+ contain clusters of the filesystem to be unmounted.
+
+Please note that this kind of "cache" has a limited size. If you exceed that
+size by writing *much* data at once the dmsdos driver no longer lets the
+daemon do the job, but starts to compress the data itself. You will notice a
+moderate system slowdown in that case. In extreme situations you might not
+even see an immediate reaction on a keystroke, but those cases have become
+rare. (If you suddenly see a *great* system slowdown this is usually caused
+by too much fragmentation - not by compression.)
+
+The size of this "cache" can be selected during 'make config'. The number
+specified there means the maximum (i.e. total, not per CVF) number of
+clusters that can be in the list. If you want to know the amount in bytes,
+multiply with the cluster size (which is 32KB for drivespace 3 and 8KB for
+the other compressed filesystems). The dmsdos driver may use up to this
+amount of _uncompressed_ disk space inside each compressed partition as
+"cache". You can use dutil to find out how much uncompressed free space is
+on the disk. If it is less than the "cache" size you shouldn't write more
+than the amount of uncompressed free space to the disk at once. Otherwise,
+the disk is getting full with the cached uncompressed data and you receive
+a 'no space left on device' error.
+
+If there's nothing to compress any more the daemon goes to sleep mode, but
+when the dmsdos driver needs the daemon it tries to awake it immediately.
+Furthermore, the daemon awakes periodically to check for data and inform
+the driver when it is time to release memory.
+
+Please note that using the daemon causes *duplicate* write access to the
+filesystem. This results in a lot of more MDFAT level fragmentation. Thus,
+the daemon cannot really make your system faster. In fact, it only makes
+your system *appear* faster by doing a kind of load balancing. This load
+balancing avoids unnecessary locks (kind of wait loops) in the driver. It
+means the time some processes would have to wait can be used by other
+processes.
+
+As I'm sometimes asked what to recommend: For a system with read-only or
+very little write access and enough memory you don't need the daemon. For a
+system with moderate write access and an appropriate fast processor the
+internal daemon is best choice. For a system with moderate to high-traffic
+write access to compressed partitions I recommend the external daemon.
+If you have a slow processor and you need CPU time for some other intensive
+computing, it's best to use the external daemon with a nice value (see
+'man nice') - this prevents the daemon from slowing down the other
+processes. If the daemon does not compress fast enough try to lower the
+compression level in its command line. Well, and if you want to write a
+large chunk of data (some 100MB) to a compressed partition at once, disable
+the daemon before - it cannot help in this situation. It only causes too
+much fragmentation which in turn slows down the system horribly.
+
+Just another hint: If you want to use the daemon only for memory management
+but not for delayed compression, you can clear speedup bit #7.
+
+You see, there's much room for experimentation here, especially if you use
+the external daemon :) There are some pre-configured dmsdos config files
+which just implement some of the above recommendations. This is explaind
+in INSTALL.TXT so see there for details, please.
+
+
+11. The dmsdos library 'libdmsdos.a'
+-----------------------------------------------------------------------------
+
+This feature is currently experimental, but begins to become more and more
+interesting.
+
+The dmsdos library is intended to provide some low-level dmsdos functions
+to user processes. The library is not new source code - it's just the
+dmsdos module source compiled in a different way (well, with some tricks).
+The dmsdos library aims towards working with raw CVFs, not filesystems.
+It is usable only for *not* mounted CVFs. Well, for mounted ones there's
+the dmsdos module :)
+
+Documentation (including a detailed description of the interface functions)
+can be found in file libdmsdos.doc.
+
+
+12. The dmsdos filesystem checker 'dmsdosfsck'
+------------------------------------------------------------------------------
+
+The goal of dmsdosfsck is not having a complete and reliable filesystem
+checker for CVFs. If you want that, use the filesystem maintainance tools
+that came with your CVF software package under Dos (or blame their
+manufacturers if they are not working well...). dmsdosfsck is just meant
+as a kind of workaround for problems that may show up if the dmsdos or
+FAT driver suddenly detects an unexpected inconsistency and might get
+confused. With dmsdosfsck those problems that may confuse the drivers can
+be detected and corrected before mounting the CVF.
+
+The command line of dmsdosfsck looks like this:
+
+dmsdosfsck [-a] [-r] [-v] [-l] device|file
+
+where -a means repair automatically (uh... be VERY careful), -r means
+repair interactively, -v means be verbose, and -l means list the files
+while processing them. These options are somewhat similar to the well-known
+dosfsck.
+
+Currently not all errors can be corrected. It just corrects fatal FAT
+errors like loops, crosslinks, illegal values, and unexpected ends of
+chains - things the kernel FAT driver does not like at all and tends to
+crash or hang if it runs over them. (Well, this is a question of philosophy.
+Should a kernel driver be idiot proof or may it assume the filesystem is
+valid in most cases at the most vital places? I don't want to give an
+answer, but the FAT driver of Linux 2.0.34 seems not 100% idiot proof.)
+So there is really a need for a dmsdosfsck utility here.
+
+Furthermore, it can detect but not correct MDFAT errors, crosslinks,
+damaged MDFAT entries - you must repair them under Dos using the CVF
+maintainance tools that came with your CVF software package. The kernel
+FAT driver does not see these structures, and the dmsdos driver simply
+returns read errors - those errors just cannot confuse drivers or even
+make the system unusable.
+
+It can detect and correct BITFAT allocation errors - this is necessary
+for write access. (But the dmsdos module already does it if it is told
+to do so at mount time.)
+
+It detects damaged directory entries and can correct them. This is also a
+situation the kernel FAT driver doesn't like at all. Be warned, repairing
+damaged directories is a very critical job which cannot be automated
+completely. Some damaged directories cannot be repaired and will be converted
+into files instead. This gives you a chance to take a low-level disk editor
+and repair this by hand under Dos later (if you know how to do it). Repaired
+directories are saved without compression so you should be able to analyse
+them under Dos without further help.
+
+It also checks whether one filename is used more than once in a directory.
+The file can optionally be renamed. This is usually done by appending ~%d
+to the filename where %d is the lowest possible number not causing a
+conflict.
+
+It also detects lost cluster chains but does not correct them. They just
+use space and do nothing evil. For that, use the Dos tools that came....:)
+
+
+ *** Currently, dmsdosfsck must be considered extremely alpha test ***
+
+
+It's far away from complete, but some useful things work. You *can*
+use dmsdosfsck with the generic fsck frontend. You can even
+run your usual fsck when mounting from /etc/fstab at boot time, even on
+CVF partitions. But that's not trivial. If you really want to do such
+things, take a look at the example script 'msdos.fsck-wrapper' which is
+suitable for the generic fsck frontend. Be sure to understand what the
+script does. Change it to your requirements. Then replace the link in
+/sbin/fsck.msdos (or /etc/fs/fsck.msdos) by a link to the script :)
+
+
+13. The dmsdos Midnight Commander external filesystem utility 'mcdmsdos'
+------------------------------------------------------------------------------
+
+Dmsdos comes with an interface utility that accepts standard Midnight
+Commander commands for reading archive files. The utility is named
+'mcdmsdos' and is compiled during usual dmsdos compile process.
+The utility is currently read-only.
+
+Please refer to the documentation of Midnight Commander for further
+information on how to configure an external filesystem.
+You may want to write a small shell script as a wrapper around mcdmsdos
+in order to suppress output to stderr distorting the screen, e.g.
+
+ #!/bin/sh
+ exec /usr/local/bin/mcdmsdos $@ 2> /dev/null
+
+Furthermore, mcdmsdos can be used as a utility to quickly look at what is
+inside the CVF and to extract single files from a CVF. Today mcdmsdos even
+works in a Win32 environment, so it may be worth to learn its command line
+parameters :))
+
+ mcdmsdos list <CVF_name>
+
+ lists (recursively) the contents of the CVF, i.e. the names of all
+ files that are in that compressed partition are printed.
+
+ mcdmsdos copyout <CVF_name> <path/name_of_file> <outfile>
+
+ extracts the file <path/name_of_file> from the CVF <CVF_name> and
+ writes it to <outfile> (which must be a real file, not stdout).
+
+ mcdmsdos copyin <CVF_name> <path/name_of_file> <infile>
+
+ is expected to copy <infile> into the CVF <CVF_name> at
+ <path/name_of_file> according to Midnight Commander documentation.
+ This command is currently not implemented as the utility is
+ read-only.
+
+If documentation of newer versions of Midnight Commander wants some more
+commands consider the respective operations to fail. I expect something
+like remove, rmdir, mkdir etc. which are missing in the current standard
+(I wrote this utility according to Midnight Commander 3.0 documentation).
+But that doesn't matter as long as mcdmsdos is read-only :)
+
+
+14. Authors and email addresses:
+------------------------------------------------------------------------------
+
+CVF-FAT/dmsdos is the official successor of the former dmsdos filesystem. The
+dmsdos filesystem was initially written from scratch by Frank Gockel, after
+taking a close look at the THS filesystem (a read-only doublespace fs with
+only DS-0-2 decompression written by Thomas Scheuermann). Stacker support
+and drivespace 3 'ultra' compression/decompression were added by Pavel Pisa
+as well as improved DS/JM compression and decompression routines. Meanwhile,
+it contains several parts of code that was directly provided or code that is
+based on the ideas from a lot of people over the net in order to fix bugs,
+to improve performance, and to add features.
+
+The dmsdos code is distributed under the GNU General Public Licence
+(see file COPYING).
+
+The dmsdos driver is currently maintained by Pavel Pisa (stacker access,
+SQ compression, meanwhile all other compression too) and me, Frank Gockel
+(rest of the code).
+
+Pavel's email address is pisa@cvlinux.felk.cvut.cz, my email address is
+gockel@sent13.uni-duisburg.de.
+
+If you want to contact me via email, please write in English or take a close
+look at the country codes in the email addresses :)
diff --git a/doc/ioctl.doc b/doc/ioctl.doc
new file mode 100644
index 0000000..77c9f96
--- /dev/null
+++ b/doc/ioctl.doc
@@ -0,0 +1,199 @@
+DMSDOS ioctl commands (outdated.... newers are missing here)
+
+*****************************************************************************
+WARNING: INFORMATION IN THIS FILE IS OUTDATED AND PARTIALLY WRONG. SEE THE
+ DMSDOS UTILITIES SOURCE CODE FOR PROGRAMMING EXAMPLES.
+
+ When I have enough time I write better documentation.....
+*****************************************************************************
+
+Example code:
+
+ #include<stdio.h>
+ #include<linux/dmsdos_fs.h>
+ #include<sys/ioctl.h>
+ #include<sys/types.h>
+ #include<sys/stat.h>
+ #include<fcntl.h>
+ #include<string.h>
+ #include<errno.h>
+
+ Dblsb dblsb;
+ int fd;
+ int ret;
+ unsigned long w[10];
+ Mdfat_entry mde;
+
+ /* open file descriptor fd */
+ fd=open(CVF_name,O_RDONLY);
+ if(fd<0)
+ { printf("No such file or directory.\n");
+ return;
+ }
+
+DMSDOS_GET_DBLSB:
+read DMSDOS version number and read CVF parameters in dblsb structure.
+This function also performs a version check, i.e. if the dmsdos driver in
+the kernel thinks the version number you supply in s_dcluster is too old,
+it "or"s the returned value with 0x0f000000 and doesn't fill in the values
+for the dblsb structure.
+
+** Since not all DMSDOS versions support all commands, this ioctl should be
+always issued first **
+
+ dblsb.s_dcluster=DMSDOS_VERSION;
+ ret=ioctl(fd,DMSDOS_GET_DBLSB,&dblsb);
+ if(ret<0)
+ { printf("This is not a DMSDOS directory.\n");
+ exit(1);
+ }
+ printf("You are running DMSDOS Version %d.%d.%d.\n\n",(ret&0xff0000)>>16,
+ (ret&0x00ff00)>>8,ret&0xff);
+ if(ret&0x0f000000)
+ { printf("This program is too old for the DMSDOS version you are running.\n");
+ exit(1);
+ }
+
+ dblsb structure see dmsdos.h.
+
+DMSDOS_READ_BITFAT:
+read bitfat entry
+w[0]=sectornr; returns 0 in w[1] if sector free
+
+ ret=ioctl(fd,DMSDOS_READ_BITFAT,w);
+ if(ret<0)error.....;
+
+DMSDOS_WRITE_BITFAT:
+write bitfat entry
+w[0]=sectornr, w[1]=new value (0=free, 1=allocated)
+
+ ret=ioctl(fd,DMSDOS_WRITE_BITFAT,w);
+ if(ret<0)error.....;
+
+DMSDOS_READ_MDFAT:
+read mdfat entry
+w[0]=clusternr; returns result in mde structure.
+
+ w[1]=&mde;
+ ret=ioctl(fd,DMSDOS_READ_MDFAT,w);
+ if(ret<0)error......;
+
+DMSDOS_WRITE_MDFAT:
+write mdfat entry
+w[0]=clusternr; mde=new mdfat entry value.
+
+ w[1]=&mde;
+ ret=ioctl(fd,DMSDOS_WRITE_MDFAT,w);
+ if(ret<0)error......;
+
+raw MDFAT entry: 32/40 bit number (hidden to the user)
+
+ dos 6.x, Win95 without drivespace 3:
+ 3322222222221111111111
+ 10987654321098765432109876543210
+ ucaaaammmm?sssssssssssssssssssss
+
+ Win95 with drivespace 3:
+ 33333333322222222221111111111
+ 9876543210987654321098765432109876543210
+ ucaaaaaammmmmm??ssssssssssssssssssssssss
+
+Mdfat_entry structure: (the dmsdos driver automatically converts it to the
+ appropriate 32 or 40 bit number)
+
+ mde.sector_minus_1= ('ssss...' bits)
+ mde.size_lo_minus_1= ('mmmmmm' bits)
+ mde.size_hi_minus_1= ('aaaaaa' bits)
+ mde.flags= ('uc' bits)
+ mde.unknown= ('?' bit(s)), are NOT written, always set to zero
+
+ u: 1=entry is used, 0=entry is free
+ c: 1=cluster is uncompressed, 0=cluster is compressed
+ a: uncompressed size minus 1 (number of sectors belonging to this cluster)
+ m: compressed size minus 1 ( " " " )
+ ?: unknown, but seem(s) to be used sometimes
+ s: sector number minus 1 (theoretical 2 GB limit)
+ The sector number is counted from the beginning of the CVF file
+ starting with sector number 0.
+
+DMSDOS_SET_COMP:
+set compression method for write access.
+
+ ioctl(fd,DMSDOS_SET_COMP,READ_ONLY); or:
+ ioctl(fd,DMSDOS_SET_COMP,UNCOMPRESSED); or:
+ ioctl(fd,DMSDOS_SET_COMP,GUESS); or:
+ ioctl(fd,DMSDOS_SET_COMP,DS_0_0); or:
+ ioctl(fd,DMSDOS_SET_COMP,DS_0_1); or:
+ ioctl(fd,DMSDOS_SET_COMP,DS_0_2); or:
+ ioctl(fd,DMSDOS_SET_COMP,JM_0_0); or:
+ ioctl(fd,DMSDOS_SET_COMP,JM_0_1); or:
+ ioctl(fd,DMSDOS_SET_COMP,SQ_0_0);
+
+DMSDOS_SET_CF:
+set compression level:
+
+ ioctl(fd,DMSDOS_SET_CF,level-1);
+
+DMSDOS_EXTRA_STATFS:
+special statfs command for dmsdos
+reads special info in w.
+
+ ioctl(fd,DMSDOS_EXTRA_STATFS,w);
+
+w[0]= free sectors in CVF.
+w[1]= used sectors.
+w[2]= maximum free unfragmented sectors (contiguous sectors)
+w[3]= free fat clusters
+w[4]= used fat clusters
+w[5]= clusters allocated in fat but free in MDFAT (filesystem error if > 0)
+w[6]= sum of sectors used by compressed clusters
+w[7]= sum of sectors used by uncompressed clusters
+w[8]= number of compressed clusters
+w[9]= number of uncompressed clusters
+
+DMSDOS_READ_BLOCK:
+read sector from CVF; returns sector contents in bstruct.data;
+
+ struct bstruct
+ { unsigned long sectornr;
+ unsigned char data[512];
+ };
+
+ bstruct.sectornr= sector number;
+
+ ioctl(fd,DMSDOS_READ_BLOCK,&bstruct);
+
+DMSDOS_WRITE_BLOCK:
+
+ analogue to read_block...
+
+DMSDOS_READ_DIRENTRY:
+DMSDOS_WRITE_DIRENTRY:
+
+ removed and unsupported...
+
+DMSDOS_READ_DFAT:
+w[0]=clusternr; returns fat entry value in w[1]; (last=-1 or 0xFFFFFFFF)
+
+ ret=ioctl(fd,DMSDOS_READ_DFAT,w);
+ if(ret<0)error......;
+
+DMSDOS_WRITE_DFAT:
+w[0]=clusternr; w[1]=new value;
+
+ ret=ioctl(fd,DMSDOS_WRITE_DFAT,w);
+ if(ret<0)error......;
+
+DMSDOS_SIMPLE_CHECK:
+performs simple filesystem check (crosslinks & allocation errors)
+w[0]=0; means do not try to repair errors
+
+returns result in w[0] (0=ok, -1=FAT error, -2=MDFAT error, -3=BITFAT error,
+ 1=out of memory, check aborted,
+ 2=FAT ok, but out of memory for MDFAT check)
+
+ ioctl(fd,DMSDOS_SIMPLE_CHECK,w);
+ if(w[0]==....)printf(.....);
+
+
+dmsdos daemon ioctls missing here....
diff --git a/doc/libdmsdos.doc b/doc/libdmsdos.doc
new file mode 100644
index 0000000..8d4ce70
--- /dev/null
+++ b/doc/libdmsdos.doc
@@ -0,0 +1,614 @@
+This is documentation for the dmsdos library. 10Sep1998
+
+
+The dmsdos library, libdmsdos.a, provides the dmsdos interface that is known
+from the kernel to a user-level process. This is acchieved by compiling the
+dmsdos code simply in a different way (well, with some rather dirty tricks).
+
+The library is intended to export the following functions:
+
+ open_cvf
+ close_cvf
+ dbl_bitfat_value
+ dbl_fat_nextcluster
+ dbl_mdfat_cluster2sector
+ dbl_mdfat_value
+ dmsdos_read_cluster
+ dmsdos_write_cluster
+ raw_bread
+ raw_brelse
+ raw_getblk
+ raw_mark_buffer_dirty
+ raw_set_uptodate
+ simple_check
+ stac_bitfat_state
+
+In fact, it exports much more, but only these listed here are guaranteed
+to be kept in future versions. Well, one thing should be important to know:
+The library does NOT export virtual sector management. It will never do.
+
+The functions are just used like in the kernel. For prototypes, see file
+dmsdos.h. How to use them see the sources. There's some example code
+how to use the library (dcread.c).
+
+The first two, open_cvf and close_cvf, are special. They are called instead
+of mounting and unmounting the CVF.
+
+How to "mount" a CVF (example code):
+
+ #include "lib_interface.h"
+ #include "dmsdos.h"
+
+ struct super_block*sb;
+ sb=open_cvf("CVF_Filename",rwflag /*0=RDONLY or 1=RDWR*/);
+ if(sb==NULL)
+ { fprintf(stderr,"open CVF failed\n");
+ exit(1);
+ }
+
+Keep the sb pointer. It must be passed to a lot of functions. It can be used
+in the same way as in the kernel. The sb pointer is used to distinguish
+between several open CVFs (in fact, in sb->s_dev the file descriptor is
+stored).
+
+After use, the CVF must be "unmounted" again:
+
+ close_cvf(sb);
+
+If you want to see more example code, look at the dcread utility source
+(file dcread.c in the src directory).
+
+Notes:
+ * The user-level functions are single-threaded. You cannot run several
+ processes on the same CVF unless all are only reading. The library
+ uses flock in order to detect such situations and just denies access
+ in that case. You can however use different CVFs in parallel, even
+ read-write, even in one program.
+ * You should use this library only to access a CVF that is currently
+ _not_ mounted. There's a high risk of destroying the CVF or even
+ crashing the system otherwise.
+ * You should not touch CVFs that are currently used by dosemu with
+ wholedisk or partition access. Again there's a high risk of destroying
+ the CVF or crashing dosemu otherwise.
+ * If you do not intend to write to the CVF, open it in read-only mode.
+ Then the CVF is not locked exclusively and other processes are allowed
+ to read the CVF at the same time.
+ * The library prints the "kernel" messages to stderr. It obeys the
+ loglevel variable. The only way to make libdmsdos quiet is to
+ direct stderr into a file or to /dev/null.
+ * The first call of open_cvf initializes the library and prints some
+ version information to stderr.
+ * open_cvf does not do a filesystem check. If you do want the same
+ simple filesystem check that dmsdos usually does when mounting, call
+ simple_check afterwards.
+ * open_cvf may open the filesystem read-only though you request
+ read-write access. This happens if write access is denied or if the
+ dmsdos subsystem detects unexpected problems with the CVF. You may
+ not like this behaviour, but it's best for long life of your CVFs :)
+ Programmers please check sb->s_flags for the MS_RDONLY flag after
+ calling open_cvf to be sure.
+ * To compile your application using the dmsdos library you should under
+ no cirumstances define the macro __KERNEL__ for the whole program.
+ It causes problems with libc6.
+
+As an idea what the dmsdos library might be good for:
+
+ * A mtools clone for CVFs, for example called dtools. (A simple program,
+ dcread, exists but its quatilty cannot be compared to mtools. Sorry.)
+ * A fsck.dmsdos. (An incomplete alpha test version exists.)
+ * A defragmentation program. (Not yet written.)
+ * For debugging dmsdos at user level (it's the same source code).
+ * An external fs for midnight commander. (A read-only interface program,
+ mcdmsdos, exists.)
+ * ... add your favourite idea here :)
+ * ... I've even received mail about whether it would be possible to
+ port libdmsdos to Win32 and use it for a Win95/98/NT driver :))
+
+Support for a shared dmsdos library
+-----------------------------------
+
+libdmsdos can be compiled as a shared library. You must edit the
+Makefile in the src directory for that purpose. Note that the default
+installation does not compile the shared library. This is intended. If
+you are not an experienced shared library hacker please be careful. It's
+easy to screw up some binaries with shared libraries :)
+
+You should not use the dmsdos shared library for now as there's currently no
+standard that ensures that future versions will be backwards compatible.
+Link statically with libdmsdos.a unless disk space or memory is very critical.
+
+WARNING: If there's a shared dmsdos library lying around, gcc defaults to
+linking *dynamically* all programs that need this library. For example, if
+libdmsdos.so is present and you recompile dmsdosfsck, the binary will be
+dynamically linked against that library. This can be very confusing and may
+not do what you want :(
+
+Built-in direct translation access mode
+---------------------------------------
+
+This feature has been added to the library for convenience. It only works
+for FAT12 or FAT16 MSDOS filesystems. It does not work, for example, for
+CVFs on a cdrom or in a FAT32 or non-FAT filesystem.
+
+The dmsdos library can access a CVF that resides in an uncompressed FAT12 or
+FAT16 MSDOS host partition even if the uncompressed host partition is not
+mounted (this was programmed in fact by cut'n'paste of some old dmsdosfs
+functions). If there are more than one CVF in that partition, the first
+valid CVF found in the root directory of the uncompressed host partition
+is used. If you want to select one of more CVFs, append a colon and the
+extension of the CVF to the filename.
+
+For example, if your uncompressed host partition is /dev/hda1 and you want
+to access the CVF DRVSPACE.001 on that partition, use "/dev/hda1:001" as
+filename when calling open_cvf. If DRVSPACE.001 is the only CVF inside
+/dev/hda1 you can even use "/dev/hda1" and it will find the CVF.
+
+This special feature allows, for example, dmsdosfsck to check a CVF in
+a partition that has not yet been mounted. This may be useful at boot time
+to check CVFs :)
+
+Standard C library functions that libdmsdos needs
+-------------------------------------------------
+
+If you want to use libdmsdos in an environment where the standard C library
+functions are not available then you must write an emulation for all these
+functions and link your code against it instead of the standard C library.
+The emulation needn't be full-featured like the C library. Look at the source
+if you are in doubt.
+
+ close (4)
+ errno
+ exit (3)
+ flock (only if compiled with -DUSE_FLOCK) (4)
+ sopen (only if compiled with -DUSE_SOPEN) (4) (8)
+ fprintf (2)
+ free (6)
+ lseek (1) (7)
+ malloc (6)
+ memcpy
+ memmove
+ memset
+ open (only if _not_ compiled with -DUSE_SOPEN) (8)
+ read (1)
+ strcat
+ strerror
+ strncat
+ strncmp
+ strncpy
+ strrchr
+ time (5)
+ vsprintf (2) (9)
+ write (1)
+
+ (1) only used in aligned 512 byte blocks
+ (2) only used for logging to stderr, no other files are used
+ (3) used for abort in case of fatal error
+ (4) locking may be left out
+ (5) a time-proportional counter would be enough, needn't be exact
+ (6) called quite often, be aware of possible memory fragmentation
+ (7) only used on 512 byte boundaries with SEEK_SET, also used to find out
+ file size with SEEK_END
+ (8) must parse filename (invent your own syntax if necessary)
+ (9) a free, portable emulation is in linux/lib/vsprint.c
+
+If possible, please avoid hacking around in dmsdos kernel code if the library
+does not work in your environment (unless it's a bug). The only files that
+should be modified due to OS or compiler differences are lib_interface.c and
+lib_interface.h. Please surround your changes with appropriate #ifdef's so
+the modified code still compiles under all those environments where it worked
+before. Please do not violate portability of the code. Hardware comes and
+goes, OSses are born and die, but portable code survives :)
+
+
+dmsdos library reference guide
+------------------------------
+
+All functions need the header files lib_interface.h and dmsdos.h. Error
+checking is not always possible. This is because some of the low-level kernel
+functions dmsdos calls when running as kernel module never fail. In user
+space, there may be failures, however. This means, libdmsdos is currently
+not 100% error-safe. As a workaround (that works under the standard C library)
+you can set errno to NO_ERROR before calling the function and afterwards
+check whether it has changed to an error value.
+
+ open_cvf:
+
+ struct super_block* open_cvf(char*filename,int rwflag)
+
+ Opens CVF described by filename. rwflag is 0 for read-only, otherwise
+ the file is opened read-write. The file is locked read-only or
+ read-write, depending on rwflag (by calling flock or sopen).
+
+ filename is interpreted in a special way if it contains a colon.
+ See chapter 'Built-in direct translation access mode' above. The colon
+ and the rest of the string after it are stripped off before the filename
+ is forwarded to the libc function open or sopen.
+
+ Return Value: Pointer to valid super_block structure (which can be
+ interpreted as a kind of file handle), NULL if failed.
+
+ Note: Keep the super_block pointer. Most dmsdos functions need it in
+ order to identify the CVF.
+
+ Note: The CVF may be opened read-only though you opened it in read-write
+ mode. This happens if the dmsdos code detects unexpected errors in the
+ CVF. Please check sb->s_flags for the MS_RDONLY bit after calling
+ open_cvf.
+
+ close_cvf:
+
+ void close_cvf(struct super_block*sb)
+
+ Closes the CVF. If necessary, the file is unlocked before.
+
+ dbl_bitfat_value:
+
+ int dbl_bitfat_value(struct super_block*sb,int sectornr,int*new)
+
+ Reads or writes bitfat value that belongs to sector sectornr.
+ *** This is a low-level function you are probably never interested in.
+ Bitfat values are CVF format specific. You do not need to know them
+ unless debugging write access :))
+
+ If new is NULL the value is read and returned.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: read access: read value. write access: undefined. A
+ negative value indicates an error.
+
+ dbl_fat_nextcluster:
+
+ int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*new)
+
+ Reads or writes the FAT entry for cluster clusternr. (You should know
+ what a FAT is, otherwise please read a good book on Dos disk access.)
+
+ If new is NULL the value is read and returned.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: read access: read value. write access: undefined.
+ -1 means the cluster is marked as last cluster in a file or an error
+ occured. Errors may also be represented by other negative results.
+
+ Note: -1 can also be used for write access in order to indicate end of
+ file mark. It is automatically converted to the right value for the FAT
+ size.
+
+ Note: if clusternr is invalid a -1 is returned. This usually makes
+ runaway FAT reading loops break as if there was an EOF in the cluster
+ chain. There's no means to find out whether a -1 is an error or an EOF
+ except making sure a valid cluster number is given.
+
+ dbl_mdfat_cluster2sector:
+
+ int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr)
+
+ This is a low-level function that reads the mdfat and extracts the
+ starting sector information from it. It may be useful for write
+ access with dmsdos_write_cluster. It returns the starting sector number
+ of cluster clusternr.
+ *** Do not use the standard formula that is used in a normal FAT
+ filesystem (multiplying the cluster number with the number of sectors
+ per cluster and adding some offsets) - that formula is not valid in a
+ compressed filesystem. Use dbl_mdfat_cluster2sector instead.
+
+ Return value: starting sector of the cluster clusternr. If negative,
+ an error occured.
+
+ Note: see dmsdos_write_cluster what this function is useful for.
+
+ dbl_mdfat_value:
+
+ int dbl_mdfat_value(struct super_block* sb,int clusternr,
+ Mdfat_entry*new,Mdfat_entry*mde)
+
+ Reads or writes the mdfat/allocmap entry that belongs to cluster
+ clusternr.
+ *** This is a low-level function you are probably never interested in.
+ Mdfat values are CVF format specific. You do not need to know them
+ unless debugging dmsdos :))
+
+ If new is NULL the value is read and stored in *mde.
+ Otherwise the actual value is replaced with that one found in *new.
+
+ Return value: A negative value indicates an error.
+ Note: consider *mde as undefined after write access.
+
+ dmsdos_read_cluster:
+
+ int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+
+ Reads cluster clusternr in clusterd. The dmsdos library handles all
+ low-level access including decompression of the data.
+
+ *** Be sure to have reserved enough memory for the data that this
+ function writes into clusterd. Usually, use a full cluster size.
+ How to determine the full cluster size see the dcread example code.
+
+ Return value: number of bytes actually read (clusters may be shorter
+ than the full size in a CVF, which is not possible in an uncompressed
+ FAT filesystem). Usually, you can ignore this unless you use write
+ access. (A cluster may even have length zero.) Just in case, if the
+ cluster is shorter than full size the unused slack is zero'd out. On
+ error, a negative value is returned.
+
+ Note: This function cannot read the root directory (cluster 0). You must
+ use low-level disk access for that (i.e. raw_bread). See also the dcread
+ example code.
+
+ dmsdos_write_cluster:
+
+ int dmsdos_write_cluster(struct super_block*sb, unsigned char* clusterd,
+ int length, int clusternr, int near_sector,
+ int ucflag)
+
+ Writes cluster clusternr from clusterd back to disk. The dmsdos
+ library handles all low-level disk access including compression.
+
+ length is the position of the last valid byte plus 1 in clusterd.
+ This is usually the value that dmsdos_read_cluster returned when you
+ read this cluster before unless you modified some bytes beyond that
+ value. The idiot-proof value is always the full cluster size.
+ *** Warning: if length is too low, the data may be truncated during
+ write access. If it is too high disk space is wasted.
+
+ ucflag is a flag that indicates whether libdmsdos should try to
+ compress the data. 0 means try to compress, 1 means write the data
+ without compression. Please do not use other values than 0 and 1
+ (they do exist but have some very special meanings).
+
+ near_sector can be used to control where libdmsdos tries to place
+ the data on disk. For good performance on large files, it should
+ be set to the start sector value of the cluster that preceeds the
+ actual cluster in the file. The start sector value can be obtained with
+ dbl_mdfat_cluster2sector on that preceeding cluster. (This way actually
+ helps avoiding low-level fragmentation _and_ file data fragmentation in
+ the CVF.)
+ *** Do not use the standard formula that is used in a normal FAT
+ filesystem (multiplying the cluster number with the number of sectors
+ per cluster and adding some offsets) - that formula is not valid in a
+ compressed filesystem.
+
+ The near_sector value is just meant as a hint. If there's not enough
+ free space on the disk around near_sector, dmsdos writes the data
+ somewhere else. If near_sector is set to zero, dmsdos uses an internal
+ way to find out a good value that avoids low-level fragmentation but
+ that is not necessarily good for performance on the file that is being
+ written. Nevertheless, 0 always works.
+
+ WARNING: If you are writing a directory cluster you should not compress
+ the data (it would hurt performance a lot). Some of the original CVF Dos
+ software versions never compress directories, so you shouldn't do
+ either (it may cause compatibility problems otherwise). You also should
+ always use full cluster size for directories on some CVF versions. In
+ order to feel safe you can use the DIR_MAY_BE_COMPRESSED and
+ DIR_MAY_BE_SHORT macros. Have a look at their definition in file
+ dmsdos.h.
+
+ Return value: A negative value indicates an error.
+
+ Note: The data in clusterd are not destroyed by this function.
+
+ raw_bread:
+
+ struct buffer_head* raw_bread(struct super_block*sb,int block)
+
+ Read a 512 byte block from the CVF. The data are read without further
+ modification. Block numbering scheme begins with zero.
+
+ Return value: pointer to a buffer_head structure that contains the 512
+ bytes of data that represent the data block. The data are in
+ buffer_head->b_data in an unsigned character array (see the dcread
+ example). On error, NULL is returned.
+
+ Note: The data are returned in dynamically allocated memory. If you
+ do not need the data any more, you must free them by calling raw_brelse
+ in the buffer_head structure pointer.
+
+ Note: You may modify the data. You must call raw_mark_buffer_dirty
+ after modification (this writes the data back to disk) and before
+ raw_brelse.
+
+ Note: If you want to overwrite the whole block, use raw_getblk instead
+ of raw_bread (this leaves out the unnecessary disk read access).
+
+ raw_brelse:
+
+ void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+
+ Release dynamic memory for the data read by raw_bread.
+
+ raw_getblk:
+
+ struct buffer_head* raw_getblk(struct super_block*sb,int block)
+
+ Like raw_bread, but leaves out the actual disk access. This is intended
+ to leave out unnecessary disk read access if you want to overwrite the
+ whole block. Consider the data as undefined. See raw_bread.
+
+ Note: raw_getblk should be followed by a call of raw_set_uptodate
+ for compatibility. See there.
+
+ raw_mark_buffer_dirty:
+
+ void raw_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val)
+
+ Write data back to disk after they have been modified.
+
+ dirty_val must be always 1 (due to a misunderstanding/bug in dmsdos).
+
+ Note: This function does not indicate an error. This is bad, but it is
+ because the Linux kernel interface also does not return error codes with
+ this function. As a hack (that works with libc) you can set errno to
+ NO_ERROR and check it afterwards, but note that this will not work in
+ the dmsdos kernel driver. Usually, there cannot be an error during that
+ kind of write access unless the disk has bad sectors.
+
+ raw_set_uptodate:
+
+ void raw_set_uptodate(struct super_block*sb,struct buffer_head*bh,int v)
+
+ No longer used (does nothing in libdmsdos).
+
+ For compatibility with the kernel driver, this should be called with
+ v=1 after raw_getblk on the buffer_head pointer that raw_getblk returned
+ (unless it's NULL due to failure).
+
+ simple_check:
+
+ int simple_check(struct super_block*sb,int repair)
+
+ Check some internal filesystem tables for consistency.
+
+ If repair is 1 libdmsdos tries to correct errors (the CVF must have been
+ opened read-write in that case). If it is 0 the tables are just checked.
+
+ Return value: a negative value indicates an error, i.e. either the
+ tables are inconsistent or the errors, if any, could not be repaired.
+
+ Note: It is strongly recommended to run simple_check immediately after
+ opening the CVF if you want to write to it. Do not write to a CVF if
+ simple_check fails on it (you may detroy some files in the CVF). As
+ CVFs are known to be very prone to so-called bitfat errors, you probably
+ want to give the program that you are writing a kind of "expert option"
+ that lets simple_check run with repair=1. The same warnings apply as to
+ the dmsdos mount option bitfaterrs:repair :))
+
+ stac_bitfat_state:
+
+ int stac_bitfat_state(struct super_block*sb,int new_state)
+
+ Stacker has a kind of clean/dirty flag that indicates whether the CVF
+ was correctly unmounted. This function can be used to manipulate it.
+ *** This is a low-level function you should not use unless you are a
+ dmsdos and Stacker expert. See the dmsdos source code.
+
+ Note: Do not call this function on non-Stacker CVFs.
+
+
+Programming notes
+-----------------
+
+In a normal libdmsdos application, you open the CVF with open_cvf. If you
+intend to write to the CVF later, you should call simple_check after that
+and refuse write access if an error is found.
+
+Then you use dmsdos_read_cluster and dmsdos_write_cluster to access the
+CVF. Only for the root directory you need raw_bread, raw_mark_buffer_dirty
+and raw_brelse. For following files you need to read the FAT with
+dbl_fat_nextcluster.
+
+You write to the FAT with dbl_fat_nextcluster in order to truncate files or
+append data to them, or even to delete files and directories and to create
+new ones - all like in a usual FAT filesystem.
+
+If you are done, don't forget to close the CVF with close_cvf.
+
+Here is a simple example that illustrates the usage of all important
+libdmsdos functions. You should not compile and execute this example as
+it's not always correct C syntax. Things like error handling have been left
+out to simplify the code (which is not good). Furthermore, the program just
+pokes around in the CVF which would be bad for your data :)
+
+
+ #include "lib_interface.h"
+ #include "dmsdos.h"
+
+ struct super_block*sb;
+ struct Dblsb*dblsb;
+
+ /* open the CVF read-write */
+ sb=open_cvf("dblspace.001",1);
+ if(sb==NULL)...error...
+
+ /* check the CVF */
+ if(simple_check(sb,0)<0)...error...
+
+ /* read and display cluster 2 as hex numbers */
+ int i;
+ int size;
+ int full_size;
+ unsigned char* clusterd;
+
+ /* determine full size */
+ dblsb=MSDOS_SB(sb)->private_data;
+ full_size=dblsp->s_sectperclust*512;
+
+ clusterd=malloc(full_size);
+ if(clusterd==NULL)...error...
+
+ size=dmsdos_read_cluster(sb,clusterd,2);
+ if(size<0)...error...
+ for(i=0;i<size;++i) printf("%x ",clusterd[i]);
+ printf("\n");
+
+ /* modify one byte at position 4234 and write cluster 2 back */
+ int pos;
+
+ pos=4234;
+ clusterd[4234]=123;
+ if(size<=pos)size=pos+1; /* adapt size */
+
+ if(dmsdos_write_cluster(sb,clusterd,size,2,0,0)<0)...error...
+
+ free(clusterd); /* free memory that is no longer needed */
+
+ /* determine which cluster follows cluster 2 in fat */
+ int fatval;
+
+ fatval=dbl_fat_nextcluster(sb,2,NULL);
+ if(fatval==0) printf("cluster 2 is unused\n");
+ else if(fatval==-1) printf("cluster 2 is marked as EOF\n");
+ else printf("cluster 2 is followed by cluster %d in FAT\n");
+
+ /* mark it as EOF */
+ int new;
+
+ new=-1;
+ dbl_fat_nextcluster(sb,2,&new);
+
+ /* read root directory */
+ int i;
+ unsigned char*data;
+ int number;
+ struct buffer_head*bh;
+
+ dblsb=MSDOS_SB(sb)->private_data;
+ number=dblsb->s_rootdirentries*32/512;
+ startblock=dblsb->s_rootdir;
+
+ data=malloc(number*512);
+ if(data==NULL)...error...
+
+ for(i=0;i<number;++i)
+ { bh=raw_bread(sb,startblock+i);
+ if(bh==NULL)...error...
+ memcpy(data+i*512,bh->b_data,512);
+ raw_brelse(sb,bh);
+ }
+ /* complete root directory is now in data */
+
+ /* modify root directory */
+ ...modify data where you like...
+
+ /* write it back completely */
+ for(i=0;i<number;++i)
+ { bh=raw_getblk(sb,startblock+i); /* or raw_bread(...) */
+ if(bh==NULL)...error...
+ raw_set_uptodate(sb,bh,1); /* leave out if raw_bread used above */
+ memcpy(bh->data,data+i*512,512);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+
+ free(data); /* free memory that is no longer needed */
+
+ /* we are done */
+ close_cvf(sb);
+
+
+For a piece of code that actually compiles and runs and does something
+eventually useful and ... does not kill your data :-)) see the dcread
+example program (file dcread.c).
diff --git a/doc/messages.doc b/doc/messages.doc
new file mode 100644
index 0000000..71c6294
--- /dev/null
+++ b/doc/messages.doc
@@ -0,0 +1,871 @@
+dmsdos driver: kernel messages in alphabetical order.
+
+*** Some messages need a better explanation. Under construction :) ***
+
+Classes: I: Information.
+ E: Error in filesystem, check it under dos (incl. the surface test).
+ O: Other problem, not a filesystem error.
+ B: Bug, please send a bug report.
+ P: Panic message. Extremely severe filesystem, hardware or dmsdos
+ error. The process that actually called dmsdos when the problem
+ occured has been "frozen" in order to prevent data corruption.
+ Though your system continues to run, you should reboot immediately
+ and check the compressed filesystems. You may have to press the
+ reset button in order to reboot.
+ ?: Unknown. (Look at the source code.)
+
+------------------------------------------------------------------------------
+
+DMSDOS: adapting free sectors count
+ I: Stacker sector count is corrected.
+
+DMSDOS: BB_ClustCnt=0x%x impossible (FAT32?)
+ E/O?: The filesystem is in an unsupported format, damaged or otherwise
+ strange. Dmsdos refuses to mount it. Please verify whether it is FAT32
+ under Dos/Win95. Currently compressed FAT32 is not supported.
+ (Compressed FAT32 has never been reported to exist so far.)
+
+DMSDOS: bitfat cache:
+ I: Debug message. Ignore.
+
+DMSDOS: BITFAT mismatches MDFAT, repairing...
+DMSDOS: BITFAT mismatches MDFAT (sector %d is %d and should be %d)
+DMSDOS: BITFAT mismatches MDFAT (sector %d)
+ E: There's an error in the allocation table (BITFAT) of your compressed
+ partition - next time you boot dos you should check it. WARNING:
+ Writing to an errorneous filesystem may cause further damage.
+ Optionally (with mount option bitfaterrs=repair)
+ the driver tries to repair the problem by recalculating the BITFAT.
+
+DMSDOS: bmap called, unsupported!
+ B: Shouldn't happen.
+
+DMSDOS: cannot write dirty cluster %d on dev 0x%x, trying again later
+ E/O: Very likely the filesystem is full. There should be another message
+ describing more details. The driver keeps the cluster in memory. This
+ is a very critical condition as you may lose the data in that cluster.
+ The driver usually tries 5 times to write the data again before
+ throwing them away and setting the filesystem to read-only mode. If
+ the filesystem is really full you still have a chance to free up some
+ space now.
+
+DMSDOS: ccache contents:
+DMSDOS: ccache statistics:
+ I: Debug messages. Ignore.
+
+DMSDOS: ch_dirty(_locked): READ-ONLY filesystem
+ O: When the filesystem suddenly is set read-only (e.g. because of a
+ problem) some kernel functions seem not to obey the RO flag. In this
+ case, the dmsdos driver itself rejects the command in order to
+ avoid trashing the filesystem.
+
+DMSDOS: ch_read: no memory!
+ O: System memory is even too low for read access. See below for hints
+ about 'no memory' messages.
+
+DMSDOS: ch_read: read_cluster failed
+ E: The cluster could not be read from disk. There should be another
+ message describing more details.
+
+DMSDOS: check_free_sectors: wrong count %d corrected to %d
+ B: Looks like a minor bug. As the message tells, the problem is corrected.
+
+DMSDOS: clear_list_dev: Uhh, sb==NULL ...
+ B: Shouldn't happen.
+
+DMSDOS: cluster %d read error
+ E: There should be another message describing the error more exactly.
+
+DMSDOS: compression method not recognized.
+ E: Check filesystem. May also be caused by running incompatible/unsupported
+ or not yet fully supported compression software under dos. In detail, the
+ driver found a compressed cluster but didn't recognize the compression
+ header. This may even be suspicious to be a dmsdos bug.
+
+DMSDOS: could not find MDR signature or found more than one, mdrc=%d (ignored)
+ I: Debug message. It may indicate a problem with the CVF, but as this
+ signature is almost completely unimportant, you can most likely ignore
+ it.
+
+DMSDOS: could not guess compression method for CVF
+ I: There's no compressed file in your compressed filesystem that dmsdos
+ could analyse to determine the compression method. To avoid problems,
+ dmsdos has disabled compression when writing to this CVF. Specify a
+ compression method explicitely when mounting (comp=xxx option) or write
+ something (more than 1KB) to the compressed partition under dos.
+
+DMSDOS: counted free sectors=%d
+ I: The message tells how many free sectors have been counted in the CVF.
+
+DMSDOS: CVF almost full or highly fragmented at MDFAT level.
+ I: The driver warns that further write access might become dangerous.
+
+ This indicates a really bad situation. The algorithm to find free space
+ in a CVF performs three trials to find an appropriate hole for new data.
+ All of them use different priorities to avoid MDFAT level fragmentation.
+ But this time it did not succeed until its last trial. That means
+ additional MDFAT level fragmentation cannot be avoided any longer and the
+ more write access you perform the more likely the last trial is to fail.
+
+ Watch the free space on the compressed partition. If there seems to be
+ enough, try to defragment the partition as described in chapter
+ 'defragment procedures' in file dmsdos.doc.
+
+ This message has KERN_EMERG priority to ensure you can see it on the
+ screen.
+
+DMSDOS: CVF end padding %d sectors.
+ I: Size of STACVOL is stored in its header. If real file is
+ longer than this information, user is informed about that
+ unused sectors. It can happens when you copy stacvol
+ to other host drive.
+
+DMSDOS CVF-FAT extension version %d.%d.%d
+ I: Tells version number and compile-time options when loading dmsdos.
+ The term 'read-only' means that you are using a read-only dmsdos
+ version (write access has been turned off during dmsdos configuration).
+ It does *not* tell that the filesystem is mounted read-only or
+ read-write.
+
+DMSDOS: CVF full.
+DMSDOS: CVF full (cannot even allocate fragmented space)
+DMSDOS: CVF full or too fragmented at MDFAT level.
+ O/E: The message tells it all. The last write access may have failed and
+ data may have been lost. As you cannot tell how well the data will
+ compress it is dangerous to fill a CVF up to the last byte. There might
+ even be some errors in the filesystem now (but that's very unlikely).
+
+ These messages have KERN_EMERG priority. They indicate that something
+ dangerous has happened. The next time you boot dos you'd better check
+ the filesystem. And, of course, free up some space.
+
+DMSDOS: CVF has bitfat mismatches, ignored.
+DMSDOS: CVF has bitfat mismatches, setting to read-only.
+DMSDOS: CVF has serious errors or compatibility problems, setting to read-only.
+ I: The driver has detected an error in the filesystem and has either
+ ignored the error or set the filesystem to read-only mode. There should
+ be another message describing the error. In rare cases, the error may
+ be caused by compatibility problems.
+
+DMSDOS: CVF has FAT32 signature, not mounted. Please report this.
+ O: Compressed FAT32 is unsupported. I've never seen it, but I may start
+ to write support for it if someone reports that a strange M$ product
+ really creates compressed FAT32 filesystems. I don't believe so.
+
+DMSDOS: CVF is in doublespace format (version 1).
+DMSDOS: CVF is in drivespace 3 format.
+DMSDOS: CVF is in drivespace format (version 2).
+DMSDOS: CVF is in stacker 3 format.
+DMSDOS: CVF is in stacker 4 format.
+ I: The driver reports what it has detected.
+
+DMSDOS: CVF is in unknown (new?) format, please report.
+ I: The driver didn't recognize the CVF format after verifying that it is
+ indeed a CVF. Watch for further messages. You'd better not write to
+ the partition :)
+
+DMSDOS: CVF is shorter about %d sectors. Mounting READ ONLY.
+ E: Error in stacker filesystem. Check under dos.
+ Real length of STACVOL is smaller, than that stored in stacvol
+ header. Stacvol is probably corrupted.
+
+DMSDOS: CVF on device 0x%x unmounted.
+ I: The message tells it all.
+
+DMSDOS: CVF set to read-only.
+ I: The message tells it all.
+
+DMSDOS: daemon is lying about its pid
+ O/B: Either someone is trying to compromise system security by misusing
+ dmsdos ioctls (without success) or the external daemon has a bug.
+
+DMSDOS: daemon_write_cluster: ch==NULL
+ B: Shouldn't happen.
+
+DMSDOS: Data are not SQ compressed
+ E: Error in drivespace 3 filesystem (invalid data?), check under Win95.
+
+DMSDOS: dataend corrected due to MDR signature old=%d new=%d
+ I: Due to a problem with the loopback block device - it only gives size
+ information in units of 1K blocks and not in 512 byte sectors - dmsdos
+ cannot determine the exact end of the CVF. So, it tries to guess where
+ the real end of the compressed partition is. This guess is verified by
+ searching the MDR signature, which is typically located in the last
+ sector of the CVF. If the driver has guessed the wrong end, this is
+ automatically corrected and this message is logged. In theory, maximum
+ error can be 1 sector.
+
+DMSDOS: dbl_bitfat_value: version not found?? cannot happen
+DMSDOS: dbl_mdfat_value: unknown version?? This is a bug.
+ B: Shouldn't happen.
+
+DMSDOS: dbl_replace_existing_cluster: checking old fraglist: first fragment wrong in cluster %d
+ E: Filesystem error in drivespace 3 volume. Check under Win95.
+
+DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem
+ B: Uhh. Cut'n'paste your logfile and mail it to me. This is a serious bug.
+
+DMSDOS: dblspace_fat_access: READ-ONLY filesystem
+ O: When the filesystem suddenly is set read-only (e.g. because of a
+ problem) some kernel functions seem not to obey the RO flag. In this
+ case, the dmsdos driver itself rejects the command in order to
+ avoid trashing the filesystem. There's notably a problem with the
+ FAT driver, which seems to poke around in the FAT randomly after having
+ just set the filesystem to read-only mode by a filesystem panic (argh).
+ The reason is unknown (well, it doesn't seem to be really in the FAT
+ driver). To be safe, dmsdos just doesn't allow something to change the
+ FAT in that case.
+
+DMSDOS: dblspace_mark_buffer_dirty: READ-ONLY filesystem
+ O: When the filesystem suddenly is set read-only (e.g. because of a
+ problem) some kernel functions seem not to obey the RO flag. In this
+ case, the dmsdos driver itself rejects the command in order to
+ avoid trashing the filesystem.
+
+DMSDOS: decompression of cluster %d in CVF failed.
+ E/B: There's an error in the compressed data (there should be another
+ message describing what exactly failed). The next time you boot dos you
+ should check it (including the surface test since only this test finds
+ compression errors). If it is okay under dos/win95 but still fails
+ under dmsdos, *please* send a bug report (best with raw cluster data
+ extracted by using duitl).
+
+DMSDOS: dec_sq: submethod not tested - raw read
+ ?: Maybe a drivespace 3 problem. Check under Win95.
+
+DMSDOS: delete_cache_cluster: get_ch returned NULL
+ ?: Shouldn't happen. This message means that the cluster couldn't be
+ locked during deletion. So it's deleted without locking. So what.
+
+DMSDOS: Deleted clusters found, removing...
+ I: Cleaning up stacker filesystem.
+
+DMSDOS: dfat cache:
+ I: Debug message. Ignore.
+
+DMSDOS: dirty cluster %d on dev 0x%x removed, data are lost
+ O/E: The driver is giving up that cluster. Writing has failed more than
+ 5 times. YOU HAVE BEEN WARNED 5 TIMES! In order to prevent a system
+ lockup caused by endless loops the driver has thrown the data away.
+ THE DATA ARE DEFINITIVELY LOST NOW. Very likely the filesystem is
+ damaged now and a part of a directory is also lost.
+ The driver sets the filesystem to read-only mode (printing it in the
+ next message). This is a really bad situation. You'd better boot Dos
+ and have the CVF maintainance software repair what can be repared.
+ If the CVF repair tool permits it, save all lost clusters - they are
+ likely to contain some of the lost files.
+
+DMSDOS: Dirty virtual sector cannot be written - FILESYSTEM DAMAGE POSSIBLE! Trying to delay write.
+ O: THE FILESYSTEM IS FULL! It's even so full that some clusters cannot
+ be written back to the disk (they're kept in memory instead). Free up
+ some space IMMEDIATELY! Otherwise your filesystem will be destroyed
+ on unmount. After some warnings (the number depends on your cache size)
+ the driver will give up and stop the system with a panic.
+
+DMSDOS: dos max_cluster=%d too large, cutting to %d.
+ E?:Seems to be a filesystem error. Verify that dmsdos recognized the FAT
+ bit size correctly. If it hasn't, this is a bug.
+
+DMSDOS: error in DS-0-x compressed data.
+ E: There's an error in the filesystem. The next time you boot dos you
+ should check it (including the surface test).
+
+DMSDOS: error in JM-0-x compressed data.
+ E: There's an error in the filesystem. The next time you boot dos you
+ should check it (including the surface test).
+
+DMSDOS: Error while reading an mmap file %d <> %d
+ E: There should be another message describing the error more exactly.
+
+DMSDOS: evaluate_option: loglevel set to 0x%lx.
+DMSDOS: evaluate_option: speedup set to 0x%lx.
+ I: The messages tell it all.
+
+DMSDOS: exit_daemon: counter<0 ???
+ B: Shouldn't happen.
+
+DMSDOS: FAT bit size not recognized, guessed %d bit
+ I: The driver did not find a valid FAT size entry in the CVF super block
+ and the emulated boot block, so it guessed the FAT size according to the
+ CVF size. This may indicate a minor filesystem error or incompatibility.
+
+ Warning: There's a small chance to guess the wrong FAT bit size, so be
+ careful.
+
+ Note: This message may appear on all stacker filesystems since they
+ don't seem to have a FATxx signature in the bootblock.
+
+DMSDOS: FAT crosslink or loop in CVF detected (cluster %d), giving up.
+ E: There's a FAT level error in your compressed filesystem. This is
+ a serious error you should immediately repair by running dos scandisk.
+
+ Note: Verify with dutil that dmsdos has recognized the right FAT
+ bit size. If it hasn't, this is a bug.
+
+DMSDOS: FAT size does not match cluster count. Mounting READ ONLY.
+ E: Error in stacker filesystem.
+
+DMSDOS: file_mmap_nopage: no memory!
+ O: Shouldn't happen.
+
+DMSDOS: file_read: inode = NULL, rejected.
+DMSDOS: file_read: mode = %07o, rejected.
+ B: Maybe a bug in the FAT driver or the rest of the kernel.
+
+DMSDOS: file_readx: FAT mismatches file size for ino=%ld
+ E/B: FAT error or bug.
+
+DMSDOS: filesystem on dev 0x%x probably damaged, set to READ-ONLY mode
+ E: The message tells it all. You'd better boot Dos and have your CVF
+ maintainance software repair the CVF.
+
+DMSDOS: file_write: READ-ONLY filesystem
+ O: When the filesystem suddenly is set read-only (e.g. because of a
+ problem) some kernel functions seem not to obey the RO flag. In this
+ case, the dmsdos driver itself rejects the command in order to
+ avoid trashing the filesystem.
+
+DMSDOS: find_free_bitfat: free sectors=%d, cannot believe this. Counting...
+ B: Something is wrong with the free sector count. The driver tries to
+ correct this by counting the free sectors again. This is a minor bug.
+
+DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!
+ B: Cannot happen. (Uh, oh... happened in dmsdos <= 0.6.3. Don't use them.)
+
+DMSDOS: fraglist!=mde cluster %d sector %d!=%ld or count %d!=%d
+ E: Error in drivespace 3 filesystem. Check under Win95.
+
+DMSDOS: free_ccache_dev: oh oh, freeing busy cluster...
+ B: Bug in cluster cache code. Expect a crash or hang soon. Please report.
+
+DMSDOS: free_cluster_sectors: error in fragmentation list in cluster %d
+DMSDOS: free_cluster_sectors: first fragment wrong in cluster %d
+DMSDOS: free_cluster_sectors: fragmentation list unreadable in cluster %d
+ E: Error in drivespace 3 filesystem. Check under Win95.
+
+DMSDOS: free sectors=%d
+ I: The number of free sectors is displayed.
+
+DMSDOS: free sector finding statistics:
+ I: Debug message, ignore.
+
+DMSDOS: get_ch: actual looks modified ARGHHH, retrying
+DMSDOS: get_ch: free looks modified ARGHHH, retrying
+ I: Debug message. Ignore. (This messages may be important to verify that
+ new SMP safe cluster locking works correctly.)
+
+DMSDOS: get_ch: free->c_count!=0
+ B: Shouldn't happen. Indicates a bug (probably leak) in cluster caching
+ code.
+
+DMSDOS: get_ch: max_retries reached, breaking loop. This may be a bug.
+ B?: Shouldn't happen. Indicates that the driver broke an endless loop
+ to prevent a complete system hang. If you haven't run horribly out
+ of memory and you aren't loading the system to death this is an
+ extremely unlikely situation. Very suspicious to be a bug.
+
+DMSDOS: get_ch: oldest->c_count!=0
+ B: Shouldn't happen. Indicates a bug (probably leak) in cluster caching
+ code.
+
+DMSDOS: get_cluster failed (FAT problem ?)
+ O/E: The FAT driver returned an error. Maybe a problem with the FAT.
+
+DMSDOS: giving up after %d errors. There may be more errors.
+ I/E: The filesystem check tries to continue after the first error was
+ detected, but it definitely stops after 20 errors in order to prevent
+ your syslog from becoming larger than the errorneous filesystem.....
+
+DMSDOS: illegal dfat access (cluster=%d max_cluster2=%d)
+ ?: Bug or filesystem error.
+
+DMSDOS: illegal fragcount in cluster %d
+ E: Error in drivespace 3 filesystem. Check it under Win95.
+
+DMSDOS: illegal mdfat access (cluster=%d max_cluster2=%d)
+ ?: Bug or filesystem error.
+
+DMSDOS: illegal virtual sector %d, can't map to real sector
+ B: Problem with virtual sector mapping.
+
+DMSDOS: Inconsistent first data sector number. Mounting READ ONLY.
+DMSDOS: Inconsistent sector length
+ E: Error in stacker filesystem.
+
+DMSDOS: init_daemon: daemon already present
+ I: Debug message, ignore.
+
+DMSDOS: Interesting MDFAT non-lin subalocation (cluster %d)
+ I: Will be disabled, because it is normal for stacker 4.
+
+DMSDOS: ioctl: D_READ: no memory!
+ O: Lack of system memory. Can be ignored. (*)[see below]
+
+DMSDOS: ioctl: D_READ: read_cluster failed!
+ E: Seems to be an error in your filesystem (the dmsdos daemon tried to
+ read some data but read access failed). There should be at least one
+ preceeding message that describes the error more exactly.
+
+DMSDOS: ioctl: D_WRITE: no memory!
+ O: Lack of system memory. Can be ignored. (*)[see below]
+
+DMSDOS: ioctl: loglevel set to 0x%lx.
+ I: The message tells it all.
+
+DMSDOS: ioctl: read_cluster: no memory!
+ O: The driver could not read a cluster because of lack of memory. Don't run
+ so much applications at the same time, increase your swap space or add
+ memory to your machine. (Shouldn't happen.) (*)[see below]
+
+DMSDOS: ioctl: speedup set to 0x%lx.
+ I: The message tells it all.
+
+DMSDOS: killing internal daemon...
+ I: Debug message. Ignore.
+
+DMSDOS: list statistics:
+ I: Debug message. Ignore.
+
+DMSDOS: lock_ch: count=0! This is a bug.
+ P: Serious bug in cluster caching code. Please report. The system might
+ even hang completely now.
+
+DMSDOS: log_ccache_statistics: cannot happen.
+DMSDOS: log_list_statistics: cannot happen.
+ B: Bug, please report.
+
+DMSDOS: MDFAT bad allocation (cluster %d)
+ E: Error in stacker filesystem.
+
+DMSDOS: mdfat cache:
+ I: Debug message, ignore.
+
+DMSDOS: MDFAT crosslink in CVF detected (cluster %d)
+DMSDOS: MDFAT crosslink detected (cluster %d)
+ E: There's a MDFAT level crosslink in your compressed partition. This is a
+ serious error you should immediately repair by running dos scandisk.
+
+DMSDOS: MDFAT entry invalid (cluster %d, sect %d)
+DMSDOS: MDFAT entry invalid in CVF (cluster %d)
+DMSDOS: MDFAT entry invalid in CVF (fragmented cluster %d fragpnt %d)
+ E: There's a problem with your compressed partition. Dmsdos ignores
+ invalid entries if they are assigned to a file (the file will be
+ unreadable).
+
+DMSDOS: MDFAT-level dead sectors found in CVF (cluster %d)
+DMSDOS: MDFAT-level dead sectors found, removing...
+ E/I: The driver has found sectors in the filesystem that belong to a
+ non-existing cluster. Depending on the repair flag, it tries to
+ repair the problem.
+
+DMSDOS: MDR signature found at sector %d
+ I: Debug message. Usually shows the number of the last sector in the CVF.
+
+DMSDOS: MDR test breaks at i=%d
+ I: Debug message that is produced during CVF size test. There's very
+ likely also a message 'access beyond end of device' logged just before
+ this message. It's part of the MDR test to provoke an access beyond
+ the device end. You can safely ignore both messages.
+
+DMSDOS: mount_dblspace: out of memory
+ O: Shouldn't happen.
+
+DMSDOS: mounting CVF on device 0x%x %s...
+ I: The message tells it all.
+
+DMSDOS: mount_stacker: out of memory
+ O: Shouldn't happen.
+
+DMSDOS: MOVEBACK ioctl has gone
+ O: Then don't use it.
+
+DMSDOS: MSDBL/MSDSP signature not found, CVF skipped
+ E: This file doesn't seem to be really a CVF. The driver refuses, for
+ example, to mount your phone directory that has been renamed to
+ something like dblspace.001. Check the compressed filesystem under
+ dos (if dos refuses to boot because it runs into a similar problem,
+ use a dos boot disk and hold down CTRL and F8 during boot process).
+
+DMSDOS: no memory for decompression!
+ O: The driver could not decompress a cluster because of lack of memory.
+ Don't run so much applications at the same time, increase your swap space
+ or add memory to your machine. (Shouldn't happen.) (*)[see below]
+
+DMSDOS: page-aligned memory returned by kmalloc - please disable XMALLOC
+ B: The xmalloc allocation mechanism is broken. This may be caused by
+ using a very new kernel. You can still use dmsdos, but you are strongly
+ encouraged to disable xmalloc: rerun the dmsdos configuration, disable
+ advanced memory management and recompile. Please send also a problem
+ report so I can start looking for what broke xmalloc in your kernel.
+
+DMSDOS: part 1 of filesystem check failed, aborting.
+DMSDOS: part 2 of filesystem check failed, aborting.
+ I: The message tells it all.
+
+DMSDOS: read BITFAT state error
+ E: Problem with stacker filesystem. Probably low level host drive IO
+ error.
+
+DMSDOS: read_cluster: illegal cvf version flag!
+ B: Bug, please report.
+
+DMSDOS: read_cluster: mdfat sectors > sectperclust, cutting
+ E: There's an error in the filesystem. The next time you boot dos you
+ should check it (including the surface test). (This indicates a
+ corrupted MDFAT, may be suspicious to be a dmsdos write access bug.)
+
+DMSDOS: read_file bug: f_pos not cluster-aligned
+ B: ???
+
+DMSDOS: read_fragments: cluster does not look fragmented!
+DMSDOS: read_fragments failed!
+DMSDOS: read_fragments: safety_counter exceeds membytes!
+ E: Error in drivespace 3 filesystem. Check under Win95.
+
+DMSDOS: read_fragments: size limit reached.
+ I: Debug message. Ignore.
+
+DMSDOS: read_the_page: no memory!
+ O: Shouldn't happen.
+
+DMSDOS: read_virtual_sector: no memory!
+DMSDOS: read_virtual_sector: read_cluster failed!
+ E/I: Problem with virtual sector handling.
+
+DMSDOS: READ/WRITE DIRENTRY ioctl has gone
+DMSDOS: RECOMPRESS ioctl has gone
+ O: So don't use them.
+
+DMSDOS: sd4_comp: Compression ends with mismash
+DMSDOS: sd4_comp: Hufman code leakage in table 1
+DMSDOS: sd4_comp: Hufman code leakage in table 2
+DMSDOS: sd4_huffman: Problems with number of bits
+ I: Huffman code length of some character is longer
+ than 15 bits in STAC4 compression. Code will be recomputed
+ with little worse compression ratio.
+ Message may be disabled in future.
+
+DMSDOS: sd4_decomp: no memory!
+ O: Data cannot be decompressed, memory is low. (See below for 'no memory'
+ messages.)
+
+DMSDOS: sd4_decomp: Magic = %X => error!
+DMSDOS: sd4_decomp: Table 2 consistency check !!!!
+DMSDOS: sd4_decomp: Table 1 consistency check !!!!
+DMSDOS: sd4_decomp: Table 1 error
+DMSDOS: sd4_decomp: Under !!!
+DMSDOS: sd4_decomp: Over !!!!
+DMSDOS: sd4_decomp: End read %X and should be %X
+DMSDOS: sd4_decomp: Error end token %X
+ E: Data cannot be decompressed.
+ Filesystem probably damaged. Run DOS stac/check.
+
+DMSDOS: SETMAXCLUSTER ioctl has gone.
+ O: This ioctl command has turned out to be too dangerous. Use the native
+ CVF maintainance tools that came with your CVF package under Dos/Win95.
+
+DMSDOS: set_maxcluster %d refused: cluster %d in use
+ O: You tried to specify a cluster limit that is too low via the
+ 'setmaxcluster' command of dutil. This message also displays the
+ currently lowest possible value ('cluster %d in use'). Your command has
+ been skipped.
+
+DMSDOS: set_maxcluster refused - CVF is not in doublespace or drivespace<=2 format.
+ O: The setmaxcluster command is intended as workaround for a bug in
+ dos scandisk. You can only use it on CVFs that have been created by
+ a doublespace version that has this bug.
+
+DMSDOS: strange version flag %d, assuming 0.
+ I/E: The version flag in the CVF header contains garbage. Either it's
+ destroyed or you have found a new doublespace/drivespace version. In
+ the latter case please let me know.
+
+DMSDOS: simple_check aborted (no memory)
+ O: Lack of kernel memory caused the dmsdos driver to abort the filesystem
+ check. Shouldn't happen. (*)[see below]
+
+DMSDOS: simple_check: BITFAT abnormal state
+DMSDOS: simple_check: BITFAT mounted/dirty
+DMSDOS: simple_check: BITFAT state error
+ IE: The BITFAT/allocation map of the stacker filesystem is marked as
+ 'not up-to-date'. This may have been caused by a dos crash or reset.
+ The filesystem can only be used read-only. You can repair it by
+ booting DOS (stacker will show its "updating allocation map"
+ message) or use dmsdos' mount option 'bitfaterrs=repair' or use dutil.
+
+DMSDOS: simple_check: read BITFAT sumary error
+ E: Error in stacker filesystem.
+
+DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)
+ O: Lack of kernel memory caused the dmsdos driver to skip the MDFAT and
+ BITFAT test. Since this test may require up to 1 MB kernel memory
+ (depending on the partition size), this message may occur occasionally.
+ You needn't be concerned about this. (*)[see below]
+
+DMSDOS: sq_comp: ERROR: Processed only %d bytes !!!!!!
+DMSDOS: sq_comp: Huffman code leakage in table 1
+DMSDOS: sq_comp: Huffman code leakage in table 2
+DMSDOS: sq_comp: Huffman code leakage in table 3
+DMSDOS: sq_huffman: Problems with number of bits
+ I: SQ compression problem. Data will be compressed again with lower
+ compression ratio or written without compression.
+
+DMSDOS: sq_comp: Not enough memory
+ O: The data written uncompressed due to lack of memory for compression.
+
+DMSDOS: SQ-0-0 decompression failed.
+DMSDOS: sq_dec: huff BAD last token %x
+DMSDOS: sq_dec: huff count_1 too big
+DMSDOS: sq_dec: huff count_2 too big
+DMSDOS: sq_dec: huff error in char and len table
+DMSDOS: sq_dec: huff error in helper table
+DMSDOS: sq_dec: huff error in offset table
+DMSDOS: sq_dec: huff offset OVER
+DMSDOS: sq_dec: huff offset UNDER
+DMSDOS: sq_dec: out of memory!
+DMSDOS: sq_dec: submethod not tested - fixed huffman
+DMSDOS: sq_dec: unknown submethod - 3
+ E/B: Error in SQ compressed data (drivespace 3). Check under Win95.
+ I've been reported about SQ decompression problems when the files
+ even decompress well under Win95. There seems to be a problem
+ somewhere in the SQ decompression code, but I'm unable to fix it.
+
+DMSDOS: stac3_decomp: char repeat overrun!
+DMSDOS: stac3_decomp: end token 0x%02X
+DMSDOS: stac3_decomp: Illegal back pointer length 0x%x at pos 0x%x->0x%x
+DMSDOS: stac3_decomp: Multi rep overrun 0x%x at pos 0x%x->0x%x
+DMSDOS: stac3_decomp: ReadNC error!
+DMSDOS: stac3_decomp: Unknown token %d on pos 0x%X->0x%X
+DMSDOS: stac3_decomp: xor sum error!
+ E: Looks like your stacker filesystem is corrupt. The data of a file
+ couldn't be decompressed.
+
+DMSDOS: stac3_decomp: Multi rep:(%dx %d)
+DMSDOS: stac3_decomp: Rep:(%dx)
+ I: Debug messages. Ignore.
+
+DMSDOS: stac_cwalk_init: bad bytes_in_cluster %d
+DMSDOS: stac_cwalk_init: count = %d < 0 in long subalocated
+DMSDOS: stac_cwalk_init: count = %d < 0 in short subalocated
+DMSDOS: stac_cwalk_init: fragment signature not found cluster=%d
+DMSDOS: stac_cwalk_init: sector count mismash fragmented cluster=%d!
+DMSDOS: stac_cwalk_init: suballocation signature not found cluster=%d
+DMSDOS: stac_cwalk_init: suballocation error 1, cluster %d
+DMSDOS: stac_cwalk_init: suballocation error 2, cluster %d
+DMSDOS: stac_cwalk_init: suballocation error 3, cluster %d, zerro offset 0x%X 0x%X
+DMSDOS: stac_cwalk_init: suballocation error 4, cluster %d
+DMSDOS: stac_cwalk_init: suballocation not present, cluster %d
+DMSDOS: stac_cwalk_init: too much fragmented cluster=%d!
+DMSDOS: stac_cwalk_init: unknown flags 0x%2x cluster %d
+DMSDOS: stac_cwalk_init: wrong cluster types for subalocation, cluster %d
+ E?: Looks like your stacker filesystem is corrupt.
+
+DMSDOS: stac_cwalk_sector: finfo==NULL, cluster %d
+ B: Internal bug in fragmented clusters access.
+
+DMSDOS: Stacker 0x1A0A signature not found
+ E/O: This CVF doesn't seem to be really a CVF.
+
+DMSDOS: Stacker sector size not 512 bytes, hmm...
+ E: Strange error in stacker filesystem or incompatible version (?)
+
+DMSDOS: STACKER signature not found
+ E/O: This CVF doesn't seem to be really a CVF.
+
+DMSDOS: stac_read_cluster: alloc error in cluster %d
+DMSDOS: stac_read_cluster: decompression error cluster=%d
+DMSDOS: stac_read_cluster: internal cw error 1 cluster=%d
+DMSDOS: stac_read_cluster: internal cw error 2 cluster=%d
+ E: Error in stacker filesystem.
+
+DMSDOS: stac_read_cluster: no memory!
+ O: The driver could not read a cluster because of lack of memory. Don't run
+ so much applications at the same time, increase your swap space or add
+ memory to your machine. (Shouldn't happen.) (*)[see below]
+
+DMSDOS: stac_replace_existing_cluster: This is a bug - reboot and check filesystem
+ B: Safety check in allocation routine failed. Please send a bug report.
+
+DMSDOS: stac_special_free: alloc error in cluster %d
+ E: Problems with deleting of cluster may be caused by
+ - damaged filesystem
+ should be reported when mounted as bitfat mismashes
+ can be repaired by mount option bitfaterrs=repair
+ - dmsdos internal bug
+
+DMSDOS: stac_write_cluster: alloc error in cluster %d
+DMSDOS: stac_write_cluster: internal cw error 1 cluster=%d
+DMSDOS: stac_write_cluster: internal cw error 2 cluster=%d
+ E: Error in stacker filesystem.
+
+DMSDOS: stac_write_cluster: no memory for compression, writing uncompressed!
+ O: Shouldn't happen.(*)[see below]
+
+DMSDOS: starting internal daemon...
+ I: The message tells it all.
+
+DMSDOS: Sumary: Deleted clusters = %d
+DMSDOS: Sumary: Free sectors = %d
+DMSDOS: Sumary: info1 = %d
+DMSDOS: Sumary: info2 = %d
+ I: Internal stacker messages. Information from checking of CVF.
+ First two are counted and next two are read from CVF.
+
+DMSDOS: support for doublespace/drivespace(<3) not compiled in.
+DMSDOS: support for drivespace 3 not compiled in.
+DMSDOS: support for stacker 3 not compiled in.
+DMSDOS: support for stacker 4 not compiled in.
+ O: The message tells it all. Rerun the dmsdos configuration and recompile
+ the source if you do need support for this type of CVF.
+
+DMSDOS: Too many BITFAT mismatches, check aborted.
+DMSDOS: Too many BITFAT mismatches in CVF, check aborted.
+ E: The message tells it all.
+
+DMSDOS: try_daemon: kill_proc daemon_pid=%d failed with error code %d, assuming daemon has died
+ O: The driver tried to wake up the external daemon, but it seems to be no
+ longer running.
+
+DMSDOS: try_daemon: no empty slot found, listcount corrected.
+ B: Shouldn't happen.
+
+DMSDOS: try_fragmented: cnt<0 ? This is a bug.
+DMSDOS: try_fragmented: frags=%d ? Cannot happen.
+DMSDOS: try_fragmented returned non-free sectors!
+ B: Shouldn't happen (bugs in code for writing fragmented clusters for
+ drivespace 3 volumes).
+
+DMSDOS: trying to allocate fragmented space...
+ I: The driver tries to create a fragmented cluster since disk space is
+ low or highly fragmented.
+
+DMSDOS: unable to read acache area=%d
+DMSDOS: unable to read bitfat area %d for sector %d
+DMSDOS: unable to read dfat area %d for cluster %d
+DMSDOS: unable to read emulated boot block of CVF
+DMSDOS: unable to read mdfat area %d for cluster %d
+DMSDOS: unable to read second dfat
+DMSDOS: unable to read second mdfat
+DMSDOS: unable to read super block
+DMSDOS: unable to read super block of CVF
+ E: Bad block or serious error in the underlying msdos filesystem. The
+ CVF or the underlying filesystem is most likely destroyed.
+
+DMSDOS: unable to read fragmentation list of cluster %d.
+ E: Error in drivespace 3 filesystem or hardware error.
+
+DMSDOS: unknown option %s, rejected
+ O: Syntax error in mount option string.
+
+DMSDOS: Updating BITFAT
+DMSDOS: Updating BITFAT.
+DMSDOS: Updating BITFAT finished
+DMSDOS: version_flag=%d sectperclust=%d
+ I: Debug info.
+
+DMSDOS: vmalloc returned unaligned memory - please disable XMALLOC
+ P/B: The xmalloc allocation mechanism is broken. This may be caused by
+ using a very new kernel. The driver detected this problem and stopped
+ immediately to prevent memory corruption. You may have to press the
+ reset button after this message. You can still use dmsdos, but you must
+ disable xmalloc before: rerun dmsdos configuration and disable advanced
+ memory management. Please send also a problem report so I can start
+ looking for what broke xmalloc in your kernel.
+
+DMSDOS: write access not compiled in, ignored
+ I: Someone or something tried to write to a compressed partition but write
+ access support is not compiled in. This message always appeares
+ *instead* of a real write access.
+
+ If you *want* to write to a compressed partition, you must compile
+ in write access support before. Rerun the dmsdos configuration
+ ('make config' in the src directory), recompile and reinstall dmsdos.
+
+DMSDOS: write_cluster: guessed 0x%08x.
+ I: The driver succeeded to guess the compression method and tells what
+ it has guessed.
+
+DMSDOS: write_cluster: guessing compression method...
+ I: The driver is analysing some clusters to determine the compression
+ method automatically. This kind of 'guessing' has proven to be very
+ reliable, so it may be a good idea to let the driver guess the
+ compression method instead of specifying it explicitely in a mount
+ option.
+
+DMSDOS: write_cluster: illegal cvf_version flag!
+ B: Looks like a bug in the dmsdos configuration (some missing or wrong
+ ifdefs somewhere). Let me know, please. Meanwhile, configure dmsdos
+ to support everything, recompile, and try again.
+
+DMSDOS: write_cluster: no memory for compression, writing uncompressed!
+ I: The driver could not compress a cluster because of lack of memory, so
+ it skipped compression. Don't run so much applications at the same time,
+ increase your swap space or add memory to your machine. (Shouldn't
+ happen.) (*)[see below]
+
+DMSDOS: write_file: ch_dirty failed!
+DMSDOS: write_file: ch_noread failed
+DMSDOS: write_file: ch_noread failed!
+ E: There should be another message describing the error.
+
+DMSDOS: write_file: CVF full (free sector count too low)
+DMSDOS: write_file: CVF full (full flag set)
+ O: Guess what. To prevent filesystem corruption, the driver has stopped
+ write access at a cluster boundary and returned the 'no space left on
+ device' error code in order to give an application the chance to exit
+ cleanly.
+
+ WARNING: As you cannot exactly say how well the data compress it is
+ dangerous to fill a CVF up to the last byte. The driver tries to stop
+ applications quite a time before it becomes dangerous. This message
+ indicates that up to now nothing dangerous has happened.
+
+DMSDOS: write_file: length>clustersize ??? bug !!!
+ B: Cannot happen.
+
+DMSDOS: write_file: fat_add_cluster failed
+ O: Filesystem is full or has run out of clusters. Run dutil to distinguish
+ between these two situations. In the latter case you need to boot dos
+ and increase the estimated compression ratio.
+
+DMSDOS: write_file: read_cluster failed!
+ E: There should be another message describing the error.
+
+DMSDOS: write_file: something wrong, cannot happen
+DMSDOS: write_file: something's wrong, cannot happen
+ B: Problem with writing a file.
+
+DMSDOS: write_fragmented: raw_getblk sector %d failed
+ O/B: Low-level disk i/o problem or serious bug (may even be a bug in
+ the rest of the kernel). A fragmented cluster couldn't be written.
+ This leaves behind a damaged filesystem. You must repair it under
+ Win95 (if it is a real hardware error, e.g. dying disk, you might
+ be lost hopelessly without a backup).
+
+DMSDOS: unable to read emulated boot block
+ E: Error in filesystem, check it under dos.
+
+DMSDOS: zero_new_cluster: ch_noread failed???
+ O/B: Don't know. Really. This shouldn't happen.
+
+VFS: LRU list corrupted
+VFS: free list corrupted
+(or similar)
+ P/B/O: You are very suffering from the vfat brelse bug (this is a serious
+ bug in some 2.2.x and maybe late 2.1.x kernels). This bug is triggered
+ by dmsdos. Ensure you have applied the vfat-brelse-bugfix.diff or you
+ are using a kernel where the bug is fixed (in 2.2.2 it is not yet fixed).
+ See file patches/DIFFS.TXT or INSTALL.TXT for details.
+
+ If not, this indicates probably a leak in dmsdos virtual sector code.
+ Please send a bug report to the current dmsdos maintainer and be prepared
+ to be asked for some tests in order to track down the bug.
+
+(*) ['no memory' messages:]
+ There was a problem in previous dmsdos versions that caused these
+ messages rarely though there was enough memory free. I've tried to fix
+ it by calling another memory allocation routine in newer dmsdos
+ releases. It's currently unknown if the new method is better. So if
+ you think you receive 'no memory' messages too often, you can try to
+ switch the allocation mode. It's setup during dmsdos configuration
+ ('make config'). If you change it, please let me know your results.
diff --git a/doc/patent.doc b/doc/patent.doc
new file mode 100644
index 0000000..bce9427
--- /dev/null
+++ b/doc/patent.doc
@@ -0,0 +1,101 @@
+
+Concerning the relationship between dmsdos and patents I keep receiving mail
+like this one...
+
+> In article <pycola.884372545.31697@laulujoutsen.pc.helsinki.fi> you wrote:
+> : dmsdos 0.9.0 has just been released. It supports read and write access to
+> : compressed dos filesystems [...]
+>
+> I think you should make sure no patents are violated. I am afraid that you
+> MUST get permission to distribute your code. I wish it was impossible
+> to get a software patent, but unfortunately companies do receive these
+> patents. There are many patents in encryption and compression technology!
+> [...]
+> Again, I wish it was possible/legal to write a free variant for every
+> existing commercial variant. Unfortunately it is not always possible.
+
+Let me say some gereral things about this. The patent problem is indeed as
+old as the first dmsdos version, which was released approximately three
+years ago.
+
+First, please allow me to correct some statements that are not litterally
+written above, but that sound like a misunderstanding. Dmsdos is not a
+"free variant" of an "existing commercial variant". Even both "free" and
+"variant" are wrong. Dmsdos is not free, but copyrighted and distributed
+under the GPL.
+
+Dmsdos also is not a "variant" of some commercially available software
+package. It is just a tool that makes it possible to read from and write
+to some variants of compressed dos filesystems. Dmsdos is surely not a
+filesystem compression software package like stacker and doublespace, as
+such a package needs a lot of more software e.g. all kinds of creation and
+maintainance tools. I know it would be nice to have them under Linux, but
+I won't write them for well known reasons.
+
+If you follow the dmsdos history, i.e. the dmsdos documentation of older
+releases, there have always been some comments on the possibility of patent
+problems mentioned in the documentation, and thus some features users liked
+to have in dmsdos were not implemented. Not because I knew they are
+forbidden by patent but because I thought they *might* be covered by a
+patent.
+
+Yes, I did contact the respective companies (no need to say which ones :) )
+for legal issues and, of course, I asked whether they were willing to help
+developing the code by providing documentation about their filesystem.
+So what? When I really had luck and got an answer, it was of no value. A
+stripped down version of one answer was published once in the dmsdos
+documentation. I removed the name of the company and the name of the person
+since I didn't want to blame one person. But I wanted to show the level
+of interest of these companies - on the one hand in support and cooperation,
+and on the other hand in a Linux version of their code. It was absolutely
+zero.
+
+So I did my own research. It was surely not exhaustive. Patents are things
+for lawyers, but I'm not a lawyer myself and I don't have the money to
+get a bunch of lawyers study all the software patents concerning data
+compression. Furthermore, I don't earn any penny with dmsdos.
+
+It lead to the result that there are a lot of patented compression
+algorithms. Also the compression algorithms that the original dos software
+uses for filesystem compression are covered by patents. So what. Dmsdos
+doesn't use them. Dmsdos was developed without official documentation, and
+it turned out that its compression algorithm even reached a higher
+compression ratio (but was much slower).
+
+I still tried to contact the companies, rarely, but it became more and more
+boring. I must admit that I gave it up some time ago. I also must admit
+that I didn't add all the patent problem related stuff to the dmsdos
+documentation when I rewrote it some day. I considered it simply dead.
+This implies that some features are still missing in dmsdos and will
+probably never be added because I don't just want to be exposed to the
+risk of violating a software patent and provoking a company owning it.
+The community of dmsdos users on the net seem to have accepted this.
+
+I also must say that dmsdos is not at all fully my own work. I just happen
+to maintain the code currently, and I'm not doing this on my own. The
+documentation used to implement dmsdos came from a lot of people on the
+net and even from a previous sample implementation(*) that was released under
+the GPL. If you know the GPL you also know that it has a very restrictive
+patent section, so I considered this quite safe.
+
+In fact, the compression and decompression routines in dmsdos are something
+like a collection of parts of free or GPL'd software. Most of them have
+meanwhile been rewritten from scratch for better performance. They use very
+common compression techniques. And you don't receive a patent for something
+that is well known. You can receive a patent, e.g. for a special, highly
+optimized algorithm, but, let me repeat this, dmsdos does not use any
+patented compression algorithms.
+
+So what can I do? Just throw away the dmsdos code and remove it from
+the servers? This is like a snail going back into its house and staying
+there though nothing is happening outside. Just continue trying to contact
+the companies? I'm bored by their answers if I happen to get one. Sorry.
+Just giving dmsdos maintainance into the hand of someone else? Heh. That
+would solve the problem probably for me, but not for others.
+
+If you just happen to know more about the patent situation than me, please
+let me know.
+
+
+(*) ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/thsfs.tgz
+ written 1994 by Thomas Scheuermann (current email address unknown)
diff --git a/doc/testing.doc b/doc/testing.doc
new file mode 100644
index 0000000..e8b342b
--- /dev/null
+++ b/doc/testing.doc
@@ -0,0 +1,68 @@
+
+Testing of read access with Dosemu
+==================================
+
+
+I describe the method I use for my drives, you may need to change some names.
+I have a DOS filesystem on /dev/hda1, I usually mount it on /DOS.
+This drive is seen as c: from DOS when starting.
+Stacvol file is c:stacvol.dsk. When config.sys is executed
+stacvol.dsk is swapped and becomes c: and old c: is d:.
+
+Prepare dmsdos package in /usr/local/src/dmsdos-x.y.z/src
+ rmmod dmsdos # for changes from next compilation
+ make clean
+ make
+ insmod dmsdos
+ cd /DOS
+ mount -r -t msdos -o loop stacvol.dsk /mnt # important: mount READ-ONLY!!!
+
+Now you should see the root of your DOS drive in /DOS directory and
+under /mnt the root of your stacker drive.
+In the syslog you can see the parameters of all mounted stacker drives.
+If you have dutil compiled, you can proceed the next test
+
+ dutil /mnt checkfs
+
+If there are errors you can see them in the syslog as DMSDOS: ...
+Now you can make the read test.
+Configure Dosemu for readonly access to the full hard drive or partition.
+
+ disk { partition "/dev/hda1" readonly } # must be first to boot from it
+ disk { image "/var/lib/dosemu/hdimage" } # or something similar
+ # you need this to have access to dosemu utilities
+
+You must include somewhere in config.sys line
+
+ device=<hdimage_drive>:\emufs.sys /
+
+Now run Dosemu by command
+
+ dos -C
+
+If it does not work try to find out if stacker can see its stackvols.
+There may be differences if you have some other organization of the drives
+under DOS.
+Now you can see or change redirections to the Linux filesystem
+
+ <hdimage_drive>:lredir
+
+For comparing you can use the dos utility cmpdisk.exe, which tries read all
+files from two drives. One should be the drive managed by regular
+dos stacker and the second by the Linux dmsdos driver.
+
+ cd <stac_drive>:\
+ cd <redirected_linux_drive>:\mnt
+ cmpdisk <stac_drive>: <redirected_linux_drive>:
+
+If some file is read differently from one drive, cmpdisk waits
+for a keystroke. Then it continues. Parameters of cmpdisk can
+be in form:
+
+ <drive_letter>:[\]<directory>\
+
+for example
+
+ E:\mnt\
+
+Any differences are suspected to be dmsdos bugs!
diff --git a/doc/troubleshooting.doc b/doc/troubleshooting.doc
new file mode 100644
index 0000000..5d15ad4
--- /dev/null
+++ b/doc/troubleshooting.doc
@@ -0,0 +1,290 @@
+troubleshooting.doc
+
+This file contains solutions to common problems with the dmsdos driver.
+It is mostly based on the feedback of dmsdos users that have sent mail to me
+for some hints (thanks a lot to all of you!).
+
+------------------------------------------------------------------------------
+
+ If you have problems, you may have found a bug. But check the following
+ list before (it is a collection of problems I have been reported and, I
+ think, can be solved easily). See also file BUGS for a list of known bugs.
+
+ *** See also your kernel log for 'DMSDOS:...' messages. Each message is
+ explained in file messages.doc. There you can find hints how to
+ solve the problems, too.
+
+- It does not install/compile!
+
+ Maybe the cvf.diff did not apply correctly. Check for *.rej files in
+ your kernel sources (only for the 2.0.xx kernels).
+
+ In some 2.1.xx kernels umsdos is broken. Get at least 2.1.94 if you want
+ to use umsdos together with dmsdos.
+
+ Depending on the dmsdos configuration the code might fail to compile.
+ Critical configuration options are:
+
+ * advanced memory management (switch it off if problems occur)
+ * writable mmap (switch it *on* for latest 2.1.xx kernels)
+
+ Please keep in mind that 2.1.xx kernels are still changing quite fast.
+ I cannot test every configuration option against each kernel...
+
+- The module doesn't load. It fails with a message about undefined symbols.
+
+ The dmsdos module requires fat filesystem support. If the fat module is
+ not loaded and not part of the kernel the dmsdos module refuses to load
+ and complains about missing symbols. Since kernel 2.0.34, fat additionally
+ depends on nls support. If it is the UMSDOS module that doesn't load,
+ see also the next problem :)
+
+- The UMSDOS module doesn't load though fat and msdos support is present.
+ It complains about missing symbols 'fat_readpage' and 'fat_dir_ioctl'.
+
+ This is a known bug in some older cvf.diffs from alpha test versions.
+ I forgot to add some symbols to the export list in older cvf.diffs for
+ kernel 2.0.33. This bug also went in 2.1.80 when CVF-FAT was included.
+ In order to fix the problem, edit file linux/fs/fat/fatfs_syms.c and
+ append the missing symbols 'fat_readpage' and 'fat_dir_ioctl' to the
+ export list. Look how the other symbols are listed there and put in the
+ missing ones in the same way. Then recompile the fat module (or the
+ kernel if your fat support isn't modular).
+
+- It causes a kernel panic with the message "VFS: LRU list corrupted".
+
+ You are very suffering from the vfat brelse bug (this is a serious
+ bug in some 2.2.x and maybe late 2.1.x kernels). This bug is triggered
+ by dmsdos. Ensure you have applied the vfat-brelse-bugfix.diff.
+ See file patches/DIFFS.TXT or INSTALL.TXT for details.
+
+ If not, this indicates probably a leak in dmsdos virtual sector code.
+ Please send a bug report to the current dmsdos maintainer and be prepared
+ to be asked for some tests in order to track down the bug.
+
+- It crashes and writes a register dump to the syslog.
+
+ If you did not load the module with the -m flag this time, you are out
+ of luck here. Please load the module with the -m flag (this prints a
+ symbol table) and redirect the output into a file. This symbol table is
+ required to analyse the crash. The symbol table usually differs from one
+ insmod to another depending on free memory pages, so you cannot just
+ use an older one :) Then try to reproduce the problem.
+
+ This smells like a bug - most likely. To be sure that this is really a
+ dmsdos related problem please read the README file in the Linux kernel
+ sources (/usr/src/linux/README). There's a procedure explained how to
+ obtain valuable information from the register dump in the syslog, in
+ particular, how to find out in which function the crash occured. Please
+ note that the register dump is absolutely system dependent and thus must
+ be analysed and interpreted by *you* (it's really not difficult, but it must
+ be done carefully - otherwise it's meaningless). Just use the symbol table
+ the insmod -m command created *before* the crash.
+
+ If it turns out to be a dmsdos function, you have discovered a serious bug
+ that should be fixed. Please send a bug report in that case. Please also
+ explain how to reproduce the problem with simple commands like 'ls',
+ 'cat' or 'touch', for example.
+
+- It hangs without any comments.
+
+ Can you switch to another virtual console and enter commands there? What
+ does 'ps' tell about the hanging process? Can you kill it? If the answer
+ to all these is no, this may be a bug.
+
+ Can you reproduce the problem with simple commands like 'ls' or 'cat'?
+
+ Please note that write access to a compressed partition may be awfully
+ slow. This depends on the compression level and on the fragmentation of
+ the compressed filesystem. Such a situation might be interpreted as a hang
+ though it isn't really one (especially on a 386SX16 machine...).
+
+ Also reading a large file or a huge directory might cause a system freeze
+ for a short time. This is especially true for the /dev directory (given
+ that you copied it to a compressed partition...)
+
+- After mounting the CVF I only see garbage when I do 'ls /_mountpoint_'.
+
+ Are you sure you loaded the dmsdos module? The plain fat driver (without
+ the dmsdos module) mounts some compressed volumes without complaining, but
+ it really can't deal with them and logs a lot of errors when trying to
+ read, for example, a directory on that partition. Since the mount command
+ is exactly the same this might indeed happen accidentially.
+
+ Take a look at your system log (usually in /var/log/messages). If you
+ don't see DMSDOS messages - unmount the filesystem, load the dmsdos module
+ and try again.
+
+ If you are sure you have loaded the module (try 'lsmod') dmsdos auto
+ detection may have failed. Try again with the appropriate cvf_format=xxx
+ mount option, i.e. cvf_format=dblspace (for M$ doublespace and drivespace)
+ or cvf_format=stacker (for Stacker 3 and 4).
+
+ If you do see DMSDOS messages, there are very likely much of them. If
+ the dmsdos driver cannot deal correctly with a filesystem it becomes
+ very noisy and sets the partition to read-only mode. In that case, please
+ let me know. You might have a new dblspace or stacker version that
+ should be supported too.
+
+- I cannot write to files, delete files, create directories ... in the
+ compressed subdirectories.
+
+ Lots of possibilities here -- watch your kernel log for a DMSDOS error
+ message that may explain what went wrong...
+ * Have you disabled write access at compile time? Then you'll find a
+ "write access not compiled in" message.
+ * Did you mount read-only (option ro)?
+ * Is your compressed partition almost full? The driver refuses to allocate
+ new clusters on a partition that is almost full when it thinks further
+ write access may be dangerous and might cause data loss. Delete some
+ files and retry.
+ * The compressed partition may have errors. The driver sets errorneous
+ compressed partitions to read-only immediately after mounting them.
+ However, you can allow write access again by setting the comp parameter
+ via dutil (example: 'dutil /DOS/dblspace.001 setcomp guess'). Of course,
+ it is not recommended to write to errorneous partitions.....
+
+- When trying to write to a compressed filesystem I receive I/O errors.
+
+ See previous problem. If you think it's another reason, run dutil to check
+ whether the filesystem is full or too fragmented. If the compressed
+ filesystem is read-only, all write access to the compressed filesystem is
+ refused. There is, of course, no real I/O error. The dmsdos driver returns
+ the 'permission denied' or 'no space left on device' error code depending
+ on the situation, but some applications simply claim 'I/O error' instead.
+
+- When trying to write to the compressed partition, I receive "no space left
+ on device" errors though dutil says there's *a lot* of free space (>100KB).
+
+ That depends. 100KB is not much. If the filesystem does not allow
+ fragmented data dmsdos reserves the space of 10 clusters for the case of
+ emergency (just imagine that data that compress well can be replaced by
+ data that don't compress at all and need more physical space). For a CVF
+ with 8KB cluster size it's 80KB, for a CVF with 32KB cluster size it's
+ 320KB. You cannot use this space for regular data. If the filesystem
+ supports fragmented data (e.g. drivespace 3, stacker 4) it's enough to
+ reserve space for one fragment of maximum size (drivespace 3: 32.5KB,
+ stacker 4: fragment support for write access is not yet implemented,
+ so size is 10 clusters).
+
+ There are some other possibilities. You may have run out of clusters. The
+ driver ensures that you don't get more clusters than dos would give to
+ you (otherwise dos' scandisk.exe would *destroy* the compressed partition
+ the next time it checks it by inventing some strange errors and trying to
+ correct them [arghhhh... seems to be a scandisk bug]). This can be fixed
+ by booting dos, running dblspace.exe or drvspace.exe, and setting the
+ compression ratio to the value the program suggests. (The procedure for
+ Stacker should be similar.) Don't be surprised if it suggests high
+ compression rates you aren't used to from dos - it's because dmsdos
+ compresses a little better than dos itself. (In fact, you needn't
+ believe the value dos claims - it's exaggerating sometimes....)
+
+ The third possibility is that your compressed partitiion has become too
+ fragmented at internal MDFAT level. Dos 6.0-6.22 Doublespace/Drivespace
+ and Win95 Doublespace/Drivespace (*not* drivespace 3) and Stacker 3 are
+ known to suffer extremely from internal MDFAT level fragmentation. Note
+ that this kind of fragmentation is different from usual FAT level
+ fragmentation and can not be repaired by those popular FAT defragmentation
+ programs. Use the CVF maintainance tools that came with your CVF software
+ to defragment the CVF.
+
+- A file I want to read appears to be empty but the directory says it is not
+ empty.
+
+ There was a problem decompressing the file. See your kernel messages
+ (/var/log/messages). The first thing to do is to boot dos and run the dos
+ filesystem checker on the compressed partition (do not skip the surface test
+ since only this test finds erros in compressed data). After that, reboot
+ Linux and retry. If the problem did not disappear this may be a real
+ dmsdos bug and should be emailed to the author.
+
+- I mounted a compressed floppy at /mnt as type umsdos and tried to run
+ umssync on the disk. It failed with an error message and now some or all
+ files are gone (help)!
+
+ You cannot umssync a compressed floppy without freeing up some space for
+ the umsdos special file --linux-.--- (the real msdos part of a compressed
+ floppy is always totally full).
+
+ But you have luck, the files are not gone at all. Unmount the floppy and
+ mount it as type msdos. Then remove the (empty) file --linux-.--- and the
+ 'readme' or 'readthis' or whatoever (it contains a message that this disk
+ is compressed and that you must run Micro$ doublespace to read it ...
+ very interesting - and not true at all: you can use Linux and dmsdos
+ instead :-) ). Now there should be some bytes free for umsdos. Unmount
+ the floppy again, mount it as type umsdos and retry. If the problem
+ appeared inside the compressed partition, also mount as type msdos, remove
+ --linux-.--- where files were gone, free up some space, mount again as type
+ umsdos, and retry running umssync.
+
+ This problem might be considered a umsdos caveat.
+
+- The utility dutil reports a different compression ratio and different free
+ space than dos.
+
+ The utility uses another method to calculate the compression ratio and the
+ amount of free space. Well, dos displays a dream ratio, dutil exactly
+ determines how much the files shrunk on compression. Dos' ratio is higher
+ because it compares the space the files *have* allocated with the space
+ the files *would have* allocated on an uncompressed partition with an 8 KB
+ (32KB under drivespace 3) cluster size. The slack at the end of the files
+ causes the difference (especially on small files), but saving wasted space
+ is not the same as compression (I think). Since the ratio is used to
+ estimate the free space, this value is also different.
+
+- The external utility dutil displays garbage.
+
+ You may have an old version of dutil somewhere in
+ your path, find and delete it and recompile dutil. If dutil displays just
+ zeros, you are definitely running a dutil from an older dmsdos version.
+ Remove it and recompile the source.
+
+- The external dmsdos utility displays some "lost clusters", but dos'
+ scandisk says everything is okay.
+
+ The term "lost clusters" doesn't mean that the clusters are not assigned to
+ a file (like in a normal dos fs). Instead, it tells there are clusters that
+ have been allocated in the FAT but have a zero MDFAT entry. This is not a
+ filesystem error, it only means that the clusters do exist, but they don't
+ contain any data and therefore don't use disk space. So they aren't really
+ used, but they aren't free either. This is a special situation that can only
+ occur in a compressed filesystem.
+
+- I can't see long filenames on my win95 partitions, I only see the short
+ ones.
+
+ Mount as type vfat instead of msdos.
+
+- I cannot unmount my dmsdos partition. umount complains with "device busy".
+
+ Some process is currently using a file or a directory of your partition.
+ Find and kill it, then retry. This may be the external dmsdos daemon...
+ Hint: 'man fuser'.
+
+- I cannot read some directories in my CVF. They seem to be empty, but they
+ aren't empty under dos/win95.
+
+ Watch for DMSDOS error messages in the kernel log.
+
+- On mount, I receive some strange error messages saying there were errors
+ in cluster xyz, but dos scandisk doesn't find them and even tells that the
+ CVF doesn't have such many clusters.
+
+ Sorry, there *is* an error in your filesystem. However, it's a kind of
+ hidden error, i.e. it doesn't have an immediate effect. It's due to
+ garbage in some currently unused parts of the FAT or MDFAT. Most likely
+ the slack of the FAT or MDFAT is not zerod out. It should be zerod out
+ in order to represent still a valid FAT/MDFAT in case the compressed
+ filesystem is enlarged (this may even happen by increasing the compression
+ ratio). Dos scandisk & Co. don't check the unused FAT/MDFAT slack for
+ errors. I've been reported that these errors can be fixed by setting a
+ large compression ratio, running scandisk again (now it should find and
+ correct the errors), and then resetting the compression ratio.
+
+- The dmsdos daemon always exits with an error "Can't get permissions...."
+
+ You are using the *internal* dmsdos daemon which is automatically started
+ when it is needed. In this case there's no need for the external daemon.
+ Watch your process list or look for a
+ '#define INTERNAL_DAEMON' in dmsdos-config.h. (The internal daemon is a
+ kernel process like kflushd or nfsiod.) See file dmsdos.doc for details.
diff --git a/in-kernel-uninstall.sh b/in-kernel-uninstall.sh
new file mode 100644
index 0000000..16476ba
--- /dev/null
+++ b/in-kernel-uninstall.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+if [ ! -d /usr/src/linux ];
+then
+ echo "No linux sources found in /usr/src/linux, aborting."
+ exit 1
+fi
+
+if [ ! -d /usr/src/linux/fs/dmsdos ];
+then
+ echo "dmsdos is not present in your kernel souce tree."
+ exit 1
+fi
+
+rm -r /usr/src/linux/fs/dmsdos
+( cd /usr/src/linux/fs
+ mv Config.in.orig Config.in
+ mv Makefile.orig Makefile
+ mv filesystems.c.orig filesystems.c
+ mv /usr/src/linux/Documentation/Configure.help.orig /usr/src/linux/Documentation/Configure.help
+)
diff --git a/in-kernel.sh b/in-kernel.sh
new file mode 100644
index 0000000..64c8d11
--- /dev/null
+++ b/in-kernel.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+if [ ! -d /usr/src/linux ];
+then
+ echo "No linux sources found in /usr/src/linux, aborting."
+ exit 1
+fi
+
+head -3 /usr/src/linux/Makefile | tr -d " " > /tmp/help.$$
+source /tmp/help.$$
+rm /tmp/help.$$
+VPS=`echo "$VERSION.$PATCHLEVEL.$SUBLEVEL"`
+VP=`echo "$VERSION.$PATCHLEVEL"`
+
+if [ -d /usr/src/linux/fs/dmsdos ];
+then
+ echo "dmsdos is already present in your kernel souce tree."
+ echo "This may have one of the following reasons:"
+ echo " * You have already run this script. There's no reason to run it again."
+ echo " * You have an older dmsdos version installed in your kernel sources. Get"
+ echo " fresh kernel sources and retry."
+ exit 1
+fi
+
+echo "This script integrates dmsdos into your kernel source tree. This will melt"
+echo "dmsdos and the linux kernel sources together. It will allow you to compile"
+echo "dmsdos as a fix part of your kernel (probably useful for a boot disk)."
+echo "Be warned, this is not well tested. If you haven't read the installation"
+echo "instructions or if you don't know what you are doing press CTRL-C now."
+echo
+echo "WARNING:"
+echo "THIS MAY MESS UP YOUR KERNEL SOURCES. BE SURE TO HAVE A BACKUP."
+echo
+echo "Press Enter to continue. Press CTRL-C to abort."
+read junk
+
+echo -n "Scanning for *.rej files..."
+REJ=`( cd /usr/src/linux; find . -name "*.rej" -print )`
+if [ ! -z "$REJ" ];
+then
+ echo
+ echo "There are *.rej files lying around in your kernel sources. Can't continue."
+ echo "$REJ"
+ echo "This usually means that you tried to patch your kernel some time ago but"
+ echo "that failed horribly. If you haven't repaired this by hand your kernel"
+ echo "sources are screwed up. Either throw them away and install fresh sources or"
+ echo "just delete the *.rej files in order to make me believe everything is ok :)"
+ exit 1
+fi
+
+echo " not found, ok."
+
+
+mkdir /usr/src/linux/fs/dmsdos
+( cd src
+ cp dblspace_*.c dstacker_*.c dmsdos.h Config.in /usr/src/linux/fs/dmsdos
+ cp Makefile.kernel /usr/src/linux/fs/dmsdos/Makefile
+ if [ "$VP" = "2.0" ];
+ then
+ F=dblspace_fileops.c-2.0.33
+ else
+ F=dblspace_fileops.c-2.1.80
+ fi
+ cp $F /usr/src/linux/fs/dmsdos/dblspace_fileops.c
+ cp /usr/src/linux/Documentation/Configure.help /usr/src/linux/Documentation/Configure.help.orig
+ cat Configure.help >> /usr/src/linux/Documentation/Configure.help
+)
+
+( cd /usr/src/linux; patch -p1 ) < patches/in-kernel-2.0.35.diff
+
+( cd /usr/src/linux/fs/dmsdos
+ ln -s ../../include/linux/config.h dmsdos-config.h
+)
+
+echo -n "Scanning for *.rej files..."
+REJ=`( cd /usr/src/linux; find . -name "*.rej" -print )`
+if [ ! -z "$REJ" ];
+then
+ echo
+ echo "There were *.rej files created during patch. Can't continue."
+ echo "$REJ"
+ echo "This shouldn't happen unless you have a highly patched or hacked kernel."
+ echo "Please have a look at the files and find out what went wrong."
+ exit 1
+fi
+
+echo " not found, ok."
diff --git a/magic.sh b/magic.sh
new file mode 100644
index 0000000..b1fd1ba
--- /dev/null
+++ b/magic.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+if [ ! -f /etc/magic ];
+then
+ echo "/etc/magic not found, skipped."
+ exit 0
+fi
+
+grep "CVF formats" /etc/magic > /dev/null
+if [ "$?" != "1" ];
+then
+ echo "/etc/magic contains already CVF magic numbers."
+ exit 0
+fi
+
+echo "If you want to make the command file(1) recognize CVFs the magic numbers"
+echo "can be added to the database /etc/magic automatically now."
+echo -n "Do you want me to do this ? (Y/N) "
+read ANS junk
+if [ "$ANS" = y -o "$ANS" = Y ];
+then
+ cat patches/magic >> /etc/magic
+fi
+
+exit 0
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 0000000..f2a7cfd
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,21 @@
+
+MAN_PREFIX=/usr/local/man
+
+install:
+ install dutil.1 -m 644 $(MAN_PREFIX)/man1/dutil.1
+ install dmsdosd.1 -m 644 $(MAN_PREFIX)/man1/dmsdosd.1
+ install dmsdosfsck.8 -m 644 $(MAN_PREFIX)/man8/dmsdosfsck.8
+ install mcdmsdos.1 -m 644 $(MAN_PREFIX)/man1/mcdmsdos.1
+ install cvftest.1 -m 644 $(MAN_PREFIX)/man1/cvftest.1
+ install cvflist.1 -m 644 $(MAN_PREFIX)/man1/cvflist.1
+
+install-min:
+ install dutil.1 -m 644 $(MAN_PREFIX)/man1/dutil.1
+
+uninstall:
+ rm -f $(MAN_PREFIX)/man1/dutil.1
+ rm -f $(MAN_PREFIX)/man1/dmsdosd.1
+ rm -f $(MAN_PREFIX)/man8/dmsdosfsck.8
+ rm -f $(MAN_PREFIX)/man1/mcdmsdos.1
+ rm -f $(MAN_PREFIX)/man1/cvftest.1
+ rm -f $(MAN_PREFIX)/man1/cvflist.1
diff --git a/man/cvflist.1 b/man/cvflist.1
new file mode 100644
index 0000000..7f4d0f1
--- /dev/null
+++ b/man/cvflist.1
@@ -0,0 +1,24 @@
+.TH cvflist 1 "10 Oct 1998" "dmsdos 0.9.2.0" "dmsdos CVF list tool"
+.SH NAME
+cvflist \- lists names of CVFs in a raw FAT12 or FAT16 partition
+.SH SYNOPSIS
+
+.BI cvflist " raw_device"
+
+.SH DESCRIPTION
+.I cvflist
+tries to find out whether the raw partition in raw_device contains
+CVFs. If so, their file names are printed. The raw partition must be in
+FAT12 or FAT16 format.
+
+The advantage of this program is that it works without mounting the
+partition.
+
+.SH BUGS
+.I cvflist
+may react unexpectedly on partitions that are not FAT12 or
+FAT16 (e.g. list some garbage).
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
diff --git a/man/cvftest.1 b/man/cvftest.1
new file mode 100644
index 0000000..f036b0d
--- /dev/null
+++ b/man/cvftest.1
@@ -0,0 +1,45 @@
+.TH cvftest 1 "10 Oct 1998" "dmsdos 0.9.2.0" "dmsdos CVF identification tool"
+.SH NAME
+cvftest \- dmsdos CVF identification tool
+.SH SYNOPSIS
+
+.BI cvftest " filename [-v]"
+
+.SH DESCRIPTION
+.I cvftest
+tries to find out whether the file (or device)
+.I filename
+is a CVF. It does so by reading the first 512 bytes from
+.I filename
+and checking for certain strings.
+
+If
+.I filename
+is just a minus sign (-) the data are read from standard input.
+
+If the switch
+.I -v
+is given the result of the test is printed to standard output including
+a description about what kind of CVF was detected.
+
+.SH RETURN VALUES
+.I cvftest
+returns zero (i.e. true) if
+.I filename
+has been recognized as a CVF. Otherwise it returns 1. A value >1
+indicates an error (e.g.
+.I filename
+does not exist or cannot be read).
+
+.SH BUGS
+This program is so simple that it could be a shell script. But I'm not a shell
+hacker, so it's C.
+
+The test is not idiot-proof. As it just checks for some magic values, it
+might eventually misidentify other files as CVFs. These cases are supposed
+to be extremely unlikely, though, and have never been reported yet. But if
+you take a look at the source you easily find a way to fool the program :-)
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
diff --git a/man/dmsdosd.1 b/man/dmsdosd.1
new file mode 100644
index 0000000..71262cb
--- /dev/null
+++ b/man/dmsdosd.1
@@ -0,0 +1,66 @@
+.TH dmsdosd 1 "15 Apr 1998" "dmsdos 0.9.0.14" "dmsdos daemon"
+.SH NAME
+dmsdosd \- dmsdos daemon for background compression
+.SH SYNOPSIS
+
+.BI dmsdosd " directory [level [debug]]"
+
+.SH DESCRIPTION
+.I dmsdosd
+does compression for the dmsdos driver in the background when
+the system is idle. This may reduce system load when there are much data
+to compress because the dmsdos driver can leave the data as they are and
+pass them to the daemon to do the job of compression.
+.I directory
+must be the mountpoint of a CVF that is served by dmsdos.
+In fact,
+.I directory
+may be also a subdirectory of the mountpoint. (If you mount more than
+one dmsdos partition it doesn't matter which one you specify. There is
+also no need to restart the daemon when additional partitions are mounted -
+the daemon automatically handles them all.)
+
+.I level
+is the compression level. It must be in the range of
+.BR 1 " - " 12 .
+If no
+.I level
+is given, it defaults to 12.
+
+If the parameter
+.I debug
+is given, dmsdosd prints a lot of debug information to stderr and doesn't
+put itself into the background.
+
+Note that you must run
+.I dmsdosd
+as root. The daemon refuses to run if started by another user. It also
+doesn't start if it finds already one instance of
+.I dmsdosd
+running.
+
+You can kill the daemon at any time (even when it is under heavy work).
+Send a SIGINT to let the daemon finish its actual work and then exit.
+Send a SIGTERM to make the daemon exit as soon as possible. You should not
+send a SIGKILL.
+
+.SH BUGS
+.I dmsdosd
+can only be started when a CVF has already been mounted by the
+dmsdos driver.
+
+You must kill
+.I dmsdosd
+before you can unmount the CVF that was given in the command line.
+
+Older versions (below 0.9.0.12) didn't run in the background, didn't check
+for already running processes and didn't use stderr for error and debug
+messages. This manual page is not valid for them.
+
+.SH FILES
+.I /var/run/dmsdosd.pid
+- pid of running daemon
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
diff --git a/man/dmsdosfsck.8 b/man/dmsdosfsck.8
new file mode 100644
index 0000000..67962ee
--- /dev/null
+++ b/man/dmsdosfsck.8
@@ -0,0 +1,41 @@
+.TH dmsdosfsck 8 "16 Jun 1998" "dmsdos 0.9.1.3" "dmsdos filesystem checker"
+.SH NAME
+dmsdosfsck \- dmsdos filesystem checker
+.SH SYNOPSIS
+
+.BI dmsdosfsck " [-aflrtvVw] [-d path -d ...] [-u path -u ...] filename"
+
+.SH DESCRIPTION
+.I dmsdosfsck
+checks the CVF located in
+.I filename
+for errors and optionally tries to repair errors. It is command-line
+compatible with dosfsck(8).
+
+ -a automatically repair the file system
+ -d path (*) drop that file
+ -f (*) salvage unused chains to files
+ -l list path names
+ -r interactively repair the file system
+ -t (*) test for bad clusters
+ -u path (*) try to undelete that (non-directory) file
+ -v verbose mode
+ -V (*) perform a verification pass
+ -w (*) write changes to disk immediately
+ (*) not yet implemented but option accepted for dosfsck compatibility
+
+
+.SH BUGS
+.I dmsdosfsck
+is not yet complete. Be extremely careful with repairs.
+
+.I It is strongly
+.I recommended to let only the native CVF management tools that came with your
+.I CVF software package do any repairs.
+Use
+.I dmsdosfsck
+only to check (which is a read-only operation).
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
diff --git a/man/dutil.1 b/man/dutil.1
new file mode 100644
index 0000000..1cd4530
--- /dev/null
+++ b/man/dutil.1
@@ -0,0 +1,175 @@
+.TH dutil 1 "16 Jun 1998" "dmsdos 0.9.1.3" "dmsdos utilities"
+.SH NAME
+dutil \- dmsdos hacker's utility
+.SH SYNOPSIS
+
+.BI dutil " directory [command [options ...]]"
+
+.SH DESCRIPTION
+Perform
+.I command
+on the CVF that is mounted under
+.I directory.
+In fact,
+.I directory
+may be also a subdirectory of the mountpoint.
+
+If no
+.I command
+is given, general information about the CVF is printed.
+
+
+.B List of commands
+
+
+.B cluster \- print cluster information
+
+.B sector \- read raw sector from disk and hexdump it
+
+.B rrawcluster \- read raw cluster from disk and hexdump it
+
+.B rcluster \- read and decompress cluster and hexdump it
+
+.B bitfat \- print bitfat content
+
+.B setcomp \- change compression format
+
+.B setcf \- change compression level
+
+.B dumpcache \- dump cache information to syslog
+
+.B synccache \- write dirty clusters to disk immediately
+
+.B logstat \- dump statistics to syslog
+
+.B checkfs \- check filesystem tables
+
+.B setloglevel \- change dmsdos log level
+
+.B setspeedup \- change dmsdos speedup flags
+
+.B memory \- report dmsdos memory usage for caching
+
+
+.B The commands in detail
+
+
+.B dutil directory cluster
+.I clusternr
+
+Print information about cluster
+.I clusternr.
+
+.B dutil directory sector
+.I sectornr [file]
+
+Read raw sector
+.I sectornr
+from disk and print it as hexdump. If
+.I file
+is given, it is written to a binary file (raw, not as hexdump).
+
+.B dutil directory rrawcluster
+.I clusternr [file]
+
+Read raw cluster data from cluster
+.I clusternr
+from disk and print it as hexdump. If
+.I file
+is given, it is written to a binary file (raw, not as hexdump).
+The cluster data are not decompressed. This command does not work for
+fragmented or suballocated clusters and may return complete garbage
+in these cases.
+
+.B dutil directory rcluster
+.I clusternr [file]
+
+Read and decompress cluster
+.I clusternr
+from disk and print it as hexdump. If
+.I file
+is given, it is written to a binary file (raw, not as hexdump).
+
+.B dutil directory bitfat
+.I sectornr
+
+Read bitfat allocation information of sector
+.I sectornr
+from disk and print it.
+
+.B dutil directory setcomp
+.I value
+
+Set compression scheme for write access.
+.I value
+must be one out of
+.B guess ds00 ds01 ds02 jm00 jm01 sq00 sd3 sd4 .
+See the dmsdos driver documentation about compression schemes.
+
+.B dutil directory setcf
+.I value
+
+Set compression level for write access.
+.I value
+must be one out of the range
+.BR 1 " - " 12.
+A higher level means better but slower compression.
+
+.B dutil directory dumpcache
+
+Dump dmsdos cache information to syslog.
+
+.B dutil directory synccache
+.I [value]
+
+Write dirty clusters back to disk immediately. If
+.I value
+is given and not zero, the data may be passed to the compression
+daemon instead of written directly to disk. Otherwise the command
+waits until the data are compressed and written. If you want to ensure
+physical write access to disk also
+.BR sync(1)
+the buffer cache.
+
+.B dutil directory logstat
+
+Write dmsdos statistics to syslog.
+
+.B dutil directory checkfs
+.I [value]
+
+Check the CVF internal tables for inconsistencies. If
+.I value
+is given and not zero, try to repair errors.
+
+.B dutil directory setloglevel
+.I value
+
+Set the dmsdos driver log level.
+.I value
+must be a decimal or a 0x prefixed hexadecimal number.
+See the dmsdos driver documentation for details about log levels.
+
+.B dutil directory setspeedup
+.I value
+
+Set the dmsdos driver speedup flags.
+.I value
+must be a decimal or a 0x prefixed hexadecimal number.
+See the dmsdos driver documentation for details about the speedup flags.
+
+.B dutil directory memory
+
+Report how much memory the dmsdos driver currently uses for caching.
+
+.SH BUGS
+
+The dutil commands for reading data from disk operate at a lower level than
+the dmsdos cluster cache.
+.I You must disable dmsdos write-back caching or sync the dmsdos cluster
+.I cache before each operation to get correct results in all cases.
+
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
diff --git a/man/mcdmsdos.1 b/man/mcdmsdos.1
new file mode 100644
index 0000000..9280583
--- /dev/null
+++ b/man/mcdmsdos.1
@@ -0,0 +1,47 @@
+.TH dmsdosd 1 "10 Oct 1998" "dmsdos 0.9.2.0" "dmsdos MC external fs tool"
+.SH NAME
+mcdmsdos \- dmsdos tool for Midnight Commander external filesystem
+.SH SYNOPSIS
+
+.BI mcdmsdos " mc-extfs-command args ..."
+
+i.e.
+
+.BI mcdmsdos " list <CVF_name> "
+
+.BI mcdmsdos " copyout <CVF_name> <path/name_in_CVF> <outfile> "
+
+.BI mcdmsdos " copyin <CVF_name> <path/name_in_CVF> <infile> "
+
+.SH DESCRIPTION
+.I mcdmsdos
+represents an interface for Midnight Commander
+.I mc(1)
+according to the documentation of 3.0 version for external filesystems.
+See the Midnight Commander documentation for more information about
+external filesystems.
+
+.I mc-extfs-command
+can be:
+
+.B list:
+lists the contents of a CVF (recursively) i.e. the names of all files
+in the CVF are printed.
+
+.B copyout:
+extracts a file from the CVF.
+
+.B copyin:
+stores a file in the CVF. This is currently not yet implemented and results
+in an error message.
+
+.SH BUGS
+.I mcdmsdos
+can interfere with mounted CVFs. You should not use mcdmsdos on a CVF that
+is currently mounted by dmsdos. This would not make any sense either.
+
+.SH SEE ALSO
+
+Refer to the dmsdos main documentation for further information.
+See the Midnight Commander documentation for more information about
+its external filesystem interface.
diff --git a/mkdist.sh b/mkdist.sh
new file mode 100644
index 0000000..6f34cb3
--- /dev/null
+++ b/mkdist.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+DIRNAME=`pwd`
+DIRNAME=`basename $DIRNAME`
+
+ARCHIVENAME=$DIRNAME.tgz
+
+cd ..
+
+echo "I'm going to create or overwrite file $ARCHIVENAME in directory"
+pwd
+echo "Press Enter to continue or CTRL-C to abort."
+read junk
+
+rm -f $ARCHIVENAME
+tar cvzf $ARCHIVENAME $DIRNAME
diff --git a/patch.sh b/patch.sh
new file mode 100644
index 0000000..a96b8e0
--- /dev/null
+++ b/patch.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+if [ ! -f /usr/src/linux/Makefile ];
+then
+ echo "No Linux sources found in /usr/src/linux."
+ echo "Sorry, I cannot determine automatically what needs to be done."
+ echo "Please install dmsdos manually."
+ echo "See the installation instructions for details."
+ exit 1
+fi
+
+head -3 /usr/src/linux/Makefile | tr -d " " > /tmp/help.$$
+source /tmp/help.$$
+rm /tmp/help.$$
+VPS=`echo "$VERSION.$PATCHLEVEL.$SUBLEVEL"`
+VP=`echo "$VERSION.$PATCHLEVEL"`
+
+if [ "$VERSION" -lt 2 ];
+then
+ echo "Kernel version $VPS is not supported. Need 2.0.29 or newer from 2.0 series"
+ echo "or 2.1.80 or newer."
+ exit 1
+fi
+
+echo "Now I'm trying to find out whether your kernel needs some patches for dmsdos."
+echo "If something is patched, a log file '*.log' will be written to /tmp."
+
+echo -n "Scanning for *.rej files..."
+REJ=`( cd /usr/src/linux; find . -name "*.rej" -print )`
+if [ ! -z "$REJ" ];
+then
+ echo
+ echo "There are *.rej files lying around in your kernel sources. Can't continue."
+ echo "$REJ"
+ echo "This usually means that you tried to patch your kernel some time ago but"
+ echo "that failed horribly. If you haven't repaired this by hand your kernel"
+ echo "sources are screwed up. Either throw them away and install fresh sources or"
+ echo "just delete the *.rej files in order to make me believe everything is ok :)"
+ exit 1
+fi
+
+echo " not found, ok."
+
+if [ -f /usr/src/linux/fs/fat/cvf.c ];
+then
+ echo "Kernel has already CVF-FAT support."
+ grep CVF-FAT-VERSION-ID /usr/src/linux/fs/fat/cvf.c > /dev/null
+ if [ $? = 1 ];
+ then
+ echo "But the CVF-FAT version in your kernel is an older version."
+ echo "You may want to update it. I will _not_ do this for you."
+ echo "See the installation instructions for details."
+ echo
+ echo "Press Enter to continue."
+ read junk
+ fi
+else
+ if [ "$VP" = "2.0" ];
+ then
+ if [ "$SUBLEVEL" -lt 29 ];
+ then
+ echo "Kernel version $VPS is not supported. Need 2.0.29 or newer from 2.0 series"
+ echo "or 2.1.80 or newer."
+ fi
+ #check for fat32
+ if [ -f /usr/src/linux/fs/nls.c ];
+ then
+ echo "Kernel 2.0.$SUBLEVEL with FAT32 support detected."
+ if [ $SUBLEVEL -lt 36 ];
+ then
+ echo "WARNING: FAT32 kernels earlier than 2.0.36 have a serious rmdir bug"
+ echo "that causes filesystem corruption in a msdos filesystem. This is not"
+ echo "a dmsdos problem but it is known to damage compressed filesystems too."
+ echo "Please upgrade to 2.0.36 or apply the msdos-rmdir-bugfix.diff manually"
+ echo "if you haven't repaired the bug otherwise. (I will not fix this bug.)"
+ echo "Press Enter to continue."
+ read junk
+ fi
+ echo "I'm going to install the CVF-FAT patches for FAT32 now."
+ echo "If you do not want me to do this, press CTRL-C now."
+ echo "To continue, press enter."
+ read junk
+ ( cd /usr/src/linux; patch -p1 ) < patches/cvf.diff-2.0.33+fat32-0.2.8 >& /tmp/cvf.diff.log
+ else
+ echo "Kernel 2.0.$SUBLEVEL without FAT32 detected."
+ echo "I'm going to install the CVF-FAT patches now."
+ echo "If you do not want me to do this, press CTRL-C now."
+ echo "WARNING: If you want to use FAT32 please abort now and"
+ echo "install the FAT32 patches before installing dmsdos."
+ echo "To continue, press enter."
+ read junk
+ ( cd /usr/src/linux; patch -p1 ) < patches/cvf.diff-2.0.33 >& /tmp/cvf.diff.log
+ fi
+ if [ -d /usr/src/linux/fs/uvfat ];
+ then
+ echo "UVFAT patches found."
+ echo "I'm going to install additional CVF-FAT patches for UVFAT now."
+ echo "If you do not want me to do this, press CTRL-C now."
+ echo "To continue, press enter."
+ read junk
+ ( cd /usr/src/linux; patch -p1 ) < patches/cvf-uvfat.diff-2.0.33 >& /tmp/cvf-uvfat.diff.log
+ fi
+ else
+ if [ "$VP" = "2.1" -a "$PATCHLEVEL" -lt 80 ];
+ then
+ echo "Kernel version $VPS is not supported. Need 2.0.29 or newer from 2.0 series"
+ echo "or 2.1.80 or newer."
+ exit 1
+ fi
+ fi
+fi
+
+echo -n "Scanning for *.rej files..."
+REJ=`( cd /usr/src/linux; find . -name "*.rej" -print )`
+if [ ! -z "$REJ" ];
+then
+ echo
+ echo "There were *.rej files created during patch. Can't continue."
+ echo "$REJ"
+ echo "This shouldn't happen unless you have a highly patched or hacked kernel :)"
+ echo "Please have a look at the files and find out what went wrong."
+ exit 1
+fi
+
+echo " not found, ok."
+exit 0
diff --git a/patches/DIFFS.TXT b/patches/DIFFS.TXT
new file mode 100644
index 0000000..6ad1e6f
--- /dev/null
+++ b/patches/DIFFS.TXT
@@ -0,0 +1,260 @@
+
+The list of CVF-FAT diffs is actually some lines below.
+
+Some notes about applying patches
+---------------------------------
+
+This section is for people not familiar with kernel patches. Feel free to
+skip it if you are an experienced patch hacker :)
+
+Well, as always be careful with diffs. Nobody knows whether they always
+apply correctly. Praise yourself lucky if you see errors on the screen -
+then at least you know that the patch failed. :)
+
+However, I've seen patches applying without errors, but changing code at
+the wrong place, even creating syntactically correct but senseless
+and not well working code. (For example, this happens for a small part of
+the patches if you use the plain 2.0.33 diff here for a fat32 patched
+2.0.33 kernel or a 2.1.76 kernel. Argh. It's almost impossible to find
+and repair such an error if you haven't written the code yourself.)
+
+From the patch manual:
+
+ Patch usually produces the correct results, even when it
+ has to do a lot of guessing. However, the results are
+ guaranteed to be correct only when the patch is applied to
+ exactly the same version of the file that the patch was
+ generated from.
+
+So do at least the usual security steps. It's best to get a fresh kernel
+from the net (*not* upgrading by patches). But sometimes you need patches.
+Ensure:
+
+ A. Before applying a patch:
+
+ 1. Copy your complete kernel sources to another place, and operate
+ only on this test copy, e.g.
+
+ cp -a /usr/src/linux-x.y.z /usr/src/linux-x.y.z-test
+ cd /usr/src/linux-x.y.z-test
+
+ 2. Remove all patch related files in the kernel sources, e.g.
+
+ find . -name "*.orig" -exec rm {} \;
+ find . -name "*.rej" -exec rm {} \;
+
+ B. Apply the patches you need. Order is important.
+
+ 1. Look at the first lines of the diff. There's very likely a complete
+ path in the filenames. If it begins with "linux..." you need the
+ option -p1, if it begins with "/usr/src/linux..." you need -p4 or
+ -p0 (you'll see). If the option is wrong or the number behind -p is
+ wrong, patch will display a prompt like "file to patch?" and wait
+ for your answer, which should be CTRL-C in that case :-). Okay:
+
+ patch -p1 < your-favorite-diff
+
+ 2. After applying each patch, watch for reject files, e.g.
+
+ find . -name "*.rej" -print
+
+ If there are any, then the patch failed. You must repair it manually
+ now or you can give up completely. If you dare to repair it, take
+ a look at the *.rej files and try to find out what should have been
+ changed in the sources but couldn't be changed for some reasons.
+
+ Just note, you *cannot* continue if any "*.rej" file is present and
+ the problem is not fixed manually. You will have messed up kernel
+ sources that won't work or won't even compile.
+
+ 3. After applying the patch and eventually fixing problems manually
+ remove all patch related files in the kernel sources, e.g.
+
+ find . -name "*.orig" -exec rm {} \;
+ find . -name "*.rej" -exec rm {} \;
+
+ 4. Then apply the next patch (if you need), and so on.
+
+ C. Recompile the kernel from scratch. Don't install it yet. Watch for
+ errors and warnings that were not present before:
+
+ make mrproper (this is not always needed, but if problems arise
+ without it, try again starting here)
+ make config (this is also not always needed)
+ make dep
+ make clean
+ make zImage
+ make modules
+
+ Okay, if everything succeeded, you might want to give it a try.
+ It's best to use a boot disk for the first test ('make zdisk'), but
+ if your configuration is highly modular this won't be enough,
+ so you might not have another choice than doing 'make zlilo' and
+ 'make modules_install'.
+
+ Be sure to have a rescue boot and root floppy (a simple boot disk is
+ not always sufficient). I know most people are lazy and ignore this
+ until "it" happens. I've been lazy too :-) Also be sure to have
+ your old kernel and your old modules handy (if you only operated on
+ your test copy of the kernel, as I recommended above, you have them
+ still somewhere in the original source tree).
+
+
+Good luck and happy hacking. :)
+
+
+----------------------------------------------------------------------------
+
+List of CVF-FAT related diffs
+-----------------------------
+
+cvf.diff-2.0.33
+
+ This is a diff was generated against a cyrix-patched 2.0.33 kernel (I need
+ cyrix support, so don't ask me to leave it out). This shouldn't matter as
+ the cyrix patches don't affect filesystem code. So just don't wonder why
+ the paths in that patch include the name 'cyrix-patched'. Of course, the
+ diff does not contain the cyrix patches itself :)
+
+ This diff has been tested thoroughly and found to work with a plain 2.0.33
+ kernel.
+
+ If you are using the uvfat patches, uvfat will not work correctly on CVFs
+ (it cannot since some wrappers must be patched in). You must additionally
+ apply the cvf-uvfat.diff-2.0.33 patch.
+
+ This patch does not work for fat32 support. Try the next one instead :)
+ This patch also fails for Linux 2.0.34 and newer. Try the next one in this
+ case.
+
+cvf.diff-2.0.33+fat32-0.2.8
+cvf.diff-2.0.34 (link to same file)
+
+ This diff was generated against a cyrix-patched and fat32-patched 2.0.33
+ kernel. It works also with a 2.0.33 kernel that is only fat32-patched.
+ The fat32 version used is 0.2.8.
+
+ Please don't misunderstand. This diff does *not* include fat32 support
+ itself. It only works against a kernel that has already been patched for
+ fat32 support.
+
+ This patch has also been tested against the new 2.0.34, 2.0.35 and 2.0.36
+ kernels and found to work without problems :)
+
+cvf-uvfat.diff-2.0.33
+
+ This diff changes the uvfat filesystem so it works properly on CVFs.
+ Changes are minimal. This diff does not contain the uvfat patches itself.
+
+ What uvfat is? Look:
+ uvfat : umsdos == vfat : msdos
+ Search the net for more information. There's a uvfat homepage:
+ http://lab1.psy.univie.ac.at/~michael/uvfat.html
+
+ This diff must be applied *after* the uvfat patch and *after* the
+ cvf.diff-2.0.33 patch (or cvf.diff-2.0.33+fat32-0.2.8 - depending on what
+ you need). Note that the uvfat patches caused a reject file when I tried
+ (fs/Makefile.rej), so be sure to check. The problem was because there was
+ autofs added in Linux 2.0.31, and plain uvfat is against kernel 2.0.28.
+ Just add the text uvfat to the list in Makefile, and it works. As far as
+ I know, there's no uvfat update for newer (2.1.xx with xx>80) kernels.
+
+ Okay, step by step, this is what I have done, and it worked for me:
+
+ 1. Get linux 2.0.33 sources and unpack.
+ 2. Patch in linux-2.0.28-uvfat-0.1.diff.
+ 3. Repair the rejected file fs/Makefile.
+ 4. Patch in cvf.diff-2.0.33.
+ 5. Patch in this patch.
+
+kerneld-autoload.diff
+
+ This diff has been removed because it is obsolete. Use the updated cvf.c
+ files instead (see file cvf.c.README).
+
+in-kernel-2.0.35.diff
+
+ This diff can be used to compile dmsdos as a fix part of the kernel.
+ It hangs dmsdos into some of the kernel initialization files. If you want
+ to compile dmsdos as a fix part of the kernel, apply this patch, then
+ create a directory linux/fs/dmsdos, copy the dmsdos sources from src into
+ that directory, replace the Makefile with 'Makefile.kernel', and create a
+ link 'dmsdos-config.h' pointing to the generic kernel config include file
+ 'linux/include/linux/config.h' If you want a working help subsystem just
+ append the dmsdos Configure.help file to that one in the kernel. All of
+ that should be done automatically by the script 'in-kernel.sh' so just take
+ a look at it for details :) The script additionally avoids copying
+ unnecessary files.
+
+ NOTE: This patch may not work for kernels other than 2.0.35. But changes
+ are small and easy to understand. I expect that patching the fs/Makefile
+ fails just in the same way as it fails for the UVFAT patches :) Fix that
+ by manually adding the word 'dmsdos' to the subdirectory list in that
+ file.
+
+msdos-rmdir-bugfix.diff
+
+ This is indeed an excerpt from Alan Cox' pre-2.0.36 patches. It fixes
+ the msdos rmdir bug that can cause filesystem corruption in a msdos
+ filesystem (and therefore also in a compressed msdos filesystem).
+ Affected kernels are 2.0.34 and 2.0.35 and all earlier kernels that have
+ been patched for FAT32 support version 0.2.8. The bug is fixed in 2.0.36.
+
+ The diff just adds two missing braces to the msdos fs source code ...
+
+ I do not know whether 2.1.xx kernels are affected too. Maybe you take a
+ look at what where the diff adds the two missing braces and verify that
+ they are there in your kernel sources :)
+
+ In 2.2.x kernels this bug is not present.
+
+fat-truncate-bugfix.diff
+
+ There's a design flaw somewhere in the kernel or in the FAT driver (I
+ really cannot say where) that can write to a disk though the device is
+ mounted read-only. It is triggered at least by damaged directory entries
+ on a FAT partition. Such damaged directory entries are a rare condition,
+ but the dmsdos driver may leave them around when finding another problem
+ and setting the filesystem to read-only mode. This bug can destroy any
+ FAT based filesystem that has damaged directory entries.
+
+ The bug is at least present in kernels up to 2.0.36 from the 2.0 series
+ and in all kernels in the 2.1 series. Also it is in 2.2.x up to 2.2.2. For
+ newer kernels I don't know. As always with bugfixes: take a look at the
+ diff and the kernel sources ... :)
+
+ This bug has not yet been fixed in official kernels (kernel 2.2.2).
+
+vfat-brelse-bugfix.diff
+
+ There is a serious bug in the vfat driver (calls to brelse instead of
+ fat_brelse) that makes the system corrupt internal filesystem buffer
+ lists. (The bug bypasses the CVF-FAT interface and also causes problems
+ with filesystems that use a non-512 byte sector size [very rare condition
+ today].) It has only been reported to affect 2.2.x kernels, but late 2.1.x
+ kernels may have this bug too. Check the source code. As of kernel 2.2.2,
+ it has not yet been fixed in official kernels.
+
+ The bug is not present in 2.0.x kernels.
+
+linux-2.2.13-dmsdos.patch-1
+
+ Simple fix to fat_add_cluster1 to return non-NULL pointer to
+ first buffer-head of newly allocated cluster.
+
+linux-2.3.30-pre6-dmsdos.patch-1
+
+ Many changes needed to enable basic DMSDOS functionality
+ without MMAP support for 2.3.30-pre6
+ It contains
+ - simple fix to fat_extend_dir to return non-NULL
+ pointer to first buffer-head of newly allocated cluster
+ - fixes to fat read super - cvf_format must be set before
+ cvf_mount is called.
+ - cvf struct type fixes
+
+
+--------------------------------------------------------------------------
+
+Well, are there other useful extensions for the fat based filesystems
+that should be checked whether they work with CVF-FAT ? Just let me know.
diff --git a/patches/cvf-uvfat.diff-2.0.33 b/patches/cvf-uvfat.diff-2.0.33
new file mode 100644
index 0000000..e778111
--- /dev/null
+++ b/patches/cvf-uvfat.diff-2.0.33
@@ -0,0 +1,110 @@
+diff -u -r -N linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/emd.c linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/emd.c
+--- linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/emd.c Sun Jan 4 15:01:56 1998
++++ linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/emd.c Sun Jan 4 15:15:23 1998
+@@ -34,6 +34,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_read(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+@@ -50,6 +51,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_write(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+diff -u -r -N linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/file.c linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/file.c
+--- linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/file.c Sun Jan 4 15:01:56 1998
++++ linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/file.c Sun Jan 4 15:18:16 1998
+@@ -131,3 +131,38 @@
+ NULL, /* smap */
+ };
+
++/* For other with larger and unaligned file system with readpage */
++struct file_operations uvfat_file_operations_readpage = {
++ NULL, /* lseek - default */
++ UVFAT_file_read, /* read */
++ UVFAT_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations uvfat_file_inode_operations_readpage = {
++ &uvfat_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ UVFAT_truncate,/* truncate */
++ NULL, /* permission */
++ NULL, /* smap */
++};
++
+diff -u -r -N linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/inode.c linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/inode.c
+--- linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/inode.c Sun Jan 4 15:01:56 1998
++++ linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/inode.c Sun Jan 4 15:22:33 1998
+@@ -165,11 +165,20 @@
+ if (!uvfat_isinit(inode)){
+ inode->u.uvfat_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
++ if (MSDOS_SB(inode->i_sb)->cvf_format){
++ if (MSDOS_SB(inode->i_sb)->cvf_format->flags
++ &CVF_USE_READPAGE){
++ inode->i_op = &uvfat_file_inode_operations_readpage;
++ }else{
++ inode->i_op = &uvfat_file_inode_operations_no_bmap;
++ }
++ }else{
+ if (inode->i_op->bmap != NULL){
+ inode->i_op = &uvfat_file_inode_operations;
+ }else{
+ inode->i_op = &uvfat_file_inode_operations_no_bmap;
+ }
++ }
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ uvfat_setup_dir_inode(inode);
+diff -u -r -N linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/ioctl.c linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/ioctl.c
+--- linux-2.0.33-cyrix-patched-uvfat/fs/uvfat/ioctl.c Sun Jan 4 15:01:56 1998
++++ linux-2.0.33-cyrix-patched-uvfat-test/fs/uvfat/ioctl.c Sun Jan 4 15:24:41 1998
+@@ -62,6 +62,21 @@
+ {
+ int ret = -EPERM;
+ int err;
++
++ /* forward non-uvfat ioctls - this hopefully doesn't cause conflicts */
++ if(cmd!=UVFAT_GETVERSION
++ &&cmd!=UVFAT_READDIR_DOS
++ &&cmd!=UVFAT_READDIR_EMD
++ &&cmd!=UVFAT_INIT_EMD
++ &&cmd!=UVFAT_CREAT_EMD
++ &&cmd!=UVFAT_RENAME_DOS
++ &&cmd!=UVFAT_UNLINK_EMD
++ &&cmd!=UVFAT_UNLINK_DOS
++ &&cmd!=UVFAT_RMDIR_DOS
++ &&cmd!=UVFAT_STAT_DOS
++ &&cmd!=UVFAT_DOS_SETUP)
++ return fat_dir_ioctl(dir,filp,cmd,data);
++
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UVFAT. EPERM is returned for other user.
diff --git a/patches/cvf.c b/patches/cvf.c
new file mode 100644
index 0000000..62c85c8
--- /dev/null
+++ b/patches/cvf.c
@@ -0,0 +1,147 @@
+/*
+ * CVF extensions for fat-based filesystems
+ *
+ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
+ *
+ * please do not remove the next line, dmsdos needs it for verifying patches
+ * CVF-FAT-VERSION-ID: 1.1.0
+ *
+ */
+
+#include<linux/sched.h>
+#include<linux/fs.h>
+#include<linux/msdos_fs.h>
+#include<linux/msdos_fs_sb.h>
+#include<linux/string.h>
+#include<linux/fat_cvf.h>
+#include<linux/config.h>
+#ifdef CONFIG_KERNELD
+#include<linux/kerneld.h>
+#endif
+
+#define MAX_CVF_FORMATS 3
+
+struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
+int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
+
+int register_cvf_format(struct cvf_format*cvf_format)
+{ int i,j;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i]==NULL)
+ { /* free slot found, now check version */
+ for(j=0;j<MAX_CVF_FORMATS;++j)
+ { if(cvf_formats[j])
+ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
+ { printk("register_cvf_format: version %d already registered\n",
+ cvf_format->cvf_version);
+ return -1;
+ }
+ }
+ }
+ cvf_formats[i]=cvf_format;
+ cvf_format_use_count[i]=0;
+ printk("CVF format %s (version id %d) successfully registered.\n",
+ cvf_format->cvf_version_text,cvf_format->cvf_version);
+ return 0;
+ }
+ }
+
+ printk("register_cvf_format: too many formats\n");
+ return -1;
+}
+
+int unregister_cvf_format(struct cvf_format*cvf_format)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
+ { if(cvf_format_use_count[i])
+ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
+ cvf_formats[i]->cvf_version);
+ return -1;
+ }
+
+ printk("CVF format %s (version id %d) successfully unregistered.\n",
+ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
+ cvf_formats[i]=NULL;
+ return 0;
+ }
+ }
+ }
+
+ printk("unregister_cvf_format: format %d is not registered\n",
+ cvf_format->cvf_version);
+ return -1;
+}
+
+void dec_cvf_format_use_count_by_version(int version)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==version)
+ { --cvf_format_use_count[i];
+ if(cvf_format_use_count[i]<0)
+ { cvf_format_use_count[i]=0;
+ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
+ }
+ return;
+ }
+ }
+ }
+
+ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
+ version);
+}
+
+int detect_cvf(struct super_block*sb,char*force)
+{ int i;
+ int found=0;
+ int found_i=-1;
+
+ if(force)
+ if(strcmp(force,"autoload")==0)
+ {
+#ifdef CONFIG_KERNELD
+ request_module("cvf_autoload");
+ force=NULL;
+#else
+ printk("cannot autoload CVF modules: kerneld support is not compiled into kernel\n");
+ return -1;
+#endif
+ }
+
+#ifdef CONFIG_KERNELD
+ if(force)
+ if(*force)
+ request_module(force);
+#endif
+
+ if(force)
+ { if(*force)
+ { for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
+ return i;
+ }
+ }
+ printk("CVF format %s unknown (module not loaded?)\n",force);
+ return -1;
+ }
+ }
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->detect_cvf(sb))
+ { ++found;
+ found_i=i;
+ }
+ }
+ }
+
+ if(found==1)return found_i;
+ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
+ return -1;
+}
diff --git a/patches/cvf.c-2.1.128 b/patches/cvf.c-2.1.128
new file mode 100644
index 0000000..9dd70f8
--- /dev/null
+++ b/patches/cvf.c-2.1.128
@@ -0,0 +1,147 @@
+/*
+ * CVF extensions for fat-based filesystems
+ *
+ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
+ *
+ * please do not remove the next line, dmsdos needs it for verifying patches
+ * CVF-FAT-VERSION-ID: 1.2.0
+ *
+ */
+
+#include<linux/sched.h>
+#include<linux/fs.h>
+#include<linux/msdos_fs.h>
+#include<linux/msdos_fs_sb.h>
+#include<linux/string.h>
+#include<linux/fat_cvf.h>
+#include<linux/config.h>
+#ifdef CONFIG_KMOD
+#include<linux/kmod.h>
+#endif
+
+#define MAX_CVF_FORMATS 3
+
+struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
+int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
+
+int register_cvf_format(struct cvf_format*cvf_format)
+{ int i,j;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i]==NULL)
+ { /* free slot found, now check version */
+ for(j=0;j<MAX_CVF_FORMATS;++j)
+ { if(cvf_formats[j])
+ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
+ { printk("register_cvf_format: version %d already registered\n",
+ cvf_format->cvf_version);
+ return -1;
+ }
+ }
+ }
+ cvf_formats[i]=cvf_format;
+ cvf_format_use_count[i]=0;
+ printk("CVF format %s (version id %d) successfully registered.\n",
+ cvf_format->cvf_version_text,cvf_format->cvf_version);
+ return 0;
+ }
+ }
+
+ printk("register_cvf_format: too many formats\n");
+ return -1;
+}
+
+int unregister_cvf_format(struct cvf_format*cvf_format)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
+ { if(cvf_format_use_count[i])
+ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
+ cvf_formats[i]->cvf_version);
+ return -1;
+ }
+
+ printk("CVF format %s (version id %d) successfully unregistered.\n",
+ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
+ cvf_formats[i]=NULL;
+ return 0;
+ }
+ }
+ }
+
+ printk("unregister_cvf_format: format %d is not registered\n",
+ cvf_format->cvf_version);
+ return -1;
+}
+
+void dec_cvf_format_use_count_by_version(int version)
+{ int i;
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->cvf_version==version)
+ { --cvf_format_use_count[i];
+ if(cvf_format_use_count[i]<0)
+ { cvf_format_use_count[i]=0;
+ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
+ }
+ return;
+ }
+ }
+ }
+
+ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
+ version);
+}
+
+int detect_cvf(struct super_block*sb,char*force)
+{ int i;
+ int found=0;
+ int found_i=-1;
+
+ if(force)
+ if(strcmp(force,"autoload")==0)
+ {
+#ifdef CONFIG_KMOD
+ request_module("cvf_autoload");
+ force=NULL;
+#else
+ printk("cannot autoload CVF modules: kmod support is not compiled into kernel\n");
+ return -1;
+#endif
+ }
+
+#ifdef CONFIG_KMOD
+ if(force)
+ if(*force)
+ request_module(force);
+#endif
+
+ if(force)
+ { if(*force)
+ { for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
+ return i;
+ }
+ }
+ printk("CVF format %s unknown (module not loaded?)\n",force);
+ return -1;
+ }
+ }
+
+ for(i=0;i<MAX_CVF_FORMATS;++i)
+ { if(cvf_formats[i])
+ { if(cvf_formats[i]->detect_cvf(sb))
+ { ++found;
+ found_i=i;
+ }
+ }
+ }
+
+ if(found==1)return found_i;
+ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
+ return -1;
+}
diff --git a/patches/cvf.c.README b/patches/cvf.c.README
new file mode 100644
index 0000000..9d5561c
--- /dev/null
+++ b/patches/cvf.c.README
@@ -0,0 +1,70 @@
+
+Update to CVF-FAT 1.1.0 or 1.2.0
+--------------------------------
+
+Today's kernels (currently means in time of Linux 2.2.2) use still CVF-FAT
+interface version 1.0.0. That is a bit outdated (and known to have one minor
+bug and some missing features).
+
+Latest versions are:
+ * CVF-FAT version 1.1.0 for 2.0.xx kernels
+ * CVF-FAT version 1.2.0 for kernels 2.1.128 or newer (also 2.2.x)
+ (The break between those two is somewhere between 2.1.94 and 2.1.128,
+ exactly it should be when kerneld was replaced by kmod, but I have never
+ verified that. Better use 2.0 or 2.2 kernels. :) )
+
+Files:
+
+ cvf.c: CVF-FAT 1.1.0. For kernels that support kerneld.
+ fat_cvf.txt: Documentation for CVF-FAT 1.1.0 (in file cvf.c).
+
+ cvf.c-2.1.128: CVF-FAT 1.2.0. For newer kernels that support kmod
+ instead of kerneld (including 2.2.x kernels).
+ fat_cvf.txt-2.1.128: Documentation for CVF-FAT 1.2.0 (in file
+ cvf.c-2.1.128).
+
+These files _replace_ the respective files in the kernel sources:
+
+ linux/fs/fat/cvf.c
+ linux/fs/Documentation/filesystems/fat_cvf.txt
+
+The only difference between CVF-FAT 1.1.0 and 1.2.0 is that 1.2.0 has
+been rewritten for kmod support which replaces kerneld in latest 2.1.xx
+kernels (and also 2.2.x kernels).
+*** Be sure to use the right file for your kernel! You must check the
+ kernel source or kernel documentation in order to find out whether
+ your kernel supports kerneld or kmod. At least, I know that all 2.0.xx
+ kernels support kerneld. Also, I checked that 2.1.128 uses kmod. Newer
+ kernels also should support kmod. That's all I know. You're on your
+ own here to find that out. A kernel that supports kmod should have a
+ file linux/Documentation/kmod.txt or similar, but I'm not 100% sure :)
+
+Who should update
+-----------------
+
+If you are using CVF-FAT 1.0.0 or older you may want to update. Watch for a
+version number in file linux/fs/fat/cvf.c. There should be a line somewhere
+at the beginning of the file that reads "CVF-FAT-VERSION-ID: x.y.z" where
+"x.y.z" is the version number. If this line is missing you are using version
+1.0.0 or older.
+
+Why update
+----------
+
+It fixes a bug that lets the mount command silently succeed if there is a
+special kind of error. This bug sometimes caused the plain FAT driver to
+mount a CVF in case of error with CVF-FAT support, which is surely not a
+good idea :)
+
+Now 'mount' fails with an error message in that case.
+
+Also the kerneld/kmod-autoload feature is now permanently in that file so you
+do not have to apply it as a seperate patch. For latest 2.1.xx kernels,
+where kerneld has been replaced by kmod, the kerneld-autoload support has
+been rewritten to use kmod instead.
+
+How to update
+-------------
+
+You can simply replace the old files with the new ones as listed above.
+Then recompile your modules (or your kernel if fat support is not modular).
diff --git a/patches/cvf.diff-2.0.33 b/patches/cvf.diff-2.0.33
new file mode 100644
index 0000000..84b3154
--- /dev/null
+++ b/patches/cvf.diff-2.0.33
@@ -0,0 +1,1056 @@
+diff -u -r -N linux-2.0.33-cyrix-patched/Documentation/filesystems/fat_cvf.txt linux-2.0.33-cyrix-patched-test/Documentation/filesystems/fat_cvf.txt
+--- linux-2.0.33-cyrix-patched/Documentation/filesystems/fat_cvf.txt Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-test/Documentation/filesystems/fat_cvf.txt Wed Nov 18 20:40:50 1998
+@@ -0,0 +1,210 @@
++This is the main documentation for the CVF-FAT filesystem extension. 18Nov1998
++
++
++Table of Contents:
++
++1. The idea of CVF-FAT
++2. Restrictions
++3. Mount options
++4. Description of the CVF-FAT interface
++5. CVF Modules
++
++------------------------------------------------------------------------------
++
++
++1. The idea of CVF-FAT
++------------------------------------------------------------------------------
++
++CVF-FAT is a FAT filesystem extension that provides a generic interface for
++Compressed Volume Files in FAT partitions. Popular CVF software, for
++example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
++Using the CVF-FAT interface, it is possible to load a module that handles
++all the low-level disk access that has to do with on-the-fly compression
++and decompression. Any other part of FAT filesystem access is still handled
++by the FAT, MSDOS or VFAT or even UMSDOS driver.
++
++CVF access works by redirecting certain low-level routines from the FAT
++driver to a loadable, CVF-format specific module. This module must fake
++a normal FAT filesystem to the FAT driver while doing all the extra stuff
++like compression and decompression silently.
++
++
++2. Restrictions
++------------------------------------------------------------------------------
++
++- BMAP problems
++
++ CVF filesystems cannot do bmap. It's impossible in principle. Thus
++ all actions that require bmap do not work (swapping, writable mmapping).
++ Read-only mmapping works because the FAT driver has a hack for this
++ situation :) Well, writable mmapping should now work using the readpage
++ interface function which has been hacked into the FAT driver just for
++ CVF-FAT :)
++
++- attention, DOSEmu users
++
++ You may have to unmount all CVF partitions before running DOSEmu depending
++ on your configuration. If DOSEmu is configured to use wholedisk or
++ partition access (this is often the case to let DOSEmu access
++ compressed partitions) there's a risk of destroying your compressed
++ partitions or crashing your system because of confused drivers.
++
++ Note that it is always safe to redirect the compressed partitions with
++ lredir or emufs.sys. Refer to the DOSEmu documentation for details.
++
++
++3. Mount options
++------------------------------------------------------------------------------
++
++The CVF-FAT extension currently adds the following options to the FAT
++driver's standard options:
++
++ cvf_format=xxx
++ Forces the driver to use the CVF module "xxx" instead of auto-detection.
++ Without this option, the CVF-FAT interface asks all currently loaded
++ CVF modules whether they recognize the CVF. Therefore, this option is
++ only necessary if the CVF format is not recognized correctly
++ because of bugs or incompatibilities in the CVF modules. (It skips
++ the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
++ to inhibit using any of the loaded CVF modules, just in case a CVF
++ module insists on mounting plain FAT filesystems by misunderstanding.
++ "xxx" may also be the text "autoload", which has a special meaning for
++ kerneld, but does not skip auto-detection.
++
++ If the kernel supports kerneld, the cvf_format=xxx option also controls
++ on-demand CVF module loading. Without this option, nothing is loaded
++ on demand. With cvf_format=xxx, a module "xxx" is requested automatically
++ before mounting the compressed filesystem (unless "xxx" is "none"). In
++ case there is a difference between the CVF format name and the module
++ name, setup aliases in your kerneld configuration. If the string "xxx"
++ is "autoload", a non-existent module "cvf_autoload" is requested which
++ can be used together with a special kerneld configuration (alias and
++ pre-install statements) in order to load more than one CVF module, let
++ them detect automatically which kind of CVF is to be mounted, and only
++ keep the "right" module in memory. For examples please refer to the
++ dmsdos documentation (ftp and http addresses see below).
++
++ cvf_options=yyy
++ Option string passed to the CVF module. I.e. only the "yyy" is passed
++ (without the quotes). The documentation for each CVF module should
++ explain it since it is interpreted only by the CVF module. Note that
++ the string must not contain a comma (",") - this would lead to
++ misinterpretation by the FAT driver, which would recognize the text
++ after a comma as a FAT driver option and might get confused or print
++ strange error messages. The documentation for the CVF module should
++ offer a different separation symbol, for example the dot "." or the
++ plus sign "+", which is only valid inside the string "yyy".
++
++
++4. Description of the CVF-FAT interface
++------------------------------------------------------------------------------
++
++Assuming you want to write your own CVF module, you need to write a lot of
++interface functions. Most of them are covered in the kernel documentation
++you can find on the net, and thus won't be described here. They have been
++marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long int flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ [...]
++ void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
++}
++
++This structure defines the capabilities of a CVF module. It must be filled
++out completely by a CVF module. Consider it as a kind of form that is used
++to introduce the module to the FAT/CVF-FAT driver.
++
++It contains...
++ - cvf_version:
++ A version id which must be unique. Choose one.
++ - cvf_version_text:
++ A human readable version string that should be one short word
++ describing the CVF format the module implements. This text is used
++ for the cvf_format option. This name must also be unique.
++ - flags:
++ Bit coded flags, currently only used for a readpage/mmap hack that
++ provides both mmap and readpage functionality. If CVF_USE_READPAGE
++ is set, mmap is set to generic_file_mmap and readpage is caught
++ and redirected to the cvf_readpage function. If it is not set,
++ readpage is set to generic_readpage and mmap is caught and redirected
++ to cvf_mmap. (If you want writable mmap use the readpage interface.)
++ - detect_cvf:
++ A function that is called to decide whether the filesystem is a CVF of
++ the type the module supports. The detect_cvf function must return 0
++ for "NO, I DON'T KNOW THIS GARBAGE" or anything >0 for "YES, THIS IS
++ THE KIND OF CVF I SUPPORT". The function must maintain the module
++ usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
++ and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
++ successful recongition would lead to a call of the mount_cvf function
++ later.
++ - mount_cvf:
++ A function that sets up some values or initializes something additional
++ to what has to be done when a CVF is mounted. This is called at the
++ end of fat_read_super and must return 0 on success. Definitely, this
++ function must increment the module usage counter by MOD_INC_USE_COUNT.
++ This mount_cvf function is also responsible for interpreting a CVF
++ module specific option string (the "yyy" from the FAT mount option
++ "cvf_options=yyy") which cannot contain a comma (use for example the
++ dot "." as option separator symbol).
++ - unmount_cvf:
++ A function that is called when the filesystem is unmounted. Most likely
++ it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
++ value might be ignored (it currently is ignored).
++ - [...]:
++ All other interface functions are "caught" FAT driver functions, i.e.
++ are executed by the FAT driver *instead* of the original FAT driver
++ functions. NULL means use the original FAT driver functions instead.
++ If you really want "no action", write a function that does nothing and
++ hang it in instead.
++ - cvf_zero_cluster:
++ The cvf_zero_cluster function is called when the fat driver wants to
++ zero out a (new) cluster. This is important for directories (mkdir).
++ If it is NULL, the FAT driver defaults to overwriting the whole
++ cluster with zeros. Note that clusternr is absolute, not relative
++ to the provided inode.
++
++Notes:
++ 1. The cvf_bmap function should be ignored. It really should never
++ get called from somewhere. I recommend redirecting it to a panic
++ or fatal error message so bugs show up immediately.
++ 2. The cvf_writepage function is ignored. This is because the fat
++ driver doesn't support it. This might change in future. I recommend
++ setting it to NULL (i.e use default).
++
++int register_cvf_format(struct cvf_format*cvf_format);
++ If you have just set up a variable containing the above structure,
++ call this function to introduce your CVF format to the FAT/CVF-FAT
++ driver. This is usually done in init_module. Be sure to check the
++ return value. Zero means success, everything else causes a kernel
++ message printed in the syslog describing the error that occurred.
++ Typical errors are:
++ - a module with the same version id is already registered or
++ - too many CVF formats. Hack fs/fat/cvf.c if you need more.
++
++int unregister_cvf_format(struct cvf_format*cvf_format);
++ This is usually called in cleanup_module. Return value =0 means
++ success. An error only occurs if you try to unregister a CVF format
++ that has not been previously registered. The code uses the version id
++ to distinguish the modules, so be sure to keep it unique.
++
++5. CVF Modules
++------------------------------------------------------------------------------
++
++Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
++sample implementation. It can currently be found at
++
++ ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos/dmsdos-x.y.z.tgz
++ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/dmsdos-x.y.z.tgz
++ ftp://ftp.uni-stuttgart.de/pub/systems/linux/local/system/dmsdos-x.y.z.tgz
++
++(where x.y.z is to be replaced with the actual version number). Full
++documentation about dmsdos is included in the dmsdos package, but can also
++be found at
++
++ http://fb9nt.uni-duisburg.de/mitarbeiter/gockel/software/dmsdos/index.html
++ http://www.yk.rim.or.jp/~takafumi/dmsdos/index.html (in Japanese).
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/Makefile linux-2.0.33-cyrix-patched-test/fs/fat/Makefile
+--- linux-2.0.33-cyrix-patched/fs/fat/Makefile Wed Feb 7 08:39:27 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/Makefile Sat Dec 27 21:34:20 1997
+@@ -8,7 +8,7 @@
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+
+ O_TARGET := fat.o
+-O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
++O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
+ OX_OBJS := fatfs_syms.o
+ M_OBJS := $(O_TARGET)
+
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/buffer.c linux-2.0.33-cyrix-patched-test/fs/fat/buffer.c
+--- linux-2.0.33-cyrix-patched/fs/fat/buffer.c Fri May 10 06:54:52 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/buffer.c Sat Dec 27 21:46:10 1997
+@@ -9,6 +9,7 @@
+ #include <linux/string.h>
+ #include <linux/fs.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ struct buffer_head *fat_bread (
+ struct super_block *sb,
+@@ -16,6 +17,10 @@
+ {
+ struct buffer_head *ret = NULL;
+
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_bread)
++ return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
++
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
+@@ -73,6 +78,11 @@
+ int block)
+ {
+ struct buffer_head *ret = NULL;
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_getblk)
++ return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
++
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+@@ -92,6 +102,10 @@
+ struct buffer_head *bh)
+ {
+ if (bh != NULL){
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_brelse)
++ return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
++
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+@@ -109,6 +123,12 @@
+ struct buffer_head *bh,
+ int dirty_val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty)
++ { MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty_val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -120,6 +140,12 @@
+ struct buffer_head *bh,
+ int val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_set_uptodate)
++ { MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -129,6 +155,10 @@
+ struct super_block *sb,
+ struct buffer_head *bh)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_is_uptodate)
++ return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -141,6 +171,12 @@
+ int nbreq,
+ struct buffer_head *bh[32])
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block)
++ { MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
++ return;
++ }
++
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/cache.c linux-2.0.33-cyrix-patched-test/fs/fat/cache.c
+--- linux-2.0.33-cyrix-patched/fs/fat/cache.c Fri May 10 06:54:52 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/cache.c Sun Dec 28 12:16:08 1997
+@@ -9,6 +9,7 @@
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/stat.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -24,6 +25,10 @@
+ unsigned char *p_first,*p_last;
+ int first,last,next,copy,b;
+
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->fat_access)
++ return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
++
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+@@ -246,6 +251,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_smap)
++ return sb->cvf_format->cvf_smap(inode,sector);
++
+ if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start)) {
+ if (sector >= sb->dir_entries >> MSDOS_DPS_BITS) return 0;
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/cvf.c linux-2.0.33-cyrix-patched-test/fs/fat/cvf.c
+--- linux-2.0.33-cyrix-patched/fs/fat/cvf.c Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-test/fs/fat/cvf.c Tue Nov 17 20:10:41 1998
+@@ -0,0 +1,147 @@
++/*
++ * CVF extensions for fat-based filesystems
++ *
++ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
++ *
++ * please do not remove the next line, dmsdos needs it for verifying patches
++ * CVF-FAT-VERSION-ID: 1.1.0
++ *
++ */
++
++#include<linux/sched.h>
++#include<linux/fs.h>
++#include<linux/msdos_fs.h>
++#include<linux/msdos_fs_sb.h>
++#include<linux/string.h>
++#include<linux/fat_cvf.h>
++#include<linux/config.h>
++#ifdef CONFIG_KERNELD
++#include<linux/kerneld.h>
++#endif
++
++#define MAX_CVF_FORMATS 3
++
++struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
++int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
++
++int register_cvf_format(struct cvf_format*cvf_format)
++{ int i,j;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i]==NULL)
++ { /* free slot found, now check version */
++ for(j=0;j<MAX_CVF_FORMATS;++j)
++ { if(cvf_formats[j])
++ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
++ { printk("register_cvf_format: version %d already registered\n",
++ cvf_format->cvf_version);
++ return -1;
++ }
++ }
++ }
++ cvf_formats[i]=cvf_format;
++ cvf_format_use_count[i]=0;
++ printk("CVF format %s (version id %d) successfully registered.\n",
++ cvf_format->cvf_version_text,cvf_format->cvf_version);
++ return 0;
++ }
++ }
++
++ printk("register_cvf_format: too many formats\n");
++ return -1;
++}
++
++int unregister_cvf_format(struct cvf_format*cvf_format)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
++ { if(cvf_format_use_count[i])
++ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
++ cvf_formats[i]->cvf_version);
++ return -1;
++ }
++
++ printk("CVF format %s (version id %d) successfully unregistered.\n",
++ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
++ cvf_formats[i]=NULL;
++ return 0;
++ }
++ }
++ }
++
++ printk("unregister_cvf_format: format %d is not registered\n",
++ cvf_format->cvf_version);
++ return -1;
++}
++
++void dec_cvf_format_use_count_by_version(int version)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==version)
++ { --cvf_format_use_count[i];
++ if(cvf_format_use_count[i]<0)
++ { cvf_format_use_count[i]=0;
++ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
++ }
++ return;
++ }
++ }
++ }
++
++ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
++ version);
++}
++
++int detect_cvf(struct super_block*sb,char*force)
++{ int i;
++ int found=0;
++ int found_i=-1;
++
++ if(force)
++ if(strcmp(force,"autoload")==0)
++ {
++#ifdef CONFIG_KERNELD
++ request_module("cvf_autoload");
++ force=NULL;
++#else
++ printk("cannot autoload CVF modules: kerneld support is not compiled into kernel\n");
++ return -1;
++#endif
++ }
++
++#ifdef CONFIG_KERNELD
++ if(force)
++ if(*force)
++ request_module(force);
++#endif
++
++ if(force)
++ { if(*force)
++ { for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
++ return i;
++ }
++ }
++ printk("CVF format %s unknown (module not loaded?)\n",force);
++ return -1;
++ }
++ }
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->detect_cvf(sb))
++ { ++found;
++ found_i=i;
++ }
++ }
++ }
++
++ if(found==1)return found_i;
++ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/dir.c linux-2.0.33-cyrix-patched-test/fs/fat/dir.c
+--- linux-2.0.33-cyrix-patched/fs/fat/dir.c Fri May 10 06:54:52 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/dir.c Sat Jan 3 12:41:00 1998
+@@ -382,6 +382,7 @@
+ unsigned int cmd, unsigned long arg)
+ {
+ int err;
++
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+@@ -408,6 +409,11 @@
+ vfat_ioctl_fill, NULL, 1, 0, 1);
+ }
+ default:
++ /* forward ioctl to CVF extension */
++ if(MSDOS_SB(inode->i_sb)->cvf_format
++ &&MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
++ return MSDOS_SB(inode->i_sb)->cvf_format->
++ cvf_dir_ioctl(inode,filp,cmd,arg);
+ return -EINVAL;
+ }
+
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/fatfs_syms.c linux-2.0.33-cyrix-patched-test/fs/fat/fatfs_syms.c
+--- linux-2.0.33-cyrix-patched/fs/fat/fatfs_syms.c Tue Mar 5 12:03:26 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/fatfs_syms.c Sat Jan 3 10:41:33 1998
+@@ -8,6 +8,7 @@
+
+ #include <linux/mm.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+ #include "tables.h"
+@@ -48,6 +49,13 @@
+ X(fat_uni2code),
+ X(fat_unlock_creation),
+ X(fat_write_inode),
++ X(register_cvf_format),
++ X(unregister_cvf_format),
++ X(get_cluster),
++ X(lock_fat),
++ X(unlock_fat),
++ X(fat_readpage),
++ X(fat_dir_ioctl),
+ #include <linux/symtab_end.h>
+ };
+
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/file.c linux-2.0.33-cyrix-patched-test/fs/fat/file.c
+--- linux-2.0.33-cyrix-patched/fs/fat/file.c Fri May 10 06:54:52 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/file.c Fri Jan 2 20:24:15 1998
+@@ -15,6 +15,7 @@
+ #include <linux/stat.h>
+ #include <linux/string.h>
+ #include <linux/pagemap.h>
++#include <linux/fat_cvf.h>
+
+ #include <asm/segment.h>
+ #include <asm/system.h>
+@@ -109,6 +110,40 @@
+ NULL /* smap */
+ };
+
++static struct file_operations fat_file_operations_readpage = {
++ NULL, /* lseek - default */
++ fat_file_read, /* read */
++ fat_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations fat_file_inode_operations_readpage = {
++ &fat_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ fat_truncate, /* truncate */
++ NULL, /* permission */
++ NULL /* smap */
++};
++
+ #define MSDOS_PREFETCH 32
+ struct fat_pre {
+ int file_sector;/* Next sector to read in the prefetch table */
+@@ -170,6 +205,10 @@
+ printk("fat_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_read)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_read(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_read: mode = %07o\n",inode->i_mode);
+@@ -287,6 +326,10 @@
+ printk("fat_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_write)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_write(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_write: mode = %07o\n",inode->i_mode);
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/inode.c linux-2.0.33-cyrix-patched-test/fs/fat/inode.c
+--- linux-2.0.33-cyrix-patched/fs/fat/inode.c Sat Nov 30 11:27:38 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/inode.c Sat Jan 3 10:46:27 1998
+@@ -18,6 +18,8 @@
+ #include <linux/fs.h>
+ #include <linux/stat.h>
+ #include <linux/locks.h>
++#include <linux/fat_cvf.h>
++#include <linux/malloc.h>
+
+ #include "msbuffer.h"
+ #include "tables.h"
+@@ -75,6 +77,10 @@
+
+ void fat_put_super(struct super_block *sb)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
++ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
++ }
+ fat_cache_inval_dev(sb->s_dev);
+ set_blocksize (sb->s_dev,BLOCK_SIZE);
+ lock_super(sb);
+@@ -86,7 +92,8 @@
+
+
+ static int parse_options(char *options,int *fat, int *blksize, int *debug,
+- struct fat_mount_options *opts)
++ struct fat_mount_options *opts,
++ char* cvf_format, char*cvf_options)
+ {
+ char *this_char,*value;
+
+@@ -181,6 +188,17 @@
+ return 0;
+ opts->sys_immutable = 1;
+ }
++ else if (!strcmp(this_char,"cvf_format")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_format,value,20);
++ }
++ else if (!strcmp(this_char,"cvf_options")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_options,value,100);
++ }
++
+ }
+ return 1;
+ }
+@@ -196,6 +214,14 @@
+ int debug,error,fat;
+ int blksize = 512;
+ struct fat_mount_options opts;
++ int i;
++ char cvf_format[21];
++ char cvf_options[101];
++
++ cvf_format[0]='\0';
++ cvf_options[0]='\0';
++ MSDOS_SB(sb)->cvf_format=NULL;
++ MSDOS_SB(sb)->private_data=NULL;
+
+ MOD_INC_USE_COUNT;
+ if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
+@@ -204,7 +230,7 @@
+ printk ("MSDOS: Hardware sector size is %d\n",blksize);
+ }
+ }
+- if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
++ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts, cvf_format, cvf_options)
+ || (blksize != 512 && blksize != 1024)) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+@@ -286,6 +312,9 @@
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
++ if(!strcmp(cvf_format,"none"))i=-1;
++ else i=detect_cvf(sb,cvf_format);
++ if(i>=0)error=cvf_formats[i]->mount_cvf(sb,cvf_options);
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+@@ -302,13 +331,15 @@
+ (unsigned long)b->total_sect,logical_sector_size);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+- if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
++ if(i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev "
+ "%s.\n", kdevname(sb->s_dev));
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+@@ -321,10 +352,16 @@
+ memcpy(&(MSDOS_SB(sb)->options), &opts, sizeof(struct fat_mount_options));
+ if (!(sb->s_mounted = iget(sb,MSDOS_ROOT_INO))) {
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ printk("get root inode failed\n");
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
++ if(i>=0)
++ { MSDOS_SB(sb)->cvf_format=cvf_formats[i];
++ ++cvf_format_use_count[i];
++ }
+ return sb;
+ }
+
+@@ -333,7 +370,13 @@
+ {
+ int free,nr;
+ struct statfs tmp;
+-
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_statfs)
++ { MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
++ return;
++ }
++
+ lock_fat(sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ free = MSDOS_SB(sb)->free_clusters;
+@@ -362,6 +405,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_bmap)
++ return sb->cvf_format->cvf_bmap(inode,block);
++
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ return sb->dir_start + block;
+ }
+@@ -451,7 +498,12 @@
+ !is_exec(raw_entry->ext)))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
+- inode->i_op = (sb->s_blocksize == 1024)
++ if(MSDOS_SB(sb)->cvf_format)
++ inode->i_op = (MSDOS_SB(sb)->cvf_format->flags&CVF_USE_READPAGE)
++ ? &fat_file_inode_operations_readpage
++ : &fat_file_inode_operations_1024;
++ else
++ inode->i_op = (sb->s_blocksize == 1024)
+ ? &fat_file_inode_operations_1024
+ : &fat_file_inode_operations;
+ MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/misc.c linux-2.0.33-cyrix-patched-test/fs/fat/misc.c
+--- linux-2.0.33-cyrix-patched/fs/fat/misc.c Fri May 10 06:54:52 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/misc.c Fri Jan 2 22:35:54 1998
+@@ -186,6 +186,10 @@
+ #endif
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
++ if(MSDOS_SB(sb)->cvf_format&&
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster)
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
++ else
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/fat/mmap.c linux-2.0.33-cyrix-patched-test/fs/fat/mmap.c
+--- linux-2.0.33-cyrix-patched/fs/fat/mmap.c Fri Feb 9 06:47:16 1996
++++ linux-2.0.33-cyrix-patched-test/fs/fat/mmap.c Fri Jan 2 20:15:29 1998
+@@ -93,6 +93,9 @@
+ */
+ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+ {
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(inode,file,vma);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+@@ -110,4 +113,12 @@
+ return 0;
+ }
+
++int fat_readpage(struct inode * inode, struct page * page)
++{
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
+
++ printk("fat_readpage called with no handler (shouldn't happen)\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/umsdos/emd.c linux-2.0.33-cyrix-patched-test/fs/umsdos/emd.c
+--- linux-2.0.33-cyrix-patched/fs/umsdos/emd.c Wed Feb 7 08:39:29 1996
++++ linux-2.0.33-cyrix-patched-test/fs/umsdos/emd.c Sat Jan 3 19:13:43 1998
+@@ -32,6 +32,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_read(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+@@ -48,6 +49,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_write(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/umsdos/file.c linux-2.0.33-cyrix-patched-test/fs/umsdos/file.c
+--- linux-2.0.33-cyrix-patched/fs/umsdos/file.c Tue Feb 20 09:28:13 1996
++++ linux-2.0.33-cyrix-patched-test/fs/umsdos/file.c Fri Jan 2 23:50:57 1998
+@@ -129,3 +129,38 @@
+ NULL, /* smap */
+ };
+
++/* For other with larger and unaligned file system with readpage */
++struct file_operations umsdos_file_operations_readpage = {
++ NULL, /* lseek - default */
++ UMSDOS_file_read, /* read */
++ UMSDOS_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations umsdos_file_inode_operations_readpage = {
++ &umsdos_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ UMSDOS_truncate,/* truncate */
++ NULL, /* permission */
++ NULL, /* smap */
++};
++
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/umsdos/inode.c linux-2.0.33-cyrix-patched-test/fs/umsdos/inode.c
+--- linux-2.0.33-cyrix-patched/fs/umsdos/inode.c Sat Nov 30 11:21:22 1996
++++ linux-2.0.33-cyrix-patched-test/fs/umsdos/inode.c Fri Jan 2 23:55:34 1998
+@@ -149,11 +149,20 @@
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
++ if (MSDOS_SB(inode->i_sb)->cvf_format){
++ if (MSDOS_SB(inode->i_sb)->cvf_format->flags
++ &CVF_USE_READPAGE){
++ inode->i_op = &umsdos_file_inode_operations_readpage;
++ }else{
++ inode->i_op = &umsdos_file_inode_operations_no_bmap;
++ }
++ } else {
+ if (inode->i_op->bmap != NULL){
+ inode->i_op = &umsdos_file_inode_operations;
+ }else{
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
++ }
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+diff -u -r -N linux-2.0.33-cyrix-patched/fs/umsdos/ioctl.c linux-2.0.33-cyrix-patched-test/fs/umsdos/ioctl.c
+--- linux-2.0.33-cyrix-patched/fs/umsdos/ioctl.c Wed Jul 3 15:39:48 1996
++++ linux-2.0.33-cyrix-patched-test/fs/umsdos/ioctl.c Sat Jan 3 13:01:52 1998
+@@ -60,6 +60,21 @@
+ {
+ int ret = -EPERM;
+ int err;
++
++ /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
++ if(cmd!=UMSDOS_GETVERSION
++ &&cmd!=UMSDOS_READDIR_DOS
++ &&cmd!=UMSDOS_READDIR_EMD
++ &&cmd!=UMSDOS_INIT_EMD
++ &&cmd!=UMSDOS_CREAT_EMD
++ &&cmd!=UMSDOS_RENAME_DOS
++ &&cmd!=UMSDOS_UNLINK_EMD
++ &&cmd!=UMSDOS_UNLINK_DOS
++ &&cmd!=UMSDOS_RMDIR_DOS
++ &&cmd!=UMSDOS_STAT_DOS
++ &&cmd!=UMSDOS_DOS_SETUP)
++ return fat_dir_ioctl(dir,filp,cmd,data);
++
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+diff -u -r -N linux-2.0.33-cyrix-patched/include/linux/fat_cvf.h linux-2.0.33-cyrix-patched-test/include/linux/fat_cvf.h
+--- linux-2.0.33-cyrix-patched/include/linux/fat_cvf.h Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-test/include/linux/fat_cvf.h Sat Jan 3 12:42:28 1998
+@@ -0,0 +1,55 @@
++#ifndef _FAT_CVF
++#define _FAT_CVF
++
++#define CVF_USE_READPAGE 0x0001
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ struct buffer_head* (*cvf_bread) (struct super_block*sb,int block);
++ struct buffer_head* (*cvf_getblk) (struct super_block*sb,int block);
++ void (*cvf_brelse) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_mark_buffer_dirty) (struct super_block *sb,
++ struct buffer_head *bh,
++ int dirty_val);
++ void (*cvf_set_uptodate) (struct super_block *sb,
++ struct buffer_head *bh,
++ int val);
++ int (*cvf_is_uptodate) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_ll_rw_block) (struct super_block *sb,
++ int opr,
++ int nbreq,
++ struct buffer_head *bh[32]);
++ int (*fat_access) (struct super_block *sb,int nr,int new_value);
++ void (*cvf_statfs) (struct super_block *sb,struct statfs *buf, int bufsiz);
++ int (*cvf_bmap) (struct inode *inode,int block);
++ int (*cvf_smap) (struct inode *inode,int sector);
++ int (*cvf_file_read) ( struct inode *inode,
++ struct file *filp,
++ char *buf,
++ int count);
++ int (*cvf_file_write) ( struct inode *inode,
++ struct file *filp,
++ const char *buf,
++ int count);
++ int (*cvf_mmap) (struct inode*, struct file *, struct vm_area_struct *);
++ int (*cvf_readpage) (struct inode *, struct page *);
++ int (*cvf_writepage) (struct inode *, struct page *);
++ int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
++ unsigned int cmd, unsigned long arg);
++ void (*zero_out_cluster) (struct inode*, int clusternr);
++};
++
++int register_cvf_format(struct cvf_format*cvf_format);
++int unregister_cvf_format(struct cvf_format*cvf_format);
++void dec_cvf_format_use_count_by_version(int version);
++int detect_cvf(struct super_block*sb,char*force);
++
++extern struct cvf_format *cvf_formats[];
++extern int cvf_format_use_count[];
++
++#endif
+diff -u -r -N linux-2.0.33-cyrix-patched/include/linux/msdos_fs.h linux-2.0.33-cyrix-patched-test/include/linux/msdos_fs.h
+--- linux-2.0.33-cyrix-patched/include/linux/msdos_fs.h Tue Dec 23 22:50:14 1997
++++ linux-2.0.33-cyrix-patched-test/include/linux/msdos_fs.h Sat Jan 3 13:03:16 1998
+@@ -221,12 +221,14 @@
+ /* file.c */
+ extern struct inode_operations fat_file_inode_operations;
+ extern struct inode_operations fat_file_inode_operations_1024;
++extern struct inode_operations fat_file_inode_operations_readpage;
+ extern int fat_file_read(struct inode *, struct file *, char *, int);
+ extern int fat_file_write(struct inode *, struct file *, const char *, int);
+ extern void fat_truncate(struct inode *inode);
+
+ /* mmap.c */
+ extern int fat_mmap(struct inode *, struct file *, struct vm_area_struct *);
++extern int fat_readpage(struct inode *, struct page *);
+
+
+ /* vfat.c */
+diff -u -r -N linux-2.0.33-cyrix-patched/include/linux/msdos_fs_sb.h linux-2.0.33-cyrix-patched-test/include/linux/msdos_fs_sb.h
+--- linux-2.0.33-cyrix-patched/include/linux/msdos_fs_sb.h Mon Mar 11 10:25:58 1996
++++ linux-2.0.33-cyrix-patched-test/include/linux/msdos_fs_sb.h Sat Jan 3 13:02:03 1998
+@@ -1,5 +1,6 @@
+ #ifndef _MSDOS_FS_SB
+ #define _MSDOS_FS_SB
++#include<linux/fat_cvf.h>
+
+ /*
+ * MS-DOS file system in-core superblock data
+@@ -34,6 +35,8 @@
+ int prev_free; /* previously returned free cluster number */
+ int free_clusters; /* -1 if undefined */
+ struct fat_mount_options options;
++ struct cvf_format* cvf_format;
++ void* private_data;
+ };
+
+ #endif
+diff -u -r -N linux-2.0.33-cyrix-patched/include/linux/umsdos_fs.h linux-2.0.33-cyrix-patched-test/include/linux/umsdos_fs.h
+--- linux-2.0.33-cyrix-patched/include/linux/umsdos_fs.h Tue Dec 23 22:50:14 1997
++++ linux-2.0.33-cyrix-patched-test/include/linux/umsdos_fs.h Sat Jan 3 13:04:02 1998
+@@ -135,6 +135,7 @@
+ extern struct file_operations umsdos_file_operations;
+ extern struct inode_operations umsdos_file_inode_operations;
+ extern struct inode_operations umsdos_file_inode_operations_no_bmap;
++extern struct inode_operations umsdos_file_inode_operations_readpage;
+ extern struct inode_operations umsdos_symlink_inode_operations;
+ extern int init_umsdos_fs(void);
+
+diff -u -r -N linux-2.0.33-cyrix-patched/kernel/ksyms.c linux-2.0.33-cyrix-patched-test/kernel/ksyms.c
+--- linux-2.0.33-cyrix-patched/kernel/ksyms.c Sat Nov 22 18:26:32 1997
++++ linux-2.0.33-cyrix-patched-test/kernel/ksyms.c Sat Jan 3 14:03:39 1998
+@@ -123,6 +123,9 @@
+ X(do_mmap),
+ X(do_munmap),
+ X(exit_mm),
++ X(exit_sighand),
++ X(exit_fs),
++ X(exit_files),
+
+ /* internal kernel memory management */
+ X(__get_free_pages),
diff --git a/patches/cvf.diff-2.0.33+fat32-0.2.8 b/patches/cvf.diff-2.0.33+fat32-0.2.8
new file mode 100644
index 0000000..4963301
--- /dev/null
+++ b/patches/cvf.diff-2.0.33+fat32-0.2.8
@@ -0,0 +1,1074 @@
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/Documentation/filesystems/fat_cvf.txt linux-2.0.33-cyrix-patched-fat32-test/Documentation/filesystems/fat_cvf.txt
+--- linux-2.0.33-cyrix-patched-fat32/Documentation/filesystems/fat_cvf.txt Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/Documentation/filesystems/fat_cvf.txt Wed Nov 18 20:40:50 1998
+@@ -0,0 +1,210 @@
++This is the main documentation for the CVF-FAT filesystem extension. 18Nov1998
++
++
++Table of Contents:
++
++1. The idea of CVF-FAT
++2. Restrictions
++3. Mount options
++4. Description of the CVF-FAT interface
++5. CVF Modules
++
++------------------------------------------------------------------------------
++
++
++1. The idea of CVF-FAT
++------------------------------------------------------------------------------
++
++CVF-FAT is a FAT filesystem extension that provides a generic interface for
++Compressed Volume Files in FAT partitions. Popular CVF software, for
++example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
++Using the CVF-FAT interface, it is possible to load a module that handles
++all the low-level disk access that has to do with on-the-fly compression
++and decompression. Any other part of FAT filesystem access is still handled
++by the FAT, MSDOS or VFAT or even UMSDOS driver.
++
++CVF access works by redirecting certain low-level routines from the FAT
++driver to a loadable, CVF-format specific module. This module must fake
++a normal FAT filesystem to the FAT driver while doing all the extra stuff
++like compression and decompression silently.
++
++
++2. Restrictions
++------------------------------------------------------------------------------
++
++- BMAP problems
++
++ CVF filesystems cannot do bmap. It's impossible in principle. Thus
++ all actions that require bmap do not work (swapping, writable mmapping).
++ Read-only mmapping works because the FAT driver has a hack for this
++ situation :) Well, writable mmapping should now work using the readpage
++ interface function which has been hacked into the FAT driver just for
++ CVF-FAT :)
++
++- attention, DOSEmu users
++
++ You may have to unmount all CVF partitions before running DOSEmu depending
++ on your configuration. If DOSEmu is configured to use wholedisk or
++ partition access (this is often the case to let DOSEmu access
++ compressed partitions) there's a risk of destroying your compressed
++ partitions or crashing your system because of confused drivers.
++
++ Note that it is always safe to redirect the compressed partitions with
++ lredir or emufs.sys. Refer to the DOSEmu documentation for details.
++
++
++3. Mount options
++------------------------------------------------------------------------------
++
++The CVF-FAT extension currently adds the following options to the FAT
++driver's standard options:
++
++ cvf_format=xxx
++ Forces the driver to use the CVF module "xxx" instead of auto-detection.
++ Without this option, the CVF-FAT interface asks all currently loaded
++ CVF modules whether they recognize the CVF. Therefore, this option is
++ only necessary if the CVF format is not recognized correctly
++ because of bugs or incompatibilities in the CVF modules. (It skips
++ the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
++ to inhibit using any of the loaded CVF modules, just in case a CVF
++ module insists on mounting plain FAT filesystems by misunderstanding.
++ "xxx" may also be the text "autoload", which has a special meaning for
++ kerneld, but does not skip auto-detection.
++
++ If the kernel supports kerneld, the cvf_format=xxx option also controls
++ on-demand CVF module loading. Without this option, nothing is loaded
++ on demand. With cvf_format=xxx, a module "xxx" is requested automatically
++ before mounting the compressed filesystem (unless "xxx" is "none"). In
++ case there is a difference between the CVF format name and the module
++ name, setup aliases in your kerneld configuration. If the string "xxx"
++ is "autoload", a non-existent module "cvf_autoload" is requested which
++ can be used together with a special kerneld configuration (alias and
++ pre-install statements) in order to load more than one CVF module, let
++ them detect automatically which kind of CVF is to be mounted, and only
++ keep the "right" module in memory. For examples please refer to the
++ dmsdos documentation (ftp and http addresses see below).
++
++ cvf_options=yyy
++ Option string passed to the CVF module. I.e. only the "yyy" is passed
++ (without the quotes). The documentation for each CVF module should
++ explain it since it is interpreted only by the CVF module. Note that
++ the string must not contain a comma (",") - this would lead to
++ misinterpretation by the FAT driver, which would recognize the text
++ after a comma as a FAT driver option and might get confused or print
++ strange error messages. The documentation for the CVF module should
++ offer a different separation symbol, for example the dot "." or the
++ plus sign "+", which is only valid inside the string "yyy".
++
++
++4. Description of the CVF-FAT interface
++------------------------------------------------------------------------------
++
++Assuming you want to write your own CVF module, you need to write a lot of
++interface functions. Most of them are covered in the kernel documentation
++you can find on the net, and thus won't be described here. They have been
++marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long int flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ [...]
++ void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
++}
++
++This structure defines the capabilities of a CVF module. It must be filled
++out completely by a CVF module. Consider it as a kind of form that is used
++to introduce the module to the FAT/CVF-FAT driver.
++
++It contains...
++ - cvf_version:
++ A version id which must be unique. Choose one.
++ - cvf_version_text:
++ A human readable version string that should be one short word
++ describing the CVF format the module implements. This text is used
++ for the cvf_format option. This name must also be unique.
++ - flags:
++ Bit coded flags, currently only used for a readpage/mmap hack that
++ provides both mmap and readpage functionality. If CVF_USE_READPAGE
++ is set, mmap is set to generic_file_mmap and readpage is caught
++ and redirected to the cvf_readpage function. If it is not set,
++ readpage is set to generic_readpage and mmap is caught and redirected
++ to cvf_mmap. (If you want writable mmap use the readpage interface.)
++ - detect_cvf:
++ A function that is called to decide whether the filesystem is a CVF of
++ the type the module supports. The detect_cvf function must return 0
++ for "NO, I DON'T KNOW THIS GARBAGE" or anything >0 for "YES, THIS IS
++ THE KIND OF CVF I SUPPORT". The function must maintain the module
++ usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
++ and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
++ successful recongition would lead to a call of the mount_cvf function
++ later.
++ - mount_cvf:
++ A function that sets up some values or initializes something additional
++ to what has to be done when a CVF is mounted. This is called at the
++ end of fat_read_super and must return 0 on success. Definitely, this
++ function must increment the module usage counter by MOD_INC_USE_COUNT.
++ This mount_cvf function is also responsible for interpreting a CVF
++ module specific option string (the "yyy" from the FAT mount option
++ "cvf_options=yyy") which cannot contain a comma (use for example the
++ dot "." as option separator symbol).
++ - unmount_cvf:
++ A function that is called when the filesystem is unmounted. Most likely
++ it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
++ value might be ignored (it currently is ignored).
++ - [...]:
++ All other interface functions are "caught" FAT driver functions, i.e.
++ are executed by the FAT driver *instead* of the original FAT driver
++ functions. NULL means use the original FAT driver functions instead.
++ If you really want "no action", write a function that does nothing and
++ hang it in instead.
++ - cvf_zero_cluster:
++ The cvf_zero_cluster function is called when the fat driver wants to
++ zero out a (new) cluster. This is important for directories (mkdir).
++ If it is NULL, the FAT driver defaults to overwriting the whole
++ cluster with zeros. Note that clusternr is absolute, not relative
++ to the provided inode.
++
++Notes:
++ 1. The cvf_bmap function should be ignored. It really should never
++ get called from somewhere. I recommend redirecting it to a panic
++ or fatal error message so bugs show up immediately.
++ 2. The cvf_writepage function is ignored. This is because the fat
++ driver doesn't support it. This might change in future. I recommend
++ setting it to NULL (i.e use default).
++
++int register_cvf_format(struct cvf_format*cvf_format);
++ If you have just set up a variable containing the above structure,
++ call this function to introduce your CVF format to the FAT/CVF-FAT
++ driver. This is usually done in init_module. Be sure to check the
++ return value. Zero means success, everything else causes a kernel
++ message printed in the syslog describing the error that occurred.
++ Typical errors are:
++ - a module with the same version id is already registered or
++ - too many CVF formats. Hack fs/fat/cvf.c if you need more.
++
++int unregister_cvf_format(struct cvf_format*cvf_format);
++ This is usually called in cleanup_module. Return value =0 means
++ success. An error only occurs if you try to unregister a CVF format
++ that has not been previously registered. The code uses the version id
++ to distinguish the modules, so be sure to keep it unique.
++
++5. CVF Modules
++------------------------------------------------------------------------------
++
++Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
++sample implementation. It can currently be found at
++
++ ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos/dmsdos-x.y.z.tgz
++ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/dmsdos-x.y.z.tgz
++ ftp://ftp.uni-stuttgart.de/pub/systems/linux/local/system/dmsdos-x.y.z.tgz
++
++(where x.y.z is to be replaced with the actual version number). Full
++documentation about dmsdos is included in the dmsdos package, but can also
++be found at
++
++ http://fb9nt.uni-duisburg.de/mitarbeiter/gockel/software/dmsdos/index.html
++ http://www.yk.rim.or.jp/~takafumi/dmsdos/index.html (in Japanese).
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/Makefile linux-2.0.33-cyrix-patched-fat32-test/fs/fat/Makefile
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/Makefile Wed Feb 7 08:39:27 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/Makefile Sun Jan 4 11:59:56 1998
+@@ -8,7 +8,7 @@
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+
+ O_TARGET := fat.o
+-O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
++O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
+ OX_OBJS := fatfs_syms.o
+ M_OBJS := $(O_TARGET)
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/buffer.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/buffer.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/buffer.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/buffer.c Sun Jan 4 12:07:19 1998
+@@ -9,6 +9,7 @@
+ #include <linux/string.h>
+ #include <linux/fs.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ #if 0
+ # define PRINTK(x) printk x
+@@ -26,6 +27,11 @@
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_bread)
++ return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
++
+ if (sb->s_blocksize == 512) {
+ ret = bread (sb->s_dev,block,512);
+ } else {
+@@ -81,6 +87,11 @@
+ {
+ struct buffer_head *ret = NULL;
+ PRINTK(("fat_getblk: block=0x%x\n", block));
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_getblk)
++ return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
++
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+@@ -100,6 +111,10 @@
+ struct buffer_head *bh)
+ {
+ if (bh != NULL){
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_brelse)
++ return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
++
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+@@ -117,6 +132,12 @@
+ struct buffer_head *bh,
+ int dirty_val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty)
++ { MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty_val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -128,6 +149,12 @@
+ struct buffer_head *bh,
+ int val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_set_uptodate)
++ { MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -137,6 +164,10 @@
+ struct super_block *sb,
+ struct buffer_head *bh)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_is_uptodate)
++ return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -149,6 +180,12 @@
+ int nbreq,
+ struct buffer_head *bh[32])
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block)
++ { MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
++ return;
++ }
++
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/cache.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cache.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/cache.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cache.c Sun Jan 4 12:08:48 1998
+@@ -9,6 +9,7 @@
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/stat.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -29,6 +30,10 @@
+ unsigned char *p_first,*p_last;
+ int copy,first,last,next,b;
+
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->fat_access)
++ return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
++
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+@@ -261,6 +266,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_smap)
++ return sb->cvf_format->cvf_smap(inode,sector);
++
+ if ((sb->fat_bits != 32) &&
+ (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))) {
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/cvf.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cvf.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/cvf.c Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cvf.c Tue Nov 17 20:10:41 1998
+@@ -0,0 +1,147 @@
++/*
++ * CVF extensions for fat-based filesystems
++ *
++ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
++ *
++ * please do not remove the next line, dmsdos needs it for verifying patches
++ * CVF-FAT-VERSION-ID: 1.1.0
++ *
++ */
++
++#include<linux/sched.h>
++#include<linux/fs.h>
++#include<linux/msdos_fs.h>
++#include<linux/msdos_fs_sb.h>
++#include<linux/string.h>
++#include<linux/fat_cvf.h>
++#include<linux/config.h>
++#ifdef CONFIG_KERNELD
++#include<linux/kerneld.h>
++#endif
++
++#define MAX_CVF_FORMATS 3
++
++struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
++int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
++
++int register_cvf_format(struct cvf_format*cvf_format)
++{ int i,j;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i]==NULL)
++ { /* free slot found, now check version */
++ for(j=0;j<MAX_CVF_FORMATS;++j)
++ { if(cvf_formats[j])
++ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
++ { printk("register_cvf_format: version %d already registered\n",
++ cvf_format->cvf_version);
++ return -1;
++ }
++ }
++ }
++ cvf_formats[i]=cvf_format;
++ cvf_format_use_count[i]=0;
++ printk("CVF format %s (version id %d) successfully registered.\n",
++ cvf_format->cvf_version_text,cvf_format->cvf_version);
++ return 0;
++ }
++ }
++
++ printk("register_cvf_format: too many formats\n");
++ return -1;
++}
++
++int unregister_cvf_format(struct cvf_format*cvf_format)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
++ { if(cvf_format_use_count[i])
++ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
++ cvf_formats[i]->cvf_version);
++ return -1;
++ }
++
++ printk("CVF format %s (version id %d) successfully unregistered.\n",
++ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
++ cvf_formats[i]=NULL;
++ return 0;
++ }
++ }
++ }
++
++ printk("unregister_cvf_format: format %d is not registered\n",
++ cvf_format->cvf_version);
++ return -1;
++}
++
++void dec_cvf_format_use_count_by_version(int version)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==version)
++ { --cvf_format_use_count[i];
++ if(cvf_format_use_count[i]<0)
++ { cvf_format_use_count[i]=0;
++ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
++ }
++ return;
++ }
++ }
++ }
++
++ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
++ version);
++}
++
++int detect_cvf(struct super_block*sb,char*force)
++{ int i;
++ int found=0;
++ int found_i=-1;
++
++ if(force)
++ if(strcmp(force,"autoload")==0)
++ {
++#ifdef CONFIG_KERNELD
++ request_module("cvf_autoload");
++ force=NULL;
++#else
++ printk("cannot autoload CVF modules: kerneld support is not compiled into kernel\n");
++ return -1;
++#endif
++ }
++
++#ifdef CONFIG_KERNELD
++ if(force)
++ if(*force)
++ request_module(force);
++#endif
++
++ if(force)
++ { if(*force)
++ { for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
++ return i;
++ }
++ }
++ printk("CVF format %s unknown (module not loaded?)\n",force);
++ return -1;
++ }
++ }
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->detect_cvf(sb))
++ { ++found;
++ found_i=i;
++ }
++ }
++ }
++
++ if(found==1)return found_i;
++ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/dir.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/dir.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/dir.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/dir.c Sun Jan 4 11:59:56 1998
+@@ -425,6 +425,7 @@
+ unsigned int cmd, unsigned long arg)
+ {
+ int err;
++
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+@@ -451,6 +452,11 @@
+ vfat_ioctl_fill, NULL, 1, 0, 1);
+ }
+ default:
++ /* forward ioctl to CVF extension */
++ if(MSDOS_SB(inode->i_sb)->cvf_format
++ &&MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
++ return MSDOS_SB(inode->i_sb)->cvf_format->
++ cvf_dir_ioctl(inode,filp,cmd,arg);
+ return -EINVAL;
+ }
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/fatfs_syms.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/fatfs_syms.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/fatfs_syms.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/fatfs_syms.c Sun Jan 4 12:11:10 1998
+@@ -11,6 +11,7 @@
+
+ #include <linux/mm.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -53,6 +54,13 @@
+ X(fat_uni2esc) X_PUNCT
+ X(fat_unlock_creation) X_PUNCT
+ X(fat_write_inode) X_PUNCT
++X(register_cvf_format) X_PUNCT
++X(unregister_cvf_format) X_PUNCT
++X(get_cluster) X_PUNCT
++X(lock_fat) X_PUNCT
++X(unlock_fat) X_PUNCT
++X(fat_readpage) X_PUNCT
++X(fat_dir_ioctl) X_PUNCT
+ #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0)
+ #include <linux/symtab_end.h>
+ };
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/file.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/file.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/file.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/file.c Sun Jan 4 11:59:56 1998
+@@ -17,6 +17,7 @@
+ #include <linux/stat.h>
+ #include <linux/string.h>
+ #include <linux/pagemap.h>
++#include <linux/fat_cvf.h>
+
+ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
+ #include <asm/uaccess.h>
+@@ -121,6 +122,40 @@
+ NULL /* smap */
+ };
+
++static struct file_operations fat_file_operations_readpage = {
++ NULL, /* lseek - default */
++ fat_file_read, /* read */
++ fat_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations fat_file_inode_operations_readpage = {
++ &fat_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ fat_truncate, /* truncate */
++ NULL, /* permission */
++ NULL /* smap */
++};
++
+ #define MSDOS_PREFETCH 32
+ struct fat_pre {
+ int file_sector;/* Next sector to read in the prefetch table */
+@@ -183,6 +218,10 @@
+ printk("fat_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_read)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_read(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_read: mode = %07o\n",inode->i_mode);
+@@ -305,6 +344,10 @@
+ printk("fat_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_write)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_write(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_write: mode = %07o\n",inode->i_mode);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/inode.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/inode.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/inode.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/inode.c Sun Jan 4 12:31:18 1998
+@@ -22,6 +22,7 @@
+ #include <linux/stat.h>
+ #include <linux/locks.h>
+ #include <linux/malloc.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -88,6 +89,10 @@
+
+ void fat_put_super(struct super_block *sb)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
++ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
++ }
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ fat_clusters_flush(sb);
+ }
+@@ -113,7 +118,8 @@
+
+
+ static int parse_options(char *options,int *fat, int *blksize, int *debug,
+- struct fat_mount_options *opts)
++ struct fat_mount_options *opts,
++ char* cvf_format, char*cvf_options)
+ {
+ char *this_char,*value,save,*savep;
+ char *p;
+@@ -239,6 +245,16 @@
+ ret = 0;
+ }
+ }
++ else if (!strcmp(this_char,"cvf_format")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_format,value,20);
++ }
++ else if (!strcmp(this_char,"cvf_options")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_options,value,100);
++ }
+
+ if (this_char != options) *(this_char-1) = ',';
+ if (value) *savep = save;
+@@ -261,6 +277,14 @@
+ struct fat_mount_options opts;
+ char buf[50];
+ char *p;
++ int i;
++ char cvf_format[21];
++ char cvf_options[101];
++
++ cvf_format[0]='\0';
++ cvf_options[0]='\0';
++ MSDOS_SB(sb)->cvf_format=NULL;
++ MSDOS_SB(sb)->private_data=NULL;
+
+ MOD_INC_USE_COUNT;
+ if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
+@@ -270,7 +294,8 @@
+ }
+ }
+ opts.isvfat = MSDOS_SB(sb)->options.isvfat;
+- if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
++ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts,
++ cvf_format, cvf_options)
+ || (blksize != 512 && blksize != 1024)) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+@@ -375,6 +400,9 @@
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
++ if(!strcmp(cvf_format,"none"))i=-1;
++ else i=detect_cvf(sb,cvf_format);
++ if(i>=0)error=cvf_formats[i]->mount_cvf(sb,cvf_options);
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+@@ -392,13 +420,15 @@
+ MSDOS_SB(sb)->root_cluster,MSDOS_SB(sb)->free_clusters);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+- if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
++ if(i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev "
+ "%s.\n", kdevname(sb->s_dev));
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+@@ -419,6 +449,8 @@
+ MSDOS_SB(sb)->nls_disk = load_nls_default();
+ } else {
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+@@ -433,6 +465,8 @@
+ kfree(opts.iocharset);
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ } else {
+@@ -447,9 +481,16 @@
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ if (MSDOS_SB(sb)->nls_io) unload_nls(MSDOS_SB(sb)->nls_io);
+ if (opts.iocharset) kfree(opts.iocharset);
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
++
++ if(i>=0)
++ { MSDOS_SB(sb)->cvf_format=cvf_formats[i];
++ ++cvf_format_use_count[i];
++ }
+
+ return sb;
+ }
+@@ -459,7 +500,13 @@
+ {
+ int free,nr;
+ struct statfs tmp;
+-
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_statfs)
++ { MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
++ return;
++ }
++
+ lock_fat(sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ free = MSDOS_SB(sb)->free_clusters;
+@@ -488,6 +535,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_bmap)
++ return sb->cvf_format->cvf_bmap(inode,block);
++
+ if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) {
+ return sb->dir_start + block;
+ }
+@@ -600,7 +651,12 @@
+ !is_exec(raw_entry->ext)))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
+- inode->i_op = (sb->s_blocksize == 1024)
++ if(MSDOS_SB(sb)->cvf_format)
++ inode->i_op = (MSDOS_SB(sb)->cvf_format->flags&CVF_USE_READPAGE)
++ ? &fat_file_inode_operations_readpage
++ : &fat_file_inode_operations_1024;
++ else
++ inode->i_op = (sb->s_blocksize == 1024)
+ ? &fat_file_inode_operations_1024
+ : &fat_file_inode_operations;
+ MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/misc.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/misc.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/misc.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/misc.c Sun Jan 4 11:59:56 1998
+@@ -225,6 +225,10 @@
+ #endif
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
++ if(MSDOS_SB(sb)->cvf_format&&
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster)
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
++ else
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/mmap.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/mmap.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/mmap.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/mmap.c Sun Jan 4 11:59:56 1998
+@@ -100,6 +100,9 @@
+ */
+ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+ {
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(inode,file,vma);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+@@ -117,4 +120,12 @@
+ return 0;
+ }
+
++int fat_readpage(struct inode * inode, struct page * page)
++{
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
+
++ printk("fat_readpage called with no handler (shouldn't happen)\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/emd.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/emd.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/emd.c Wed Feb 7 08:39:29 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/emd.c Sun Jan 4 11:59:56 1998
+@@ -32,6 +32,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_read(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+@@ -48,6 +49,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_write(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/file.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/file.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/file.c Tue Feb 20 09:28:13 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/file.c Sun Jan 4 11:59:56 1998
+@@ -129,3 +129,38 @@
+ NULL, /* smap */
+ };
+
++/* For other with larger and unaligned file system with readpage */
++struct file_operations umsdos_file_operations_readpage = {
++ NULL, /* lseek - default */
++ UMSDOS_file_read, /* read */
++ UMSDOS_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations umsdos_file_inode_operations_readpage = {
++ &umsdos_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ UMSDOS_truncate,/* truncate */
++ NULL, /* permission */
++ NULL, /* smap */
++};
++
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/inode.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/inode.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/inode.c Sat Nov 30 11:21:22 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/inode.c Sun Jan 4 11:59:56 1998
+@@ -149,11 +149,20 @@
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
++ if (MSDOS_SB(inode->i_sb)->cvf_format){
++ if (MSDOS_SB(inode->i_sb)->cvf_format->flags
++ &CVF_USE_READPAGE){
++ inode->i_op = &umsdos_file_inode_operations_readpage;
++ }else{
++ inode->i_op = &umsdos_file_inode_operations_no_bmap;
++ }
++ } else {
+ if (inode->i_op->bmap != NULL){
+ inode->i_op = &umsdos_file_inode_operations;
+ }else{
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
++ }
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/ioctl.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/ioctl.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/ioctl.c Wed Jul 3 15:39:48 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/ioctl.c Sun Jan 4 11:59:56 1998
+@@ -60,6 +60,21 @@
+ {
+ int ret = -EPERM;
+ int err;
++
++ /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
++ if(cmd!=UMSDOS_GETVERSION
++ &&cmd!=UMSDOS_READDIR_DOS
++ &&cmd!=UMSDOS_READDIR_EMD
++ &&cmd!=UMSDOS_INIT_EMD
++ &&cmd!=UMSDOS_CREAT_EMD
++ &&cmd!=UMSDOS_RENAME_DOS
++ &&cmd!=UMSDOS_UNLINK_EMD
++ &&cmd!=UMSDOS_UNLINK_DOS
++ &&cmd!=UMSDOS_RMDIR_DOS
++ &&cmd!=UMSDOS_STAT_DOS
++ &&cmd!=UMSDOS_DOS_SETUP)
++ return fat_dir_ioctl(dir,filp,cmd,data);
++
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/fat_cvf.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/fat_cvf.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/fat_cvf.h Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/fat_cvf.h Sun Jan 4 11:59:56 1998
+@@ -0,0 +1,55 @@
++#ifndef _FAT_CVF
++#define _FAT_CVF
++
++#define CVF_USE_READPAGE 0x0001
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ struct buffer_head* (*cvf_bread) (struct super_block*sb,int block);
++ struct buffer_head* (*cvf_getblk) (struct super_block*sb,int block);
++ void (*cvf_brelse) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_mark_buffer_dirty) (struct super_block *sb,
++ struct buffer_head *bh,
++ int dirty_val);
++ void (*cvf_set_uptodate) (struct super_block *sb,
++ struct buffer_head *bh,
++ int val);
++ int (*cvf_is_uptodate) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_ll_rw_block) (struct super_block *sb,
++ int opr,
++ int nbreq,
++ struct buffer_head *bh[32]);
++ int (*fat_access) (struct super_block *sb,int nr,int new_value);
++ void (*cvf_statfs) (struct super_block *sb,struct statfs *buf, int bufsiz);
++ int (*cvf_bmap) (struct inode *inode,int block);
++ int (*cvf_smap) (struct inode *inode,int sector);
++ int (*cvf_file_read) ( struct inode *inode,
++ struct file *filp,
++ char *buf,
++ int count);
++ int (*cvf_file_write) ( struct inode *inode,
++ struct file *filp,
++ const char *buf,
++ int count);
++ int (*cvf_mmap) (struct inode*, struct file *, struct vm_area_struct *);
++ int (*cvf_readpage) (struct inode *, struct page *);
++ int (*cvf_writepage) (struct inode *, struct page *);
++ int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
++ unsigned int cmd, unsigned long arg);
++ void (*zero_out_cluster) (struct inode*, int clusternr);
++};
++
++int register_cvf_format(struct cvf_format*cvf_format);
++int unregister_cvf_format(struct cvf_format*cvf_format);
++void dec_cvf_format_use_count_by_version(int version);
++int detect_cvf(struct super_block*sb,char*force);
++
++extern struct cvf_format *cvf_formats[];
++extern int cvf_format_use_count[];
++
++#endif
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs.h Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs.h Sun Jan 4 12:40:48 1998
+@@ -246,12 +246,14 @@
+ /* file.c */
+ extern struct inode_operations fat_file_inode_operations;
+ extern struct inode_operations fat_file_inode_operations_1024;
++extern struct inode_operations fat_file_inode_operations_readpage;
+ extern int fat_file_read(struct inode *, struct file *, char *, int);
+ extern int fat_file_write(struct inode *, struct file *, const char *, int);
+ extern void fat_truncate(struct inode *inode);
+
+ /* mmap.c */
+ extern int fat_mmap(struct inode *, struct file *, struct vm_area_struct *);
++extern int fat_readpage(struct inode *, struct page *);
+
+
+ /* vfat.c */
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs_sb.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs_sb.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs_sb.h Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs_sb.h Sun Jan 4 12:02:05 1998
+@@ -1,5 +1,6 @@
+ #ifndef _MSDOS_FS_SB
+ #define _MSDOS_FS_SB
++#include<linux/fat_cvf.h>
+
+ /*
+ * MS-DOS file system in-core superblock data
+@@ -46,6 +47,8 @@
+ struct fat_mount_options options;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
++ struct cvf_format* cvf_format;
++ void* private_data;
+ };
+
+ #endif
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/umsdos_fs.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/umsdos_fs.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/umsdos_fs.h Tue Dec 23 22:50:14 1997
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/umsdos_fs.h Sun Jan 4 12:40:48 1998
+@@ -135,6 +135,7 @@
+ extern struct file_operations umsdos_file_operations;
+ extern struct inode_operations umsdos_file_inode_operations;
+ extern struct inode_operations umsdos_file_inode_operations_no_bmap;
++extern struct inode_operations umsdos_file_inode_operations_readpage;
+ extern struct inode_operations umsdos_symlink_inode_operations;
+ extern int init_umsdos_fs(void);
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/kernel/ksyms.c linux-2.0.33-cyrix-patched-fat32-test/kernel/ksyms.c
+--- linux-2.0.33-cyrix-patched-fat32/kernel/ksyms.c Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/kernel/ksyms.c Sun Jan 4 11:59:56 1998
+@@ -124,6 +124,9 @@
+ X(do_mmap),
+ X(do_munmap),
+ X(exit_mm),
++ X(exit_sighand),
++ X(exit_fs),
++ X(exit_files),
+
+ /* internal kernel memory management */
+ X(__get_free_pages),
diff --git a/patches/cvf.diff-2.0.34 b/patches/cvf.diff-2.0.34
new file mode 100644
index 0000000..4963301
--- /dev/null
+++ b/patches/cvf.diff-2.0.34
@@ -0,0 +1,1074 @@
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/Documentation/filesystems/fat_cvf.txt linux-2.0.33-cyrix-patched-fat32-test/Documentation/filesystems/fat_cvf.txt
+--- linux-2.0.33-cyrix-patched-fat32/Documentation/filesystems/fat_cvf.txt Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/Documentation/filesystems/fat_cvf.txt Wed Nov 18 20:40:50 1998
+@@ -0,0 +1,210 @@
++This is the main documentation for the CVF-FAT filesystem extension. 18Nov1998
++
++
++Table of Contents:
++
++1. The idea of CVF-FAT
++2. Restrictions
++3. Mount options
++4. Description of the CVF-FAT interface
++5. CVF Modules
++
++------------------------------------------------------------------------------
++
++
++1. The idea of CVF-FAT
++------------------------------------------------------------------------------
++
++CVF-FAT is a FAT filesystem extension that provides a generic interface for
++Compressed Volume Files in FAT partitions. Popular CVF software, for
++example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
++Using the CVF-FAT interface, it is possible to load a module that handles
++all the low-level disk access that has to do with on-the-fly compression
++and decompression. Any other part of FAT filesystem access is still handled
++by the FAT, MSDOS or VFAT or even UMSDOS driver.
++
++CVF access works by redirecting certain low-level routines from the FAT
++driver to a loadable, CVF-format specific module. This module must fake
++a normal FAT filesystem to the FAT driver while doing all the extra stuff
++like compression and decompression silently.
++
++
++2. Restrictions
++------------------------------------------------------------------------------
++
++- BMAP problems
++
++ CVF filesystems cannot do bmap. It's impossible in principle. Thus
++ all actions that require bmap do not work (swapping, writable mmapping).
++ Read-only mmapping works because the FAT driver has a hack for this
++ situation :) Well, writable mmapping should now work using the readpage
++ interface function which has been hacked into the FAT driver just for
++ CVF-FAT :)
++
++- attention, DOSEmu users
++
++ You may have to unmount all CVF partitions before running DOSEmu depending
++ on your configuration. If DOSEmu is configured to use wholedisk or
++ partition access (this is often the case to let DOSEmu access
++ compressed partitions) there's a risk of destroying your compressed
++ partitions or crashing your system because of confused drivers.
++
++ Note that it is always safe to redirect the compressed partitions with
++ lredir or emufs.sys. Refer to the DOSEmu documentation for details.
++
++
++3. Mount options
++------------------------------------------------------------------------------
++
++The CVF-FAT extension currently adds the following options to the FAT
++driver's standard options:
++
++ cvf_format=xxx
++ Forces the driver to use the CVF module "xxx" instead of auto-detection.
++ Without this option, the CVF-FAT interface asks all currently loaded
++ CVF modules whether they recognize the CVF. Therefore, this option is
++ only necessary if the CVF format is not recognized correctly
++ because of bugs or incompatibilities in the CVF modules. (It skips
++ the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
++ to inhibit using any of the loaded CVF modules, just in case a CVF
++ module insists on mounting plain FAT filesystems by misunderstanding.
++ "xxx" may also be the text "autoload", which has a special meaning for
++ kerneld, but does not skip auto-detection.
++
++ If the kernel supports kerneld, the cvf_format=xxx option also controls
++ on-demand CVF module loading. Without this option, nothing is loaded
++ on demand. With cvf_format=xxx, a module "xxx" is requested automatically
++ before mounting the compressed filesystem (unless "xxx" is "none"). In
++ case there is a difference between the CVF format name and the module
++ name, setup aliases in your kerneld configuration. If the string "xxx"
++ is "autoload", a non-existent module "cvf_autoload" is requested which
++ can be used together with a special kerneld configuration (alias and
++ pre-install statements) in order to load more than one CVF module, let
++ them detect automatically which kind of CVF is to be mounted, and only
++ keep the "right" module in memory. For examples please refer to the
++ dmsdos documentation (ftp and http addresses see below).
++
++ cvf_options=yyy
++ Option string passed to the CVF module. I.e. only the "yyy" is passed
++ (without the quotes). The documentation for each CVF module should
++ explain it since it is interpreted only by the CVF module. Note that
++ the string must not contain a comma (",") - this would lead to
++ misinterpretation by the FAT driver, which would recognize the text
++ after a comma as a FAT driver option and might get confused or print
++ strange error messages. The documentation for the CVF module should
++ offer a different separation symbol, for example the dot "." or the
++ plus sign "+", which is only valid inside the string "yyy".
++
++
++4. Description of the CVF-FAT interface
++------------------------------------------------------------------------------
++
++Assuming you want to write your own CVF module, you need to write a lot of
++interface functions. Most of them are covered in the kernel documentation
++you can find on the net, and thus won't be described here. They have been
++marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long int flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ [...]
++ void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
++}
++
++This structure defines the capabilities of a CVF module. It must be filled
++out completely by a CVF module. Consider it as a kind of form that is used
++to introduce the module to the FAT/CVF-FAT driver.
++
++It contains...
++ - cvf_version:
++ A version id which must be unique. Choose one.
++ - cvf_version_text:
++ A human readable version string that should be one short word
++ describing the CVF format the module implements. This text is used
++ for the cvf_format option. This name must also be unique.
++ - flags:
++ Bit coded flags, currently only used for a readpage/mmap hack that
++ provides both mmap and readpage functionality. If CVF_USE_READPAGE
++ is set, mmap is set to generic_file_mmap and readpage is caught
++ and redirected to the cvf_readpage function. If it is not set,
++ readpage is set to generic_readpage and mmap is caught and redirected
++ to cvf_mmap. (If you want writable mmap use the readpage interface.)
++ - detect_cvf:
++ A function that is called to decide whether the filesystem is a CVF of
++ the type the module supports. The detect_cvf function must return 0
++ for "NO, I DON'T KNOW THIS GARBAGE" or anything >0 for "YES, THIS IS
++ THE KIND OF CVF I SUPPORT". The function must maintain the module
++ usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
++ and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
++ successful recongition would lead to a call of the mount_cvf function
++ later.
++ - mount_cvf:
++ A function that sets up some values or initializes something additional
++ to what has to be done when a CVF is mounted. This is called at the
++ end of fat_read_super and must return 0 on success. Definitely, this
++ function must increment the module usage counter by MOD_INC_USE_COUNT.
++ This mount_cvf function is also responsible for interpreting a CVF
++ module specific option string (the "yyy" from the FAT mount option
++ "cvf_options=yyy") which cannot contain a comma (use for example the
++ dot "." as option separator symbol).
++ - unmount_cvf:
++ A function that is called when the filesystem is unmounted. Most likely
++ it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
++ value might be ignored (it currently is ignored).
++ - [...]:
++ All other interface functions are "caught" FAT driver functions, i.e.
++ are executed by the FAT driver *instead* of the original FAT driver
++ functions. NULL means use the original FAT driver functions instead.
++ If you really want "no action", write a function that does nothing and
++ hang it in instead.
++ - cvf_zero_cluster:
++ The cvf_zero_cluster function is called when the fat driver wants to
++ zero out a (new) cluster. This is important for directories (mkdir).
++ If it is NULL, the FAT driver defaults to overwriting the whole
++ cluster with zeros. Note that clusternr is absolute, not relative
++ to the provided inode.
++
++Notes:
++ 1. The cvf_bmap function should be ignored. It really should never
++ get called from somewhere. I recommend redirecting it to a panic
++ or fatal error message so bugs show up immediately.
++ 2. The cvf_writepage function is ignored. This is because the fat
++ driver doesn't support it. This might change in future. I recommend
++ setting it to NULL (i.e use default).
++
++int register_cvf_format(struct cvf_format*cvf_format);
++ If you have just set up a variable containing the above structure,
++ call this function to introduce your CVF format to the FAT/CVF-FAT
++ driver. This is usually done in init_module. Be sure to check the
++ return value. Zero means success, everything else causes a kernel
++ message printed in the syslog describing the error that occurred.
++ Typical errors are:
++ - a module with the same version id is already registered or
++ - too many CVF formats. Hack fs/fat/cvf.c if you need more.
++
++int unregister_cvf_format(struct cvf_format*cvf_format);
++ This is usually called in cleanup_module. Return value =0 means
++ success. An error only occurs if you try to unregister a CVF format
++ that has not been previously registered. The code uses the version id
++ to distinguish the modules, so be sure to keep it unique.
++
++5. CVF Modules
++------------------------------------------------------------------------------
++
++Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
++sample implementation. It can currently be found at
++
++ ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos/dmsdos-x.y.z.tgz
++ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/dmsdos-x.y.z.tgz
++ ftp://ftp.uni-stuttgart.de/pub/systems/linux/local/system/dmsdos-x.y.z.tgz
++
++(where x.y.z is to be replaced with the actual version number). Full
++documentation about dmsdos is included in the dmsdos package, but can also
++be found at
++
++ http://fb9nt.uni-duisburg.de/mitarbeiter/gockel/software/dmsdos/index.html
++ http://www.yk.rim.or.jp/~takafumi/dmsdos/index.html (in Japanese).
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/Makefile linux-2.0.33-cyrix-patched-fat32-test/fs/fat/Makefile
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/Makefile Wed Feb 7 08:39:27 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/Makefile Sun Jan 4 11:59:56 1998
+@@ -8,7 +8,7 @@
+ # Note 2! The CFLAGS definitions are now in the main makefile...
+
+ O_TARGET := fat.o
+-O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o
++O_OBJS := buffer.o cache.o dir.o file.o inode.o misc.o mmap.o tables.o cvf.o
+ OX_OBJS := fatfs_syms.o
+ M_OBJS := $(O_TARGET)
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/buffer.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/buffer.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/buffer.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/buffer.c Sun Jan 4 12:07:19 1998
+@@ -9,6 +9,7 @@
+ #include <linux/string.h>
+ #include <linux/fs.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ #if 0
+ # define PRINTK(x) printk x
+@@ -26,6 +27,11 @@
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_bread)
++ return MSDOS_SB(sb)->cvf_format->cvf_bread(sb,block);
++
+ if (sb->s_blocksize == 512) {
+ ret = bread (sb->s_dev,block,512);
+ } else {
+@@ -81,6 +87,11 @@
+ {
+ struct buffer_head *ret = NULL;
+ PRINTK(("fat_getblk: block=0x%x\n", block));
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_getblk)
++ return MSDOS_SB(sb)->cvf_format->cvf_getblk(sb,block);
++
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+@@ -100,6 +111,10 @@
+ struct buffer_head *bh)
+ {
+ if (bh != NULL){
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_brelse)
++ return MSDOS_SB(sb)->cvf_format->cvf_brelse(sb,bh);
++
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+@@ -117,6 +132,12 @@
+ struct buffer_head *bh,
+ int dirty_val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty)
++ { MSDOS_SB(sb)->cvf_format->cvf_mark_buffer_dirty(sb,bh,dirty_val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -128,6 +149,12 @@
+ struct buffer_head *bh,
+ int val)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_set_uptodate)
++ { MSDOS_SB(sb)->cvf_format->cvf_set_uptodate(sb,bh,val);
++ return;
++ }
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -137,6 +164,10 @@
+ struct super_block *sb,
+ struct buffer_head *bh)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_is_uptodate)
++ return MSDOS_SB(sb)->cvf_format->cvf_is_uptodate(sb,bh);
++
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+@@ -149,6 +180,12 @@
+ int nbreq,
+ struct buffer_head *bh[32])
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block)
++ { MSDOS_SB(sb)->cvf_format->cvf_ll_rw_block(sb,opr,nbreq,bh);
++ return;
++ }
++
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/cache.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cache.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/cache.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cache.c Sun Jan 4 12:08:48 1998
+@@ -9,6 +9,7 @@
+ #include <linux/errno.h>
+ #include <linux/string.h>
+ #include <linux/stat.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -29,6 +30,10 @@
+ unsigned char *p_first,*p_last;
+ int copy,first,last,next,b;
+
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->fat_access)
++ return MSDOS_SB(sb)->cvf_format->fat_access(sb,nr,new_value);
++
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+@@ -261,6 +266,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_smap)
++ return sb->cvf_format->cvf_smap(inode,sector);
++
+ if ((sb->fat_bits != 32) &&
+ (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))) {
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/cvf.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cvf.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/cvf.c Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/cvf.c Tue Nov 17 20:10:41 1998
+@@ -0,0 +1,147 @@
++/*
++ * CVF extensions for fat-based filesystems
++ *
++ * written 1997,1998 by Frank Gockel <gockel@sent13.uni-duisburg.de>
++ *
++ * please do not remove the next line, dmsdos needs it for verifying patches
++ * CVF-FAT-VERSION-ID: 1.1.0
++ *
++ */
++
++#include<linux/sched.h>
++#include<linux/fs.h>
++#include<linux/msdos_fs.h>
++#include<linux/msdos_fs_sb.h>
++#include<linux/string.h>
++#include<linux/fat_cvf.h>
++#include<linux/config.h>
++#ifdef CONFIG_KERNELD
++#include<linux/kerneld.h>
++#endif
++
++#define MAX_CVF_FORMATS 3
++
++struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
++int cvf_format_use_count[MAX_CVF_FORMATS]={0,0,0};
++
++int register_cvf_format(struct cvf_format*cvf_format)
++{ int i,j;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i]==NULL)
++ { /* free slot found, now check version */
++ for(j=0;j<MAX_CVF_FORMATS;++j)
++ { if(cvf_formats[j])
++ { if(cvf_formats[j]->cvf_version==cvf_format->cvf_version)
++ { printk("register_cvf_format: version %d already registered\n",
++ cvf_format->cvf_version);
++ return -1;
++ }
++ }
++ }
++ cvf_formats[i]=cvf_format;
++ cvf_format_use_count[i]=0;
++ printk("CVF format %s (version id %d) successfully registered.\n",
++ cvf_format->cvf_version_text,cvf_format->cvf_version);
++ return 0;
++ }
++ }
++
++ printk("register_cvf_format: too many formats\n");
++ return -1;
++}
++
++int unregister_cvf_format(struct cvf_format*cvf_format)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==cvf_format->cvf_version)
++ { if(cvf_format_use_count[i])
++ { printk("unregister_cvf_format: format %d in use, cannot remove!\n",
++ cvf_formats[i]->cvf_version);
++ return -1;
++ }
++
++ printk("CVF format %s (version id %d) successfully unregistered.\n",
++ cvf_formats[i]->cvf_version_text,cvf_formats[i]->cvf_version);
++ cvf_formats[i]=NULL;
++ return 0;
++ }
++ }
++ }
++
++ printk("unregister_cvf_format: format %d is not registered\n",
++ cvf_format->cvf_version);
++ return -1;
++}
++
++void dec_cvf_format_use_count_by_version(int version)
++{ int i;
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->cvf_version==version)
++ { --cvf_format_use_count[i];
++ if(cvf_format_use_count[i]<0)
++ { cvf_format_use_count[i]=0;
++ printk(KERN_EMERG "FAT FS/CVF: This is a bug in cvf_version_use_count\n");
++ }
++ return;
++ }
++ }
++ }
++
++ printk("dec_cvf_format_use_count_by_version: version %d not found ???\n",
++ version);
++}
++
++int detect_cvf(struct super_block*sb,char*force)
++{ int i;
++ int found=0;
++ int found_i=-1;
++
++ if(force)
++ if(strcmp(force,"autoload")==0)
++ {
++#ifdef CONFIG_KERNELD
++ request_module("cvf_autoload");
++ force=NULL;
++#else
++ printk("cannot autoload CVF modules: kerneld support is not compiled into kernel\n");
++ return -1;
++#endif
++ }
++
++#ifdef CONFIG_KERNELD
++ if(force)
++ if(*force)
++ request_module(force);
++#endif
++
++ if(force)
++ { if(*force)
++ { for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(!strcmp(cvf_formats[i]->cvf_version_text,force))
++ return i;
++ }
++ }
++ printk("CVF format %s unknown (module not loaded?)\n",force);
++ return -1;
++ }
++ }
++
++ for(i=0;i<MAX_CVF_FORMATS;++i)
++ { if(cvf_formats[i])
++ { if(cvf_formats[i]->detect_cvf(sb))
++ { ++found;
++ found_i=i;
++ }
++ }
++ }
++
++ if(found==1)return found_i;
++ if(found>1)printk("CVF detection ambiguous, please use cvf_format=xxx option\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/dir.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/dir.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/dir.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/dir.c Sun Jan 4 11:59:56 1998
+@@ -425,6 +425,7 @@
+ unsigned int cmd, unsigned long arg)
+ {
+ int err;
++
+ /*
+ * We want to provide an interface for Samba to be able
+ * to get the short filename for a given long filename.
+@@ -451,6 +452,11 @@
+ vfat_ioctl_fill, NULL, 1, 0, 1);
+ }
+ default:
++ /* forward ioctl to CVF extension */
++ if(MSDOS_SB(inode->i_sb)->cvf_format
++ &&MSDOS_SB(inode->i_sb)->cvf_format->cvf_dir_ioctl)
++ return MSDOS_SB(inode->i_sb)->cvf_format->
++ cvf_dir_ioctl(inode,filp,cmd,arg);
+ return -EINVAL;
+ }
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/fatfs_syms.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/fatfs_syms.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/fatfs_syms.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/fatfs_syms.c Sun Jan 4 12:11:10 1998
+@@ -11,6 +11,7 @@
+
+ #include <linux/mm.h>
+ #include <linux/msdos_fs.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -53,6 +54,13 @@
+ X(fat_uni2esc) X_PUNCT
+ X(fat_unlock_creation) X_PUNCT
+ X(fat_write_inode) X_PUNCT
++X(register_cvf_format) X_PUNCT
++X(unregister_cvf_format) X_PUNCT
++X(get_cluster) X_PUNCT
++X(lock_fat) X_PUNCT
++X(unlock_fat) X_PUNCT
++X(fat_readpage) X_PUNCT
++X(fat_dir_ioctl) X_PUNCT
+ #if LINUX_VERSION_CODE < ASC_LINUX_VERSION(2,1,0)
+ #include <linux/symtab_end.h>
+ };
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/file.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/file.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/file.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/file.c Sun Jan 4 11:59:56 1998
+@@ -17,6 +17,7 @@
+ #include <linux/stat.h>
+ #include <linux/string.h>
+ #include <linux/pagemap.h>
++#include <linux/fat_cvf.h>
+
+ #if LINUX_VERSION_CODE >= ASC_LINUX_VERSION(2,1,0)
+ #include <asm/uaccess.h>
+@@ -121,6 +122,40 @@
+ NULL /* smap */
+ };
+
++static struct file_operations fat_file_operations_readpage = {
++ NULL, /* lseek - default */
++ fat_file_read, /* read */
++ fat_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations fat_file_inode_operations_readpage = {
++ &fat_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ fat_truncate, /* truncate */
++ NULL, /* permission */
++ NULL /* smap */
++};
++
+ #define MSDOS_PREFETCH 32
+ struct fat_pre {
+ int file_sector;/* Next sector to read in the prefetch table */
+@@ -183,6 +218,10 @@
+ printk("fat_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_read)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_read(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_read: mode = %07o\n",inode->i_mode);
+@@ -305,6 +344,10 @@
+ printk("fat_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_file_write)
++ return MSDOS_SB(sb)->cvf_format->cvf_file_write(inode,filp,buf,count);
++
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk("fat_file_write: mode = %07o\n",inode->i_mode);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/inode.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/inode.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/inode.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/inode.c Sun Jan 4 12:31:18 1998
+@@ -22,6 +22,7 @@
+ #include <linux/stat.h>
+ #include <linux/locks.h>
+ #include <linux/malloc.h>
++#include <linux/fat_cvf.h>
+
+ #include "msbuffer.h"
+
+@@ -88,6 +89,10 @@
+
+ void fat_put_super(struct super_block *sb)
+ {
++ if(MSDOS_SB(sb)->cvf_format)
++ { dec_cvf_format_use_count_by_version(MSDOS_SB(sb)->cvf_format->cvf_version);
++ MSDOS_SB(sb)->cvf_format->unmount_cvf(sb);
++ }
+ if (MSDOS_SB(sb)->fat_bits == 32) {
+ fat_clusters_flush(sb);
+ }
+@@ -113,7 +118,8 @@
+
+
+ static int parse_options(char *options,int *fat, int *blksize, int *debug,
+- struct fat_mount_options *opts)
++ struct fat_mount_options *opts,
++ char* cvf_format, char*cvf_options)
+ {
+ char *this_char,*value,save,*savep;
+ char *p;
+@@ -239,6 +245,16 @@
+ ret = 0;
+ }
+ }
++ else if (!strcmp(this_char,"cvf_format")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_format,value,20);
++ }
++ else if (!strcmp(this_char,"cvf_options")) {
++ if (!value)
++ return 0;
++ strncpy(cvf_options,value,100);
++ }
+
+ if (this_char != options) *(this_char-1) = ',';
+ if (value) *savep = save;
+@@ -261,6 +277,14 @@
+ struct fat_mount_options opts;
+ char buf[50];
+ char *p;
++ int i;
++ char cvf_format[21];
++ char cvf_options[101];
++
++ cvf_format[0]='\0';
++ cvf_options[0]='\0';
++ MSDOS_SB(sb)->cvf_format=NULL;
++ MSDOS_SB(sb)->private_data=NULL;
+
+ MOD_INC_USE_COUNT;
+ if (hardsect_size[MAJOR(sb->s_dev)] != NULL){
+@@ -270,7 +294,8 @@
+ }
+ }
+ opts.isvfat = MSDOS_SB(sb)->options.isvfat;
+- if (!parse_options((char *) data, &fat, &blksize, &debug, &opts)
++ if (!parse_options((char *) data, &fat, &blksize, &debug, &opts,
++ cvf_format, cvf_options)
+ || (blksize != 512 && blksize != 1024)) {
+ sb->s_dev = 0;
+ MOD_DEC_USE_COUNT;
+@@ -375,6 +400,9 @@
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
++ if(!strcmp(cvf_format,"none"))i=-1;
++ else i=detect_cvf(sb,cvf_format);
++ if(i>=0)error=cvf_formats[i]->mount_cvf(sb,cvf_options);
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("[MS-DOS FS Rel. 12,FAT %d,check=%c,conv=%c,"
+@@ -392,13 +420,15 @@
+ MSDOS_SB(sb)->root_cluster,MSDOS_SB(sb)->free_clusters);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+- if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
++ if(i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev "
+ "%s.\n", kdevname(sb->s_dev));
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+@@ -419,6 +449,8 @@
+ MSDOS_SB(sb)->nls_disk = load_nls_default();
+ } else {
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
+@@ -433,6 +465,8 @@
+ kfree(opts.iocharset);
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ sb->s_dev = 0;
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ } else {
+@@ -447,9 +481,16 @@
+ unload_nls(MSDOS_SB(sb)->nls_disk);
+ if (MSDOS_SB(sb)->nls_io) unload_nls(MSDOS_SB(sb)->nls_io);
+ if (opts.iocharset) kfree(opts.iocharset);
++ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
++ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return NULL;
+ }
++
++ if(i>=0)
++ { MSDOS_SB(sb)->cvf_format=cvf_formats[i];
++ ++cvf_format_use_count[i];
++ }
+
+ return sb;
+ }
+@@ -459,7 +500,13 @@
+ {
+ int free,nr;
+ struct statfs tmp;
+-
++
++ if(MSDOS_SB(sb)->cvf_format)
++ if(MSDOS_SB(sb)->cvf_format->cvf_statfs)
++ { MSDOS_SB(sb)->cvf_format->cvf_statfs(sb,buf,bufsiz);
++ return;
++ }
++
+ lock_fat(sb);
+ if (MSDOS_SB(sb)->free_clusters != -1)
+ free = MSDOS_SB(sb)->free_clusters;
+@@ -488,6 +535,10 @@
+ int cluster,offset;
+
+ sb = MSDOS_SB(inode->i_sb);
++ if(sb->cvf_format)
++ if(sb->cvf_format->cvf_bmap)
++ return sb->cvf_format->cvf_bmap(inode,block);
++
+ if ((inode->i_ino == MSDOS_ROOT_INO) && (sb->fat_bits != 32)) {
+ return sb->dir_start + block;
+ }
+@@ -600,7 +651,12 @@
+ !is_exec(raw_entry->ext)))
+ ? S_IRUGO|S_IWUGO : S_IRWXUGO)
+ & ~MSDOS_SB(sb)->options.fs_umask) | S_IFREG;
+- inode->i_op = (sb->s_blocksize == 1024)
++ if(MSDOS_SB(sb)->cvf_format)
++ inode->i_op = (MSDOS_SB(sb)->cvf_format->flags&CVF_USE_READPAGE)
++ ? &fat_file_inode_operations_readpage
++ : &fat_file_inode_operations_1024;
++ else
++ inode->i_op = (sb->s_blocksize == 1024)
+ ? &fat_file_inode_operations_1024
+ : &fat_file_inode_operations;
+ MSDOS_I(inode)->i_start = CF_LE_W(raw_entry->start);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/misc.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/misc.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/misc.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/misc.c Sun Jan 4 11:59:56 1998
+@@ -225,6 +225,10 @@
+ #endif
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
++ if(MSDOS_SB(sb)->cvf_format&&
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster)
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
++ else
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/fat/mmap.c linux-2.0.33-cyrix-patched-fat32-test/fs/fat/mmap.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/fat/mmap.c Sun Jan 4 11:55:32 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/fat/mmap.c Sun Jan 4 11:59:56 1998
+@@ -100,6 +100,9 @@
+ */
+ int fat_mmap(struct inode * inode, struct file * file, struct vm_area_struct * vma)
+ {
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_mmap(inode,file,vma);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+@@ -117,4 +120,12 @@
+ return 0;
+ }
+
++int fat_readpage(struct inode * inode, struct page * page)
++{
++ if(MSDOS_SB(inode->i_sb)->cvf_format)
++ if(MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage)
++ return MSDOS_SB(inode->i_sb)->cvf_format->cvf_readpage(inode,page);
+
++ printk("fat_readpage called with no handler (shouldn't happen)\n");
++ return -1;
++}
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/emd.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/emd.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/emd.c Wed Feb 7 08:39:29 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/emd.c Sun Jan 4 11:59:56 1998
+@@ -32,6 +32,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_read(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+@@ -48,6 +49,7 @@
+ int ret;
+ int old_fs = get_fs();
+ set_fs (KERNEL_DS);
++ MSDOS_I(inode)->i_binary=2;
+ ret = fat_file_write(inode,filp,buf,count);
+ set_fs (old_fs);
+ return ret;
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/file.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/file.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/file.c Tue Feb 20 09:28:13 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/file.c Sun Jan 4 11:59:56 1998
+@@ -129,3 +129,38 @@
+ NULL, /* smap */
+ };
+
++/* For other with larger and unaligned file system with readpage */
++struct file_operations umsdos_file_operations_readpage = {
++ NULL, /* lseek - default */
++ UMSDOS_file_read, /* read */
++ UMSDOS_file_write, /* write */
++ NULL, /* readdir - bad */
++ NULL, /* select - default */
++ NULL, /* ioctl - default */
++ generic_file_mmap, /* mmap */
++ NULL, /* no special open is needed */
++ NULL, /* release */
++ file_fsync /* fsync */
++};
++
++struct inode_operations umsdos_file_inode_operations_readpage = {
++ &umsdos_file_operations_readpage, /* default file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ fat_readpage, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ UMSDOS_truncate,/* truncate */
++ NULL, /* permission */
++ NULL, /* smap */
++};
++
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/inode.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/inode.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/inode.c Sat Nov 30 11:21:22 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/inode.c Sun Jan 4 11:59:56 1998
+@@ -149,11 +149,20 @@
+ if (!umsdos_isinit(inode)){
+ inode->u.umsdos_i.i_emd_dir = 0;
+ if (S_ISREG(inode->i_mode)){
++ if (MSDOS_SB(inode->i_sb)->cvf_format){
++ if (MSDOS_SB(inode->i_sb)->cvf_format->flags
++ &CVF_USE_READPAGE){
++ inode->i_op = &umsdos_file_inode_operations_readpage;
++ }else{
++ inode->i_op = &umsdos_file_inode_operations_no_bmap;
++ }
++ } else {
+ if (inode->i_op->bmap != NULL){
+ inode->i_op = &umsdos_file_inode_operations;
+ }else{
+ inode->i_op = &umsdos_file_inode_operations_no_bmap;
+ }
++ }
+ }else if (S_ISDIR(inode->i_mode)){
+ if (dir != NULL){
+ umsdos_setup_dir_inode(inode);
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/fs/umsdos/ioctl.c linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/ioctl.c
+--- linux-2.0.33-cyrix-patched-fat32/fs/umsdos/ioctl.c Wed Jul 3 15:39:48 1996
++++ linux-2.0.33-cyrix-patched-fat32-test/fs/umsdos/ioctl.c Sun Jan 4 11:59:56 1998
+@@ -60,6 +60,21 @@
+ {
+ int ret = -EPERM;
+ int err;
++
++ /* forward non-umsdos ioctls - this hopefully doesn't cause conflicts */
++ if(cmd!=UMSDOS_GETVERSION
++ &&cmd!=UMSDOS_READDIR_DOS
++ &&cmd!=UMSDOS_READDIR_EMD
++ &&cmd!=UMSDOS_INIT_EMD
++ &&cmd!=UMSDOS_CREAT_EMD
++ &&cmd!=UMSDOS_RENAME_DOS
++ &&cmd!=UMSDOS_UNLINK_EMD
++ &&cmd!=UMSDOS_UNLINK_DOS
++ &&cmd!=UMSDOS_RMDIR_DOS
++ &&cmd!=UMSDOS_STAT_DOS
++ &&cmd!=UMSDOS_DOS_SETUP)
++ return fat_dir_ioctl(dir,filp,cmd,data);
++
+ /* #Specification: ioctl / acces
+ Only root (effective id) is allowed to do IOCTL on directory
+ in UMSDOS. EPERM is returned for other user.
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/fat_cvf.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/fat_cvf.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/fat_cvf.h Thu Jan 1 01:00:00 1970
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/fat_cvf.h Sun Jan 4 11:59:56 1998
+@@ -0,0 +1,55 @@
++#ifndef _FAT_CVF
++#define _FAT_CVF
++
++#define CVF_USE_READPAGE 0x0001
++
++struct cvf_format
++{ int cvf_version;
++ char* cvf_version_text;
++ unsigned long flags;
++ int (*detect_cvf) (struct super_block*sb);
++ int (*mount_cvf) (struct super_block*sb,char*options);
++ int (*unmount_cvf) (struct super_block*sb);
++ struct buffer_head* (*cvf_bread) (struct super_block*sb,int block);
++ struct buffer_head* (*cvf_getblk) (struct super_block*sb,int block);
++ void (*cvf_brelse) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_mark_buffer_dirty) (struct super_block *sb,
++ struct buffer_head *bh,
++ int dirty_val);
++ void (*cvf_set_uptodate) (struct super_block *sb,
++ struct buffer_head *bh,
++ int val);
++ int (*cvf_is_uptodate) (struct super_block *sb,struct buffer_head *bh);
++ void (*cvf_ll_rw_block) (struct super_block *sb,
++ int opr,
++ int nbreq,
++ struct buffer_head *bh[32]);
++ int (*fat_access) (struct super_block *sb,int nr,int new_value);
++ void (*cvf_statfs) (struct super_block *sb,struct statfs *buf, int bufsiz);
++ int (*cvf_bmap) (struct inode *inode,int block);
++ int (*cvf_smap) (struct inode *inode,int sector);
++ int (*cvf_file_read) ( struct inode *inode,
++ struct file *filp,
++ char *buf,
++ int count);
++ int (*cvf_file_write) ( struct inode *inode,
++ struct file *filp,
++ const char *buf,
++ int count);
++ int (*cvf_mmap) (struct inode*, struct file *, struct vm_area_struct *);
++ int (*cvf_readpage) (struct inode *, struct page *);
++ int (*cvf_writepage) (struct inode *, struct page *);
++ int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
++ unsigned int cmd, unsigned long arg);
++ void (*zero_out_cluster) (struct inode*, int clusternr);
++};
++
++int register_cvf_format(struct cvf_format*cvf_format);
++int unregister_cvf_format(struct cvf_format*cvf_format);
++void dec_cvf_format_use_count_by_version(int version);
++int detect_cvf(struct super_block*sb,char*force);
++
++extern struct cvf_format *cvf_formats[];
++extern int cvf_format_use_count[];
++
++#endif
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs.h Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs.h Sun Jan 4 12:40:48 1998
+@@ -246,12 +246,14 @@
+ /* file.c */
+ extern struct inode_operations fat_file_inode_operations;
+ extern struct inode_operations fat_file_inode_operations_1024;
++extern struct inode_operations fat_file_inode_operations_readpage;
+ extern int fat_file_read(struct inode *, struct file *, char *, int);
+ extern int fat_file_write(struct inode *, struct file *, const char *, int);
+ extern void fat_truncate(struct inode *inode);
+
+ /* mmap.c */
+ extern int fat_mmap(struct inode *, struct file *, struct vm_area_struct *);
++extern int fat_readpage(struct inode *, struct page *);
+
+
+ /* vfat.c */
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs_sb.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs_sb.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/msdos_fs_sb.h Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/msdos_fs_sb.h Sun Jan 4 12:02:05 1998
+@@ -1,5 +1,6 @@
+ #ifndef _MSDOS_FS_SB
+ #define _MSDOS_FS_SB
++#include<linux/fat_cvf.h>
+
+ /*
+ * MS-DOS file system in-core superblock data
+@@ -46,6 +47,8 @@
+ struct fat_mount_options options;
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
++ struct cvf_format* cvf_format;
++ void* private_data;
+ };
+
+ #endif
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/include/linux/umsdos_fs.h linux-2.0.33-cyrix-patched-fat32-test/include/linux/umsdos_fs.h
+--- linux-2.0.33-cyrix-patched-fat32/include/linux/umsdos_fs.h Tue Dec 23 22:50:14 1997
++++ linux-2.0.33-cyrix-patched-fat32-test/include/linux/umsdos_fs.h Sun Jan 4 12:40:48 1998
+@@ -135,6 +135,7 @@
+ extern struct file_operations umsdos_file_operations;
+ extern struct inode_operations umsdos_file_inode_operations;
+ extern struct inode_operations umsdos_file_inode_operations_no_bmap;
++extern struct inode_operations umsdos_file_inode_operations_readpage;
+ extern struct inode_operations umsdos_symlink_inode_operations;
+ extern int init_umsdos_fs(void);
+
+diff -u -r -N linux-2.0.33-cyrix-patched-fat32/kernel/ksyms.c linux-2.0.33-cyrix-patched-fat32-test/kernel/ksyms.c
+--- linux-2.0.33-cyrix-patched-fat32/kernel/ksyms.c Sun Jan 4 11:55:33 1998
++++ linux-2.0.33-cyrix-patched-fat32-test/kernel/ksyms.c Sun Jan 4 11:59:56 1998
+@@ -124,6 +124,9 @@
+ X(do_mmap),
+ X(do_munmap),
+ X(exit_mm),
++ X(exit_sighand),
++ X(exit_fs),
++ X(exit_files),
+
+ /* internal kernel memory management */
+ X(__get_free_pages),
diff --git a/patches/fat-truncate-bugfix.diff b/patches/fat-truncate-bugfix.diff
new file mode 100644
index 0000000..f4967d9
--- /dev/null
+++ b/patches/fat-truncate-bugfix.diff
@@ -0,0 +1,13 @@
+--- linux/fs/fat/file.c.orig Thu Jan 7 18:38:59 1999
++++ linux/fs/fat/file.c Thu Jan 7 19:17:40 1999
+@@ -442,6 +442,10 @@
+ /* Why no return value? Surely the disk could fail... */
+ if (IS_IMMUTABLE(inode))
+ return /* -EPERM */;
++ if(inode->i_sb->s_flags&MS_RDONLY) {
++ printk("FAT: fat_truncate called though fs is read-only, uhh...\n");
++ return /* -EROFS */;
++ }
+ cluster = SECTOR_SIZE*MSDOS_SB(inode->i_sb)->cluster_size;
+ (void) fat_free(inode,(inode->i_size+(cluster-1))/cluster);
+ MSDOS_I(inode)->i_attrs |= ATTR_ARCH;
diff --git a/patches/fat_cvf.txt b/patches/fat_cvf.txt
new file mode 100644
index 0000000..4ddcf3e
--- /dev/null
+++ b/patches/fat_cvf.txt
@@ -0,0 +1,210 @@
+This is the main documentation for the CVF-FAT filesystem extension. 18Nov1998
+
+
+Table of Contents:
+
+1. The idea of CVF-FAT
+2. Restrictions
+3. Mount options
+4. Description of the CVF-FAT interface
+5. CVF Modules
+
+------------------------------------------------------------------------------
+
+
+1. The idea of CVF-FAT
+------------------------------------------------------------------------------
+
+CVF-FAT is a FAT filesystem extension that provides a generic interface for
+Compressed Volume Files in FAT partitions. Popular CVF software, for
+example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
+Using the CVF-FAT interface, it is possible to load a module that handles
+all the low-level disk access that has to do with on-the-fly compression
+and decompression. Any other part of FAT filesystem access is still handled
+by the FAT, MSDOS or VFAT or even UMSDOS driver.
+
+CVF access works by redirecting certain low-level routines from the FAT
+driver to a loadable, CVF-format specific module. This module must fake
+a normal FAT filesystem to the FAT driver while doing all the extra stuff
+like compression and decompression silently.
+
+
+2. Restrictions
+------------------------------------------------------------------------------
+
+- BMAP problems
+
+ CVF filesystems cannot do bmap. It's impossible in principle. Thus
+ all actions that require bmap do not work (swapping, writable mmapping).
+ Read-only mmapping works because the FAT driver has a hack for this
+ situation :) Well, writable mmapping should now work using the readpage
+ interface function which has been hacked into the FAT driver just for
+ CVF-FAT :)
+
+- attention, DOSEmu users
+
+ You may have to unmount all CVF partitions before running DOSEmu depending
+ on your configuration. If DOSEmu is configured to use wholedisk or
+ partition access (this is often the case to let DOSEmu access
+ compressed partitions) there's a risk of destroying your compressed
+ partitions or crashing your system because of confused drivers.
+
+ Note that it is always safe to redirect the compressed partitions with
+ lredir or emufs.sys. Refer to the DOSEmu documentation for details.
+
+
+3. Mount options
+------------------------------------------------------------------------------
+
+The CVF-FAT extension currently adds the following options to the FAT
+driver's standard options:
+
+ cvf_format=xxx
+ Forces the driver to use the CVF module "xxx" instead of auto-detection.
+ Without this option, the CVF-FAT interface asks all currently loaded
+ CVF modules whether they recognize the CVF. Therefore, this option is
+ only necessary if the CVF format is not recognized correctly
+ because of bugs or incompatibilities in the CVF modules. (It skips
+ the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
+ to inhibit using any of the loaded CVF modules, just in case a CVF
+ module insists on mounting plain FAT filesystems by misunderstanding.
+ "xxx" may also be the text "autoload", which has a special meaning for
+ kerneld, but does not skip auto-detection.
+
+ If the kernel supports kerneld, the cvf_format=xxx option also controls
+ on-demand CVF module loading. Without this option, nothing is loaded
+ on demand. With cvf_format=xxx, a module "xxx" is requested automatically
+ before mounting the compressed filesystem (unless "xxx" is "none"). In
+ case there is a difference between the CVF format name and the module
+ name, setup aliases in your kerneld configuration. If the string "xxx"
+ is "autoload", a non-existent module "cvf_autoload" is requested which
+ can be used together with a special kerneld configuration (alias and
+ pre-install statements) in order to load more than one CVF module, let
+ them detect automatically which kind of CVF is to be mounted, and only
+ keep the "right" module in memory. For examples please refer to the
+ dmsdos documentation (ftp and http addresses see below).
+
+ cvf_options=yyy
+ Option string passed to the CVF module. I.e. only the "yyy" is passed
+ (without the quotes). The documentation for each CVF module should
+ explain it since it is interpreted only by the CVF module. Note that
+ the string must not contain a comma (",") - this would lead to
+ misinterpretation by the FAT driver, which would recognize the text
+ after a comma as a FAT driver option and might get confused or print
+ strange error messages. The documentation for the CVF module should
+ offer a different separation symbol, for example the dot "." or the
+ plus sign "+", which is only valid inside the string "yyy".
+
+
+4. Description of the CVF-FAT interface
+------------------------------------------------------------------------------
+
+Assuming you want to write your own CVF module, you need to write a lot of
+interface functions. Most of them are covered in the kernel documentation
+you can find on the net, and thus won't be described here. They have been
+marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
+
+struct cvf_format
+{ int cvf_version;
+ char* cvf_version_text;
+ unsigned long int flags;
+ int (*detect_cvf) (struct super_block*sb);
+ int (*mount_cvf) (struct super_block*sb,char*options);
+ int (*unmount_cvf) (struct super_block*sb);
+ [...]
+ void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
+}
+
+This structure defines the capabilities of a CVF module. It must be filled
+out completely by a CVF module. Consider it as a kind of form that is used
+to introduce the module to the FAT/CVF-FAT driver.
+
+It contains...
+ - cvf_version:
+ A version id which must be unique. Choose one.
+ - cvf_version_text:
+ A human readable version string that should be one short word
+ describing the CVF format the module implements. This text is used
+ for the cvf_format option. This name must also be unique.
+ - flags:
+ Bit coded flags, currently only used for a readpage/mmap hack that
+ provides both mmap and readpage functionality. If CVF_USE_READPAGE
+ is set, mmap is set to generic_file_mmap and readpage is caught
+ and redirected to the cvf_readpage function. If it is not set,
+ readpage is set to generic_readpage and mmap is caught and redirected
+ to cvf_mmap. (If you want writable mmap use the readpage interface.)
+ - detect_cvf:
+ A function that is called to decide whether the filesystem is a CVF of
+ the type the module supports. The detect_cvf function must return 0
+ for "NO, I DON'T KNOW THIS GARBAGE" or anything >0 for "YES, THIS IS
+ THE KIND OF CVF I SUPPORT". The function must maintain the module
+ usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
+ and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
+ successful recongition would lead to a call of the mount_cvf function
+ later.
+ - mount_cvf:
+ A function that sets up some values or initializes something additional
+ to what has to be done when a CVF is mounted. This is called at the
+ end of fat_read_super and must return 0 on success. Definitely, this
+ function must increment the module usage counter by MOD_INC_USE_COUNT.
+ This mount_cvf function is also responsible for interpreting a CVF
+ module specific option string (the "yyy" from the FAT mount option
+ "cvf_options=yyy") which cannot contain a comma (use for example the
+ dot "." as option separator symbol).
+ - unmount_cvf:
+ A function that is called when the filesystem is unmounted. Most likely
+ it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
+ value might be ignored (it currently is ignored).
+ - [...]:
+ All other interface functions are "caught" FAT driver functions, i.e.
+ are executed by the FAT driver *instead* of the original FAT driver
+ functions. NULL means use the original FAT driver functions instead.
+ If you really want "no action", write a function that does nothing and
+ hang it in instead.
+ - cvf_zero_cluster:
+ The cvf_zero_cluster function is called when the fat driver wants to
+ zero out a (new) cluster. This is important for directories (mkdir).
+ If it is NULL, the FAT driver defaults to overwriting the whole
+ cluster with zeros. Note that clusternr is absolute, not relative
+ to the provided inode.
+
+Notes:
+ 1. The cvf_bmap function should be ignored. It really should never
+ get called from somewhere. I recommend redirecting it to a panic
+ or fatal error message so bugs show up immediately.
+ 2. The cvf_writepage function is ignored. This is because the fat
+ driver doesn't support it. This might change in future. I recommend
+ setting it to NULL (i.e use default).
+
+int register_cvf_format(struct cvf_format*cvf_format);
+ If you have just set up a variable containing the above structure,
+ call this function to introduce your CVF format to the FAT/CVF-FAT
+ driver. This is usually done in init_module. Be sure to check the
+ return value. Zero means success, everything else causes a kernel
+ message printed in the syslog describing the error that occurred.
+ Typical errors are:
+ - a module with the same version id is already registered or
+ - too many CVF formats. Hack fs/fat/cvf.c if you need more.
+
+int unregister_cvf_format(struct cvf_format*cvf_format);
+ This is usually called in cleanup_module. Return value =0 means
+ success. An error only occurs if you try to unregister a CVF format
+ that has not been previously registered. The code uses the version id
+ to distinguish the modules, so be sure to keep it unique.
+
+5. CVF Modules
+------------------------------------------------------------------------------
+
+Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
+sample implementation. It can currently be found at
+
+ ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos/dmsdos-x.y.z.tgz
+ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/dmsdos-x.y.z.tgz
+ ftp://ftp.uni-stuttgart.de/pub/systems/linux/local/system/dmsdos-x.y.z.tgz
+
+(where x.y.z is to be replaced with the actual version number). Full
+documentation about dmsdos is included in the dmsdos package, but can also
+be found at
+
+ http://fb9nt.uni-duisburg.de/mitarbeiter/gockel/software/dmsdos/index.html
+ http://www.yk.rim.or.jp/~takafumi/dmsdos/index.html (in Japanese).
diff --git a/patches/fat_cvf.txt-2.1.128 b/patches/fat_cvf.txt-2.1.128
new file mode 100644
index 0000000..c1cf27b
--- /dev/null
+++ b/patches/fat_cvf.txt-2.1.128
@@ -0,0 +1,210 @@
+This is the main documentation for the CVF-FAT filesystem extension. 18Nov1998
+
+
+Table of Contents:
+
+1. The idea of CVF-FAT
+2. Restrictions
+3. Mount options
+4. Description of the CVF-FAT interface
+5. CVF Modules
+
+------------------------------------------------------------------------------
+
+
+1. The idea of CVF-FAT
+------------------------------------------------------------------------------
+
+CVF-FAT is a FAT filesystem extension that provides a generic interface for
+Compressed Volume Files in FAT partitions. Popular CVF software, for
+example, are Microsoft's Doublespace/Drivespace and Stac's Stacker.
+Using the CVF-FAT interface, it is possible to load a module that handles
+all the low-level disk access that has to do with on-the-fly compression
+and decompression. Any other part of FAT filesystem access is still handled
+by the FAT, MSDOS or VFAT or even UMSDOS driver.
+
+CVF access works by redirecting certain low-level routines from the FAT
+driver to a loadable, CVF-format specific module. This module must fake
+a normal FAT filesystem to the FAT driver while doing all the extra stuff
+like compression and decompression silently.
+
+
+2. Restrictions
+------------------------------------------------------------------------------
+
+- BMAP problems
+
+ CVF filesystems cannot do bmap. It's impossible in principle. Thus
+ all actions that require bmap do not work (swapping, writable mmapping).
+ Read-only mmapping works because the FAT driver has a hack for this
+ situation :) Well, writable mmapping should now work using the readpage
+ interface function which has been hacked into the FAT driver just for
+ CVF-FAT :)
+
+- attention, DOSEmu users
+
+ You may have to unmount all CVF partitions before running DOSEmu depending
+ on your configuration. If DOSEmu is configured to use wholedisk or
+ partition access (this is often the case to let DOSEmu access
+ compressed partitions) there's a risk of destroying your compressed
+ partitions or crashing your system because of confused drivers.
+
+ Note that it is always safe to redirect the compressed partitions with
+ lredir or emufs.sys. Refer to the DOSEmu documentation for details.
+
+
+3. Mount options
+------------------------------------------------------------------------------
+
+The CVF-FAT extension currently adds the following options to the FAT
+driver's standard options:
+
+ cvf_format=xxx
+ Forces the driver to use the CVF module "xxx" instead of auto-detection.
+ Without this option, the CVF-FAT interface asks all currently loaded
+ CVF modules whether they recognize the CVF. Therefore, this option is
+ only necessary if the CVF format is not recognized correctly
+ because of bugs or incompatibilities in the CVF modules. (It skips
+ the detect_cvf call.) "xxx" may be the text "none" (without the quotes)
+ to inhibit using any of the loaded CVF modules, just in case a CVF
+ module insists on mounting plain FAT filesystems by misunderstanding.
+ "xxx" may also be the text "autoload", which has a special meaning for
+ a module loader, but does not skip auto-detection.
+
+ If the kernel supports kmod, the cvf_format=xxx option also controls
+ on-demand CVF module loading. Without this option, nothing is loaded
+ on demand. With cvf_format=xxx, a module "xxx" is requested automatically
+ before mounting the compressed filesystem (unless "xxx" is "none"). In
+ case there is a difference between the CVF format name and the module
+ name, setup aliases in your modules configuration. If the string "xxx"
+ is "autoload", a non-existent module "cvf_autoload" is requested which
+ can be used together with a special modules configuration (alias and
+ pre-install statements) in order to load more than one CVF module, let
+ them detect automatically which kind of CVF is to be mounted, and only
+ keep the "right" module in memory. For examples please refer to the
+ dmsdos documentation (ftp and http addresses see below).
+
+ cvf_options=yyy
+ Option string passed to the CVF module. I.e. only the "yyy" is passed
+ (without the quotes). The documentation for each CVF module should
+ explain it since it is interpreted only by the CVF module. Note that
+ the string must not contain a comma (",") - this would lead to
+ misinterpretation by the FAT driver, which would recognize the text
+ after a comma as a FAT driver option and might get confused or print
+ strange error messages. The documentation for the CVF module should
+ offer a different separation symbol, for example the dot "." or the
+ plus sign "+", which is only valid inside the string "yyy".
+
+
+4. Description of the CVF-FAT interface
+------------------------------------------------------------------------------
+
+Assuming you want to write your own CVF module, you need to write a lot of
+interface functions. Most of them are covered in the kernel documentation
+you can find on the net, and thus won't be described here. They have been
+marked with "[...]" :-) Take a look at include/linux/fat_cvf.h.
+
+struct cvf_format
+{ int cvf_version;
+ char* cvf_version_text;
+ unsigned long int flags;
+ int (*detect_cvf) (struct super_block*sb);
+ int (*mount_cvf) (struct super_block*sb,char*options);
+ int (*unmount_cvf) (struct super_block*sb);
+ [...]
+ void (*cvf_zero_cluster) (struct inode*inode,int clusternr);
+}
+
+This structure defines the capabilities of a CVF module. It must be filled
+out completely by a CVF module. Consider it as a kind of form that is used
+to introduce the module to the FAT/CVF-FAT driver.
+
+It contains...
+ - cvf_version:
+ A version id which must be unique. Choose one.
+ - cvf_version_text:
+ A human readable version string that should be one short word
+ describing the CVF format the module implements. This text is used
+ for the cvf_format option. This name must also be unique.
+ - flags:
+ Bit coded flags, currently only used for a readpage/mmap hack that
+ provides both mmap and readpage functionality. If CVF_USE_READPAGE
+ is set, mmap is set to generic_file_mmap and readpage is caught
+ and redirected to the cvf_readpage function. If it is not set,
+ readpage is set to generic_readpage and mmap is caught and redirected
+ to cvf_mmap. (If you want writable mmap use the readpage interface.)
+ - detect_cvf:
+ A function that is called to decide whether the filesystem is a CVF of
+ the type the module supports. The detect_cvf function must return 0
+ for "NO, I DON'T KNOW THIS GARBAGE" or anything >0 for "YES, THIS IS
+ THE KIND OF CVF I SUPPORT". The function must maintain the module
+ usage counters for safety, i.e. do MOD_INC_USE_COUNT at the beginning
+ and MOD_DEC_USE_COUNT at the end. The function *must not* assume that
+ successful recongition would lead to a call of the mount_cvf function
+ later.
+ - mount_cvf:
+ A function that sets up some values or initializes something additional
+ to what has to be done when a CVF is mounted. This is called at the
+ end of fat_read_super and must return 0 on success. Definitely, this
+ function must increment the module usage counter by MOD_INC_USE_COUNT.
+ This mount_cvf function is also responsible for interpreting a CVF
+ module specific option string (the "yyy" from the FAT mount option
+ "cvf_options=yyy") which cannot contain a comma (use for example the
+ dot "." as option separator symbol).
+ - unmount_cvf:
+ A function that is called when the filesystem is unmounted. Most likely
+ it only frees up some memory and calls MOD_DEC_USE_COUNT. The return
+ value might be ignored (it currently is ignored).
+ - [...]:
+ All other interface functions are "caught" FAT driver functions, i.e.
+ are executed by the FAT driver *instead* of the original FAT driver
+ functions. NULL means use the original FAT driver functions instead.
+ If you really want "no action", write a function that does nothing and
+ hang it in instead.
+ - cvf_zero_cluster:
+ The cvf_zero_cluster function is called when the fat driver wants to
+ zero out a (new) cluster. This is important for directories (mkdir).
+ If it is NULL, the FAT driver defaults to overwriting the whole
+ cluster with zeros. Note that clusternr is absolute, not relative
+ to the provided inode.
+
+Notes:
+ 1. The cvf_bmap function should be ignored. It really should never
+ get called from somewhere. I recommend redirecting it to a panic
+ or fatal error message so bugs show up immediately.
+ 2. The cvf_writepage function is ignored. This is because the fat
+ driver doesn't support it. This might change in future. I recommend
+ setting it to NULL (i.e use default).
+
+int register_cvf_format(struct cvf_format*cvf_format);
+ If you have just set up a variable containing the above structure,
+ call this function to introduce your CVF format to the FAT/CVF-FAT
+ driver. This is usually done in init_module. Be sure to check the
+ return value. Zero means success, everything else causes a kernel
+ message printed in the syslog describing the error that occurred.
+ Typical errors are:
+ - a module with the same version id is already registered or
+ - too many CVF formats. Hack fs/fat/cvf.c if you need more.
+
+int unregister_cvf_format(struct cvf_format*cvf_format);
+ This is usually called in cleanup_module. Return value =0 means
+ success. An error only occurs if you try to unregister a CVF format
+ that has not been previously registered. The code uses the version id
+ to distinguish the modules, so be sure to keep it unique.
+
+5. CVF Modules
+------------------------------------------------------------------------------
+
+Refer to the dmsdos module (the successor of the dmsdos filesystem) for a
+sample implementation. It can currently be found at
+
+ ftp://fb9nt.uni-duisburg.de/pub/linux/dmsdos/dmsdos-x.y.z.tgz
+ ftp://sunsite.unc.edu/pub/Linux/system/Filesystems/dosfs/dmsdos-x.y.z.tgz
+ ftp://ftp.uni-stuttgart.de/pub/systems/linux/local/system/dmsdos-x.y.z.tgz
+
+(where x.y.z is to be replaced with the actual version number). Full
+documentation about dmsdos is included in the dmsdos package, but can also
+be found at
+
+ http://fb9nt.uni-duisburg.de/mitarbeiter/gockel/software/dmsdos/index.html
+ http://www.yk.rim.or.jp/~takafumi/dmsdos/index.html (in Japanese).
diff --git a/patches/in-kernel-2.0.35.diff b/patches/in-kernel-2.0.35.diff
new file mode 100644
index 0000000..5bfbee2
--- /dev/null
+++ b/patches/in-kernel-2.0.35.diff
@@ -0,0 +1,62 @@
+--- linux-orig/fs/Makefile Thu Jun 11 14:23:31 1998
++++ linux/fs/Makefile Sun Aug 23 14:23:39 1998
+@@ -18,7 +18,7 @@
+
+ MOD_LIST_NAME := FS_MODULES
+ ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
+- hpfs sysv smbfs ncpfs ufs affs autofs
++ hpfs sysv smbfs ncpfs ufs affs autofs dmsdos
+
+ ifeq ($(CONFIG_QUOTA),y)
+ O_OBJS += dquot.o
+@@ -55,6 +55,14 @@
+ else
+ ifeq ($(CONFIG_FAT_FS),m)
+ MOD_SUB_DIRS += fat
++ endif
++endif
++
++ifeq ($(CONFIG_DMSDOS_FS),y)
++SUB_DIRS += dmsdos
++else
++ ifeq ($(CONFIG_DMSDOS_FS),m)
++ MOD_SUB_DIRS += dmsdos
+ endif
+ endif
+
+--- linux-orig/fs/Config.in Sat Jul 18 00:31:37 1998
++++ linux/fs/Config.in Sun Aug 23 14:33:10 1998
+@@ -16,6 +16,10 @@
+
+ # msdos filesystems
+ dep_tristate 'DOS FAT fs support' CONFIG_FAT_FS $CONFIG_NLS
++ dep_tristate 'DMSDOS (dblspace/drvspace/stacker) support' CONFIG_DMSDOS_FS $CONFIG_FAT_FS
++ if [ "$CONFIG_DMSDOS_FS" = "y" -o "$CONFIG_DMSDOS_FS" = "m" ]; then
++ source fs/dmsdos/Config.in
++ fi
+ dep_tristate 'MSDOS fs support' CONFIG_MSDOS_FS $CONFIG_FAT_FS
+ dep_tristate 'umsdos: Unix like fs on top of std MSDOS FAT fs' CONFIG_UMSDOS_FS $CONFIG_MSDOS_FS
+ dep_tristate 'VFAT (Windows-95) fs support' CONFIG_VFAT_FS $CONFIG_FAT_FS
+--- linux-orig/fs/filesystems.c Thu Jun 11 14:23:32 1998
++++ linux/fs/filesystems.c Sun Aug 23 14:54:27 1998
+@@ -32,6 +32,9 @@
+
+ extern void device_setup(void);
+ extern void binfmt_setup(void);
++#ifdef CONFIG_DMSDOS_FS
++extern int init_dmsdos(void);
++#endif
+
+ /* This may be used only once, enforced by 'static int callable' */
+ asmlinkage int sys_setup(void)
+@@ -72,6 +75,10 @@
+
+ #ifdef CONFIG_FAT_FS
+ init_fat_fs();
++#endif
++
++#ifdef CONFIG_DMSDOS_FS
++ init_dmsdos();
+ #endif
+
+ #ifdef CONFIG_MSDOS_FS
diff --git a/patches/linux-2.2.13-dmsdos.patch-1 b/patches/linux-2.2.13-dmsdos.patch-1
new file mode 100644
index 0000000..e126359
--- /dev/null
+++ b/patches/linux-2.2.13-dmsdos.patch-1
@@ -0,0 +1,16 @@
+diff -u -r -r linux-2.2.13-orig/fs/fat/misc.c linux-2.2.13-new1/fs/fat/misc.c
+--- linux-2.2.13-orig/fs/fat/misc.c Mon Aug 9 19:04:41 1999
++++ linux-2.2.13-new1/fs/fat/misc.c Sun Dec 5 11:43:02 1999
+@@ -222,9 +222,10 @@
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
+ if (MSDOS_SB(sb)->cvf_format &&
+- MSDOS_SB(sb)->cvf_format->zero_out_cluster)
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster) {
+ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
+- else
++ res=fat_bread(sb,fat_smap(inode,inode->i_blocks));
++ }else
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
diff --git a/patches/linux-2.3.30-pre6-dmsdos.patch-1 b/patches/linux-2.3.30-pre6-dmsdos.patch-1
new file mode 100644
index 0000000..bc4757b
--- /dev/null
+++ b/patches/linux-2.3.30-pre6-dmsdos.patch-1
@@ -0,0 +1,284 @@
+diff -u -r linux-2.3.30-6-orig/fs/fat/cvf.c linux-2.3.30-6-new1/fs/fat/cvf.c
+--- linux-2.3.30-6-orig/fs/fat/cvf.c Mon Aug 9 18:43:49 1999
++++ linux-2.3.30-6-new1/fs/fat/cvf.c Wed Nov 3 00:28:53 1999
+@@ -53,49 +53,55 @@
+ loff_t *ppos);
+
+ struct cvf_format default_cvf = {
+- 0, /* version - who cares? */
+- "plain",
+- 0, /* flags - who cares? */
+- NULL,
+- NULL,
+- NULL,
+- default_fat_bread,
+- default_fat_getblk,
+- default_fat_brelse,
+- default_fat_mark_buffer_dirty,
+- default_fat_set_uptodate,
+- default_fat_is_uptodate,
+- default_fat_ll_rw_block,
+- default_fat_access,
+- NULL,
+- default_fat_bmap,
+- generic_file_read,
+- default_fat_file_write,
+- NULL,
+- NULL
++ 0, /* cvf unique version number - 0 for fat internal CVF */
++ "plain", /* cvf version name */
++ 0, /* flags - capabilities, ex. CVF_USE_READPAGE */
++ NULL, /* detect_cvf */
++ NULL, /* mount_cvf */
++ NULL, /* unmount_cvf */
++ default_fat_bread, /* cvf_bread */
++ default_fat_getblk, /* cvf_getblk */
++ default_fat_brelse, /* cvf_brelse */
++ default_fat_mark_buffer_dirty, /* cvf_mark_buffer_dirty */
++ default_fat_set_uptodate, /* cvf_set_uptodate */
++ default_fat_is_uptodate, /* cvf_is_uptodate */
++ default_fat_ll_rw_block, /* cvf_ll_rw_block */
++ default_fat_access, /* fat_access */
++ NULL, /* cvf_statfs */
++ default_fat_bmap, /* cvf_bmap */
++ generic_file_read, /* cvf_file_read */
++ default_fat_file_write, /* cvf_file_write */
++ NULL, /* cvf_mmap */
++ block_read_full_page, /* cvf_readpage */
++ NULL, /* cvf_writepage */
++ NULL, /* cvf_dir_ioctl */
++ NULL /* zero_out_cluster */
+ };
+
+ struct cvf_format bigblock_cvf = {
+- 0, /* version - who cares? */
+- "big_blocks",
+- 0, /* flags - who cares? */
+- NULL,
+- NULL,
+- NULL,
+- bigblock_fat_bread,
+- bigblock_fat_bread,
+- bigblock_fat_brelse,
+- bigblock_fat_mark_buffer_dirty,
+- bigblock_fat_set_uptodate,
+- bigblock_fat_is_uptodate,
+- bigblock_fat_ll_rw_block,
+- default_fat_access,
+- NULL,
+- default_fat_bmap,
+- NULL,
+- default_fat_file_write,
+- NULL,
+- NULL
++ 0, /* cvf unique version number - 0 for fat internal CVF */
++ "big_blocks", /* cvf version name */
++ 0, /* flags - capabilities, ex. CVF_USE_READPAGE */
++ NULL, /* detect_cvf */
++ NULL, /* mount_cvf */
++ NULL, /* unmount_cvf */
++ bigblock_fat_bread, /* cvf_bread */
++ bigblock_fat_bread, /* cvf_getblk */
++ bigblock_fat_brelse, /* cvf_brelse */
++ bigblock_fat_mark_buffer_dirty, /* cvf_mark_buffer_dirty */
++ bigblock_fat_set_uptodate, /* cvf_set_uptodate */
++ bigblock_fat_is_uptodate, /* cvf_is_uptodate */
++ bigblock_fat_ll_rw_block, /* cvf_ll_rw_block */
++ default_fat_access, /* fat_access */
++ NULL, /* cvf_statfs */
++ default_fat_bmap, /* cvf_bmap */
++ NULL, /* ?????? */ /* cvf_file_read */
++ default_fat_file_write, /* cvf_file_write */
++ NULL, /* cvf_mmap */
++ block_read_full_page, /* cvf_readpage */
++ NULL, /* cvf_writepage */
++ NULL, /* cvf_dir_ioctl */
++ NULL /* zero_out_cluster */
+ };
+
+ struct cvf_format *cvf_formats[MAX_CVF_FORMATS]={NULL,NULL,NULL};
+diff -u -r linux-2.3.30-6-orig/fs/fat/fatfs_syms.c linux-2.3.30-6-new1/fs/fat/fatfs_syms.c
+--- linux-2.3.30-6-orig/fs/fat/fatfs_syms.c Tue Oct 26 18:21:46 1999
++++ linux-2.3.30-6-new1/fs/fat/fatfs_syms.c Mon Nov 1 22:29:25 1999
+@@ -17,6 +17,7 @@
+
+ extern struct file_operations fat_dir_operations;
+
++EXPORT_SYMBOL(fat_add_cluster);
+ EXPORT_SYMBOL(fat_new_dir);
+ EXPORT_SYMBOL(fat_bmap);
+ EXPORT_SYMBOL(fat_get_block);
+@@ -55,6 +56,12 @@
+ EXPORT_SYMBOL(fat_dir_ioctl);
+ EXPORT_SYMBOL(fat_add_entries);
+ EXPORT_SYMBOL(fat_dir_empty);
++
++#if 0 /* will be reenabled again for CVF mmap support */
++EXPORT_SYMBOL(fat_mmap);
++EXPORT_SYMBOL(fat_readpage);
++EXPORT_SYMBOL(fat_writepage);
++#endif
+
+ int init_fat_fs(void)
+ {
+diff -u -r linux-2.3.30-6-orig/fs/fat/inode.c linux-2.3.30-6-new1/fs/fat/inode.c
+--- linux-2.3.30-6-orig/fs/fat/inode.c Sat Nov 6 00:27:28 1999
++++ linux-2.3.30-6-new1/fs/fat/inode.c Sun Dec 5 01:16:44 1999
+@@ -434,12 +434,15 @@
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ char *p;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+- int debug,error,fat,cp;
++ int debug;
++ int error = 0;
++ int fat,cp;
+ int blksize = 512;
+ int fat32;
+ struct fat_mount_options opts;
+ char buf[50];
+- int i;
++ int cvf_index=-1;
++ /* int i; */
+ char cvf_format[21];
+ char cvf_options[101];
+
+@@ -514,14 +517,15 @@
+ sbi->cluster_size = b->cluster_size*sector_mult;
+ if (!sbi->cluster_size || (sbi->cluster_size & (sbi->cluster_size-1))) {
+ printk("fatfs: bogus cluster size\n");
+- brelse(bh);
+- goto out_invalid;
++ error = 1;
++ /* brelse(bh);goto out_invalid; */
++ }else{
++ for (sbi->cluster_bits=0;
++ 1<<sbi->cluster_bits<sbi->cluster_size;
++ sbi->cluster_bits++)
++ ;
++ sbi->cluster_bits += SECTOR_BITS;
+ }
+- for (sbi->cluster_bits=0;
+- 1<<sbi->cluster_bits<sbi->cluster_size;
+- sbi->cluster_bits++)
+- ;
+- sbi->cluster_bits += SECTOR_BITS;
+ sbi->fats = b->fats;
+ sbi->fat_start = CF_LE_W(b->reserved)*sector_mult;
+ if (!b->fat_length && b->fat32_length) {
+@@ -573,7 +577,7 @@
+ data_sectors = CF_LE_L(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - sbi->data_start;
+- error = !b->cluster_size || !sector_mult;
++ error |= !sector_mult;
+ if (!error) {
+ sbi->clusters = b->cluster_size ? data_sectors/
+ b->cluster_size/sector_mult : 0;
+@@ -599,11 +603,14 @@
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : (blksize == 1024 ? 10 : 11);
+ if (!strcmp(cvf_format,"none"))
+- i = -1;
++ cvf_index = -1;
+ else
+- i = detect_cvf(sb,cvf_format);
+- if (i >= 0)
+- error = cvf_formats[i]->mount_cvf(sb,cvf_options);
++ cvf_index = detect_cvf(sb,cvf_format);
++ if (cvf_index >= 0) {
++ ++cvf_format_use_count[cvf_index];
++ sbi->cvf_format = cvf_formats[cvf_index];
++ error = cvf_formats[cvf_index]->mount_cvf(sb,cvf_options);
++ }
+ else if (sb->s_blocksize == 512)
+ sbi->cvf_format = &default_cvf;
+ else
+@@ -627,7 +634,7 @@
+ sbi->root_cluster,sbi->free_clusters);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+- if (i<0) if (sbi->clusters+2 > fat_clusters)
++ if (cvf_index<0) if (sbi->clusters+2 > fat_clusters)
+ sbi->clusters = fat_clusters-2;
+ if (error)
+ goto out_invalid;
+@@ -672,10 +679,6 @@
+ sb->s_root = d_alloc_root(root_inode);
+ if (!sb->s_root)
+ goto out_no_root;
+- if(i>=0) {
+- sbi->cvf_format = cvf_formats[i];
+- ++cvf_format_use_count[i];
+- }
+ return sb;
+
+ out_no_root:
+@@ -687,6 +690,8 @@
+ unload_nls(sbi->nls_disk);
+ goto out_fail;
+ out_invalid:
++ if(cvf_index>=0)
++ --cvf_format_use_count[cvf_index];
+ if (!silent)
+ printk("VFS: Can't find a valid MSDOS filesystem on dev %s.\n",
+ kdevname(sb->s_dev));
+diff -u -r linux-2.3.30-6-orig/fs/fat/misc.c linux-2.3.30-6-new1/fs/fat/misc.c
+--- linux-2.3.30-6-orig/fs/fat/misc.c Mon Aug 30 17:23:14 1999
++++ linux-2.3.30-6-new1/fs/fat/misc.c Sun Dec 5 03:11:47 1999
+@@ -209,8 +209,16 @@
+ int cluster_size = MSDOS_SB(sb)->cluster_size;
+
+ if (MSDOS_SB(sb)->fat_bits != 32) {
+- if (inode->i_ino == MSDOS_ROOT_INO) return res;
++ if (inode->i_ino == MSDOS_ROOT_INO){
++#ifdef DEBUG
++printk("FAT extend dir called for root\n");
++#endif
++ return res;
++ }
+ }
++#ifdef DEBUG
++printk("FAT extend dir called for NON-root\n");
++#endif
+ if (!MSDOS_SB(sb)->free_clusters) return res;
+ lock_fat(sb);
+ limit = MSDOS_SB(sb)->clusters;
+@@ -279,9 +287,11 @@
+ sector = MSDOS_SB(sb)->data_start+(nr-2)*cluster_size;
+ last_sector = sector + cluster_size;
+ if (MSDOS_SB(sb)->cvf_format &&
+- MSDOS_SB(sb)->cvf_format->zero_out_cluster)
++ MSDOS_SB(sb)->cvf_format->zero_out_cluster){
++ /* CVF zeroes full cluster only */
+ MSDOS_SB(sb)->cvf_format->zero_out_cluster(inode,nr);
+- else
++ res=fat_bread(sb,fat_bmap(inode,inode->i_blocks));
++ }else
+ for ( ; sector < last_sector; sector++) {
+ #ifdef DEBUG
+ printk("zeroing sector %d\n",sector);
+diff -u -r linux-2.3.30-6-orig/include/linux/fat_cvf.h linux-2.3.30-6-new1/include/linux/fat_cvf.h
+--- linux-2.3.30-6-orig/include/linux/fat_cvf.h Mon Aug 9 18:43:49 1999
++++ linux-2.3.30-6-new1/include/linux/fat_cvf.h Sat Dec 4 23:25:26 1999
+@@ -30,8 +30,8 @@
+ ssize_t (*cvf_file_read) ( struct file *, char *, size_t, loff_t *);
+ ssize_t (*cvf_file_write) ( struct file *, const char *, size_t, loff_t *);
+ int (*cvf_mmap) (struct file *, struct vm_area_struct *);
+- int (*cvf_readpage) (struct inode *, struct page *);
+- int (*cvf_writepage) (struct inode *, struct page *);
++ int (*cvf_readpage) (struct dentry *, struct page *);
++ int (*cvf_writepage) (struct dentry *, struct page *);
+ int (*cvf_dir_ioctl) (struct inode * inode, struct file * filp,
+ unsigned int cmd, unsigned long arg);
+ void (*zero_out_cluster) (struct inode*, int clusternr);
+diff -u -r linux-2.3.30-6-orig/include/linux/msdos_fs.h linux-2.3.30-6-new1/include/linux/msdos_fs.h
+--- linux-2.3.30-6-orig/include/linux/msdos_fs.h Wed Oct 27 23:19:11 1999
++++ linux-2.3.30-6-new1/include/linux/msdos_fs.h Wed Nov 24 22:25:31 1999
+@@ -274,6 +274,7 @@
+ /* mmap.c */
+ extern int fat_mmap(struct file *, struct vm_area_struct *);
+ extern int fat_readpage(struct file *, struct page *);
++extern int fat_writepage(struct file *, struct page *);
+
+
+ /* vfat.c */
diff --git a/patches/magic b/patches/magic
new file mode 100644
index 0000000..9ee8ebb
--- /dev/null
+++ b/patches/magic
@@ -0,0 +1,16 @@
+#------------------------------------------------------------------------------
+#
+# dmsdos: file(1) magic for the CVF formats supported by dmsdos
+#
+
+3 string MSDBL6.0
+>51 byte 0
+>>13 byte 16 Doublespace CVF (version 1)
+>>13 byte 64 Drivespace 3 CVF
+>51 byte 2 Drivespace CVF (version 2)
+3 string MSDSP6.0
+>51 byte 0
+>>13 byte 16 Doublespace CVF (version 1)
+>>13 byte 64 Drivespace 3 CVF
+>51 byte 2 Drivespace CVF (version 2)
+0 string STACKER Stacker CVF
diff --git a/patches/msdos-rmdir-bugfix.diff b/patches/msdos-rmdir-bugfix.diff
new file mode 100644
index 0000000..cd8305f
--- /dev/null
+++ b/patches/msdos-rmdir-bugfix.diff
@@ -0,0 +1,19 @@
+--- linux.vanilla/fs/msdos/namei.c Sun Jun 21 18:40:55 1998
++++ linux/fs/msdos/namei.c Fri Sep 11 04:07:28 1998
+@@ -369,7 +369,7 @@
+ if (MSDOS_I(dir)->i_start) { /* may be zero in mkdir */
+ pos = 0;
+ bh = NULL;
+- while (fat_get_entry(dir,&pos,&bh,&de) > -1)
++ while (fat_get_entry(dir,&pos,&bh,&de) > -1) {
+ /* Ignore vfat longname entries */
+ if (de->attr == ATTR_EXT)
+ continue;
+@@ -379,6 +379,7 @@
+ fat_brelse(sb, bh);
+ return -ENOTEMPTY;
+ }
++ }
+ if (bh)
+ fat_brelse(sb, bh);
+ }
diff --git a/patches/vfat-brelse-bugfix.diff b/patches/vfat-brelse-bugfix.diff
new file mode 100644
index 0000000..ec9e750
--- /dev/null
+++ b/patches/vfat-brelse-bugfix.diff
@@ -0,0 +1,15 @@
+--- linux/fs/vfat/namei.c.orig Fri Feb 26 14:42:54 1999
++++ linux/fs/vfat/namei.c Fri Feb 26 14:43:28 1999
+@@ -506,10 +506,10 @@
+ continue;
+ if (memcmp(de->name,name,MSDOS_NAME))
+ continue;
+- brelse(bh);
++ fat_brelse(dir->i_sb,bh);
+ return 0;
+ }
+- brelse(bh);
++ fat_brelse(dir->i_sb,bh);
+ return -ENOENT;
+ }
+
diff --git a/src/Config.in b/src/Config.in
new file mode 100644
index 0000000..a707e2f
--- /dev/null
+++ b/src/Config.in
@@ -0,0 +1,82 @@
+#
+# For a description of the syntax of this configuration file,
+# see the Configure script.
+#
+mainmenu_name "Dmsdos Compile-Time Configuration"
+
+bool 'Detailed DMSDOS configuration (expert mode)' DMSDOS_EXPERT
+
+mainmenu_option next_comment
+comment 'CVF formats to be supported'
+bool 'Dos 6.0-6.22/Win95 Doublespace/Drivespace' DMSDOS_CONFIG_DBLSP_DRVSP
+bool 'Win95 Drivespace 3' DMSDOS_CONFIG_DRVSP3
+bool 'Stacker version 3' DMSDOS_CONFIG_STAC3
+bool 'Stacker version 4' DMSDOS_CONFIG_STAC4
+endmenu
+
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+mainmenu_option next_comment
+comment 'Memory Management'
+bool 'Enable advanced memory management (only for 2.0.xx kernels)' USE_XMALLOC
+if [ "$USE_XMALLOC" = "n" ]; then
+ bool 'Use vmalloc instead of kmalloc for cluster cache' USE_VMALLOC
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Cache setup'
+int 'Cache size for delayed compression (in clusters)' LISTSIZE
+int 'Maximum number of buffers in MDFAT cache' MDFATCACHESIZE
+int 'Maximum number of buffers in DFAT cache' DFATCACHESIZE
+int 'Maximum number of buffers in BITFAT cache' BITFATCACHESIZE
+int 'Idle buffer timeout (in seconds)' MAX_CACHE_TIME
+int 'Cache size for normal operation (in clusters)' CCACHESIZE
+int 'Idle cluster timeout (in seconds)' MAX_CCACHE_TIME
+endmenu
+
+mainmenu_option next_comment
+comment 'Read-ahead Options'
+int 'Maximum number of blocks to read ahead' MAX_READA
+bool 'Enable advanced read-ahead' USE_READA_LIST
+if [ "$USE_READA_LIST" = "y" ]; then
+ int 'Read-ahead queue size in blocks' READA_LIST_SIZE
+fi
+int 'Read-ahead threshold' READA_THRESHOLD
+endmenu
+fi
+
+mainmenu_option next_comment
+comment 'Misc Options'
+bool 'Write access support' DBL_WRITEACCESS
+bool 'Disable logging completely' NOLOG
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+int 'Default loglevel' DEFAULT_LOGLEVEL
+int 'Default compression level' DEFAULT_CF
+bool 'Sequence number logging' SEQLOG
+bool 'Writable mmap support (always enable this for 2.1/2.2 kernels)' DMSDOS_USE_READPAGE
+fi
+endmenu
+
+mainmenu_option next_comment
+comment 'Internal Compression Daemon'
+
+bool 'Enable internal compression daemon' INTERNAL_DAEMON
+if [ "$DMSDOS_EXPERT" = "y" -a "$INTERNAL_DAEMON" != "n" ]; then
+ int 'Daemon wakeup interval (in seconds)' IDMSDOSD_TIME
+fi
+endmenu
+
+if [ "$DMSDOS_EXPERT" = "y" ]; then
+mainmenu_option next_comment
+comment 'Speedup Tricks & Hacks'
+bool 'Leave directories uncompressed' SP_BIT0
+bool 'Leave umsdos EMD uncompressed' SP_BIT1
+bool 'Skip exact search in BITFAT allocation' SP_BIT2
+bool 'Fast unmount' SP_BIT3
+bool 'Write-back cluster caching (instead of write-through)' SP_BIT4
+bool 'Enable read-ahead prediction' SP_BIT5
+bool 'Fast BITFAT allocation (risky)' SP_BIT6
+bool 'Use delayed compression (with dmsdos daemon)' SP_BIT7
+bool 'Avoid fragmented writes' SP_BIT8
+endmenu
+fi
diff --git a/src/Configure b/src/Configure
new file mode 100644
index 0000000..f080f42
--- /dev/null
+++ b/src/Configure
@@ -0,0 +1,438 @@
+#! /bin/sh
+#
+# This script is used to configure dmsdos compile-time options.
+# It has been shamelessly stolen from the linux kernel. For a list
+# of credits see there (linux/scripts/Configure).
+#
+
+#
+# Make sure we're really running bash.
+#
+# I would really have preferred to write this script in a language with
+# better string handling, but alas, bash is the only scripting language
+# that I can be reasonable sure everybody has on their linux machine.
+#
+[ -z "$BASH" ] && { echo "Configure requires bash" 1>&2; exit 1; }
+
+# Disable filename globbing once and for all.
+# Enable function cacheing.
+set -f -h
+
+#
+# Dummy functions for use with a config.in modified for menuconf
+#
+function mainmenu_option () {
+ :
+}
+function mainmenu_name () {
+ :
+}
+function endmenu () {
+ :
+}
+
+#
+# help prints the corresponding help text from Configure.help to stdout
+#
+# help variable
+#
+function help () {
+ if [ -f Configure.help ]
+ then
+ #first escape regexp special characters in the argument:
+ var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+ #now pick out the right help text:
+ text=$(sed -n "/^$var[ ]*\$/,\${
+ /^$var[ ]*\$/b
+ /^#.*/b
+ /^[ ]*\$/q
+ p
+ }" Configure.help)
+ if [ -z "$text" ]
+ then
+ echo; echo " Sorry, no help available for this option yet.";echo
+ else
+ (echo; echo "$text"; echo) | ${PAGER:-more}
+ fi
+ else
+ echo;
+ echo " Can't access the file Configure.help which"
+ echo " should contain the help texts."
+ echo
+ fi
+}
+
+
+#
+# readln reads a line into $ans.
+#
+# readln prompt default oldval
+#
+function readln () {
+ if [ "$DEFAULT" = "-d" -a -n "$3" ]; then
+ echo "$1"
+ ans=$2
+ else
+ echo -n "$1"
+ [ -z "$3" ] && echo -n "(NEW) "
+ IFS='@' read ans </dev/tty || exit 1
+ [ -z "$ans" ] && ans=$2
+ fi
+}
+
+#
+# comment does some pretty-printing
+#
+# comment 'xxx'
+#
+function comment () {
+ echo "*"; echo "* $1" ; echo "*"
+ (echo "" ; echo "#"; echo "# $1" ; echo "#") >>$CONFIG
+ (echo "" ; echo "/*"; echo " * $1" ; echo " */") >>$CONFIG_H
+}
+
+#
+# define_bool sets the value of a boolean argument
+#
+# define_bool define value
+#
+function define_bool () {
+ case "$2" in
+ "y")
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ ;;
+
+ "m")
+ echo "$1=m" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ echo "#define $1_MODULE 1" >>$CONFIG_H
+ ;;
+
+ "n")
+ echo "# $1 is not set" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ ;;
+ esac
+ eval "$1=$2"
+}
+
+#
+# bool processes a boolean argument
+#
+# bool question define
+#
+function bool () {
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ case "$def" in
+ "y" | "m") defprompt="Y/n/?"
+ def="y"
+ ;;
+ "n") defprompt="N/y/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [yY] | [yY]es ) define_bool "$2" "y"
+ break;;
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+}
+
+#
+# tristate processes a tristate argument
+#
+# tristate question define
+#
+function tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]; then
+ bool "$1" "$2"
+ else
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ case "$def" in
+ "y") defprompt="Y/m/n/?"
+ ;;
+ "m") defprompt="M/n/y/?"
+ ;;
+ "n") defprompt="N/y/m/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [yY] | [yY]es ) define_bool "$2" "y"
+ break ;;
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break ;;
+ [mM] ) define_bool "$2" "m"
+ break ;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+ fi
+}
+
+#
+# dep_tristate processes a tristate argument that depends upon
+# another option. If the option we depend upon is a module,
+# then the only allowable options are M or N. If Y, then
+# this is a normal tristate. This is used in cases where modules
+# are nested, and one module requires the presence of something
+# else in the kernel.
+#
+# tristate question define default
+#
+function dep_tristate () {
+ old=$(eval echo "\${$2}")
+ def=${old:-'n'}
+ if [ "$3" = "n" ]; then
+ define_bool "$2" "n"
+ elif [ "$3" = "y" ]; then
+ tristate "$1" "$2"
+ else
+ if [ "$CONFIG_MODULES" = "y" ]; then
+ case "$def" in
+ "y" | "m") defprompt="M/n/?"
+ def="m"
+ ;;
+ "n") defprompt="N/m/?"
+ ;;
+ esac
+ while :; do
+ readln "$1 ($2) [$defprompt] " "$def" "$old"
+ case "$ans" in
+ [nN] | [nN]o ) define_bool "$2" "n"
+ break ;;
+ [mM] ) define_bool "$2" "m"
+ break ;;
+ [yY] | [yY]es ) echo
+ echo " This answer is not allowed, because it is not consistent with"
+ echo " your other choices."
+ echo " This driver depends on another one which you chose to compile"
+ echo " as a module. This means that you can either compile this one"
+ echo " as a module as well (with M) or leave it out altogether (N)."
+ echo
+ ;;
+ * ) help "$2"
+ ;;
+ esac
+ done
+ fi
+ fi
+}
+
+#
+# define_int sets the value of a integer argument
+#
+# define_int define value
+#
+function define_int () {
+ echo "$1=$2" >>$CONFIG
+ echo "#define $1 ($2)" >>$CONFIG_H
+ eval "$1=$2"
+}
+
+#
+# int processes an integer argument
+#
+# int question define default
+# GNU expr changed handling of ?. In older versions you need ?,
+# in newer you need \?
+OLD_EXPR=`expr "0" : '0\?'`
+if [ $OLD_EXPR -eq 1 ]; then
+ INT_EXPR='0$\|-\?[1-9][0-9]*$'
+else
+ INT_EXPR='0$\|-?[1-9][0-9]*$'
+fi
+function int () {
+ old=$(eval echo "\${$2}")
+ def=${old:-$3}
+ while :; do
+ readln "$1 ($2) [$def] " "$def" "$old"
+ if expr "$ans" : $INT_EXPR > /dev/null; then
+ define_int "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+#
+# define_hex sets the value of a hexadecimal argument
+#
+# define_hex define value
+#
+function define_hex () {
+ echo "$1=$2" >>$CONFIG
+ echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
+ eval "$1=$2"
+}
+
+#
+# hex processes an hexadecimal argument
+#
+# hex question define default
+#
+function hex () {
+ old=$(eval echo "\${$2}")
+ def=${old:-$3}
+ def=${def#*[x,X]}
+ while :; do
+ readln "$1 ($2) [$def] " "$def" "$old"
+ ans=${ans#*[x,X]}
+ if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
+ define_hex "$2" "$ans"
+ break
+ else
+ help "$2"
+ fi
+ done
+}
+
+#
+# choice processes a choice list (1-out-of-n)
+#
+# choice question choice-list default
+#
+# The choice list has a syntax of:
+# NAME WHITESPACE VALUE { WHITESPACE NAME WHITESPACE VALUE }
+# The user may enter any unique prefix of one of the NAMEs and
+# choice will define VALUE as if it were a boolean option.
+# VALUE must be in all uppercase. Normally, VALUE is of the
+# form CONFIG_<something>. Thus, if the user selects <something>,
+# the CPP symbol CONFIG_<something> will be defined and the
+# shell variable CONFIG_<something> will be set to "y".
+#
+function choice () {
+ question="$1"
+ choices="$2"
+ old=
+ def=$3
+
+ # determine default answer:
+ names=""
+ set -- $choices
+ firstvar=$2
+ while [ -n "$2" ]; do
+ if [ -n "$names" ]; then
+ names="$names, $1"
+ else
+ names="$1"
+ fi
+ if [ "$(eval echo \"\${$2}\")" = "y" ]; then
+ old=$1
+ def=$1
+ fi
+ shift; shift
+ done
+
+ val=""
+ while [ -z "$val" ]; do
+ ambg=n
+ readln "$question ($names) [$def] " "$def" "$old"
+ ans=$(echo $ans | tr a-z A-Z)
+ set -- $choices
+ while [ -n "$1" ]; do
+ name=$(echo $1 | tr a-z A-Z)
+ case "$name" in
+ "$ans"* )
+ if [ "$name" = "$ans" ]; then
+ val="$2"
+ break # stop on exact match
+ fi
+ if [ -n "$val" ]; then
+ echo;echo \
+ " Sorry, \"$ans\" is ambiguous; please enter a longer string."
+ echo
+ val=""
+ ambg=y
+ break
+ else
+ val="$2"
+ fi;;
+ esac
+ shift; shift
+ done
+ if [ "$val" = "" -a "$ambg" = "n" ]; then
+ help "$firstvar"
+ fi
+ done
+ set -- $choices
+ while [ -n "$2" ]; do
+ if [ "$2" = "$val" ]; then
+ echo " defined $val"
+ define_bool "$2" "y"
+ else
+ define_bool "$2" "n"
+ fi
+ shift; shift
+ done
+}
+
+CONFIG=.tmpconfig
+CONFIG_H=.tmpconfig.h
+trap "rm -f $CONFIG $CONFIG_H ; exit 1" 1 2
+
+#
+# Make sure we start out with a clean slate.
+#
+echo "#" > $CONFIG
+echo "# Automatically generated make config: don't edit" >> $CONFIG
+echo "#" >> $CONFIG
+
+echo "/*" > $CONFIG_H
+echo " * Automatically generated C config: don't edit" >> $CONFIG_H
+echo " * Run Configure instead" >> $CONFIG_H
+echo " */" >> $CONFIG_H
+
+DEFAULT=""
+if [ "$1" = "-d" ] ; then
+ DEFAULT="-d"
+ shift
+fi
+
+CONFIG_IN=./Config.in
+if [ "$1" != "" ] ; then
+ CONFIG_IN=$1
+fi
+
+DEFAULTS=dmsdos-config.default
+if [ -f .config ]; then
+ DEFAULTS=.config
+fi
+
+if [ -f $DEFAULTS ]; then
+ echo "#"
+ echo "# Using defaults found in" $DEFAULTS
+ echo "#"
+ . $DEFAULTS
+ sed -e 's/# \(.*\) is not.*/\1=n/' < $DEFAULTS > /tmp/conf.$$
+ . /tmp/conf.$$
+ rm /tmp/conf.$$
+else
+ echo "#"
+ echo "# No defaults found"
+ echo "#"
+fi
+
+. $CONFIG_IN
+
+rm -f .config.old
+if [ -f .config ]; then
+ mv .config .config.old
+fi
+mv .tmpconfig .config
+mv .tmpconfig.h dmsdos-config.h
+
+echo
+echo "Dmsdos is now configured. Do a 'make clean; make' to (re)compile."
+
+exit 0
diff --git a/src/Configure.help b/src/Configure.help
new file mode 100644
index 0000000..fca412f
--- /dev/null
+++ b/src/Configure.help
@@ -0,0 +1,365 @@
+# help file for dmsdos configuration
+# idea shamelessly stolen from Linux kernel
+
+# Format of this file: description<nl>variable<nl>helptext<nl><nl>.
+# If the question being documented is of type "choice", we list
+# only the first occurring config variable. The help texts
+# must not contain empty lines. No variable should occur twice; if it
+# does, only the first occurrence will be used by Configure. The lines
+# in a help text should be indented two positions. Lines starting with
+# #' are ignored. To be nice to menuconfig, limit your lines to 70
+# characters.
+
+Detailed DMSDOS configuration (expert mode)
+DMSDOS_EXPERT
+ Say Y if you want a detailed setup (then a lot of things that you
+ may not be familiar with unless you are a dmsdos expert will be
+ asked). If you say N most dmsdos internal values are set to safe
+ defaults which should be sufficient for most systems. (Yes, you
+ _can_ change everything. But you'll have to understand what you do.)
+
+Dos 6.0-6.22/Win95 Doublespace/Drivespace
+DMSDOS_CONFIG_DBLSP_DRVSP
+ Say Y if you want support for this kind of CVFs. This kind of CVF
+ format is built into MSDOS 6.0-6.22 and older versions of Win95.
+ If unsure, saying Y won't hurt except making the module larger.
+
+Win95 Drivespace 3
+DMSDOS_CONFIG_DRVSP3
+ Say Y if you want support for this kind of CVFs. Note that
+ Drivespace 3 has been sold seperately from Win95 for some time, but
+ newer versions of Win95 seem to have Drivespace 3 built in.
+ The software should tell its version number in the Help/Info menu
+ under Win95. If unsure, saying Y won't hurt except making the module
+ larger.
+
+Stacker version 3
+DMSDOS_CONFIG_STAC3
+ Say Y if you want support for this kind of CVFs. If unsure, saying Y
+ won't hurt except making the module larger.
+
+Stacker version 4
+DMSDOS_CONFIG_STAC4
+ Say Y if you want support for this kind of CVFs. If unsure, saying Y
+ won't hurt except making the module larger.
+
+Use vmalloc instead of kmalloc
+USE_VMALLOC
+ The kernel knows to memory allocation interfaces - kmalloc and
+ vmalloc. kmalloc is faster but more likely to fail (i.e. claim that
+ the system is out of memory) - vmalloc very rarely fails but memory
+ is swappable and it is surely not a good idea to swap the cluster
+ cache to the disk...
+ If you have a system with lots of physical RAM you might want to
+ try kmalloc for better performance. vmalloc is always the safe
+ solution. If you are not sure, say Y, which is always safe. YOU ARE
+ STRONLY ENCOURAGED TO ENABLE VMALLOC WHEN DEBUGGING NEW CODE.
+
+Advanced memory allocation
+USE_XMALLOC
+ Enables optimized memory allocation routine - it decides itself
+ whether to use kmalloc or vmalloc depending on the requested size
+ and on what the system can provide.
+ WARNING: this does not work for all kernels. It is known to work
+ for 2.0.xx kernels. But I've got reports about problems with some
+ 2.1.XX kernels and xmalloc. Maybe it also fails under 2.2.x. If
+ you get a message (maybe even a kernel panic) saying "please
+ disable XMALLOC" it is broken under your kernel.
+ You may receive *lots* of "Couldn't get a free page" messages in
+ the syslog which can be safely ignored - they result from the
+ driver's memory allocation strategy.
+ DON'T USE XMALLOC WHEN DEBUGGING NEW CODE.
+ Say N to feel safe.
+
+Size of daemon cache
+LISTSIZE
+ Maximum number of clusters that can be delayed for compression -
+ this is for the dmsdos compression daemon, not for write-back
+ caching. A value larger than 4096 seems to slow down your system
+ more than not using the daemon since the driver is busy with
+ maintaining the cache most of the time. Still good performance:
+ 1024, which is also the recommended value.
+
+Idle acache timeout
+MAX_CACHE_TIME
+ Time (in seconds) after which idle MDFAT/DFAT/BITFAT buffers are
+ returned to the kernel. Default is 60. (This time has only an effect
+ if the dmsdos daemon is running.)
+
+Max MDFAT cache size
+MDFATCACHESIZE
+ mdfat cache sizes -- please note that these number of fs buffers are
+ *permanently* held in memory - keep small on systems that are short
+ on memory. Default value: 40 (i.e. 20KB), which represents the MDFAT
+ space needed for approx. 5000 clusters (4000 on drivespace 3), i.e.
+ 40MB (or 160MB on 32K clusters) of compressed data.
+
+Max DFAT cache size
+DFATCACHESIZE
+ dfat cache sizes -- please note that these number of fs buffers are
+ *permanently* held in memory - keep small on systems that are short
+ on memory. Default value: 20 (i.e. 10KB), which represents the DFAT
+ space for approx. 5000 clusters, i.e. 40MB (160MB on 32K clusters)
+ of compressed data.
+
+Max BITFAT cache size
+BITFATCACHESIZE
+ bitfat cache sizes -- please note that these number of fs buffers
+ are *permanently* held in memory - keep small on systems that are
+ short on memory. Default value: 10 (i.e. 5KB), which represents the
+ BITFAT size for 400000 (200000 on stacker 4) compressed sectors,
+ i.e. 200MB (100MB on stacker 4) of compressed data.
+
+Enable write access
+DBL_WRITEACCESS
+ Say Y to compile in low-level write access code. If you say N all
+ low-level write access is replaced by an error written to syslog.
+ Say N if you want to build a read-only dmsdos module.
+
+Default loglevel
+DEFAULT_LOGLEVEL
+ Bit-coded field for debug messages (expert use only). Usually set
+ this to 0. Please read the documentation before changing this value.
+ The log level can also be changed at run time via dutil (see manpage)
+ or with a mount option.
+
+Default compression level
+DEFAULT_CF
+ Select between:
+ 0=least efficient but fastest compression
+ 11=most efficient but slowest compression
+ For slow processors you may want to use a lower value.
+ The compression level can be changed at run time with dutil (see
+ manpage) or with a mount option. If you are unsure say 11.
+
+Cluster cache size
+CCACHESIZE
+ Sets the maximum number of clusters dmsdos can keep in its cluster
+ cache. Keep small on systems with low memory - otherwise dmsdos may
+ eat up too much memory and slow down your system by causing great
+ swapping.
+ Every cluster uses 8KB (32KB on drivespace 3 and sometimes on
+ Stacker 4). You shouldn't exceed approx. 1/4 of your physical RAM,
+ so use 64 if you have only 4MB or if you have only 16MB and you are
+ using drivespace 3.
+ WARNINGS:
+ 1. If this value is too low, applications using files on a
+ compressed partition lock out each other. If it is much too low,
+ they may even lock out themselves and thus lock up the whole system.
+ Values below 64 are supposed critical, values below 32 are
+ supposed dangerous.
+ 2. You might cause a system lockup also if you use more cache than
+ you have physical RAM.
+
+Idle cluster cache timeout
+MAX_CCACHE_TIME
+ Time in seconds after which unused clusters are removed from the
+ cluster cache. Default is 240. (This time has only an effect if the
+ dmsdos daemon is running.)
+
+Maximum number of blocks to read ahead
+MAX_READA
+ Maximum number of sectors to read-ahead at once - default is 64 in
+ order to let a 32KB cluster fit in. This parameter has only an
+ effect if read-ahead is enabled in the speedup tricks & hacks section
+ below.
+
+Enable advanced read-ahead
+USE_READA_LIST
+ Use experimental (faster) read-ahead code in order to bypass
+ wait_on_buffer. If you say N the standard (slower) kernel read-ahead
+ code is used. Usually say Y unless you encounter strange problems.
+ This option only has an effect if read-ahead is turned on in the
+ speedup tricks & hacks section below.
+
+Size of read-ahead list
+READA_LIST_SIZE
+ Fine-tune advanced read-ahead code. I always set this to 256. I
+ don't know whether changing this value really speeds up something
+ - my own benchmarks didn't lead to a clear result.
+ This option has only an effect if read-ahead is turned on and
+ advanced read-ahead is enabled.
+
+Read-ahead threshold
+READA_THRESHOLD
+ Read-ahead is only done when more than this amount of bytes are read
+ at once. This prevents a badly written program from issuing thousands
+ of useless read-aheads. 4095 is a good value since this allows
+ read-ahead just for a memory page (mmap gets the data in 4096 byte
+ blocks and should have a gain from read-ahead). Lower values may
+ cause clustersize/(value+1)-1 useless read-aheads per cluster.
+ This option has only an effect if read-ahead is turned on in the
+ speedup tricks & hacks section below.
+
+Leave directories uncompressed
+SP_BIT0
+ Speedup Bit#0: avoids compression of directories. If it is set,
+ dmsdos never compresses directories for drivespace 3 and stacker
+ (others don't support compressed directories at all). Note that
+ directories are accessed very often and otherwise had to be
+ decompressed and compressed again after each access. Say Y.
+
+Leave umsdos EMD file uncompressed
+SP_BIT1
+ Speedup Bit#1 is for umsdos upon dmsdos. If set, the driver never
+ compress the --linux-.--- special file (it contains extended
+ directory information). Setting this bit is strongly recommended.
+ The special file is even more often written than a directory
+ since it contains the access times (the directory contains only
+ the modification times). You will regret compressing this file,
+ even if you own the fastest computer of the world. Say Y.
+
+Skip exact search on BITFAT allocation
+SP_BIT2
+ Speedup Bit#2: controls the free sector searching algorithm. If set,
+ dmsdos searchs faster but less carefully for free space in the CVF
+ at the cost of more fragmentation (this is *not* FAT level, but an
+ internal lower level and very awful fragmentation). If you say Y,
+ write access on large CVFs is faster (sometimes really notably
+ faster). Be warned, a CVF that is too fragmented will be set to
+ read-only, so you will have to boot Dos from time to time and run
+ the CVF maintainance tools over it. Usually say N, but saying Y
+ is not dangerous and may make you happier when writing much to a
+ CVF.
+
+Fast unmount
+SP_BIT3
+ Speedup Bit#3: speeds up CVF unmount. If set, the driver writes dirty
+ clusters that are in the cache immediately without compressing them
+ before when the filesystem is to be unmounted. This way the unmount
+ procedure will be quite fast. In contrast, if you say N the clusters
+ are compressed before. Note that compression may take some time and
+ thus blocks the system until it is ready. Since most people who
+ haven't read these lines tend to press the reset button when a
+ filesystem unmount takes somewhat longer (especially on a shutdown
+ or reboot), the default is Y. If you can tolerate the time to wait
+ and you are prepared to wait, say N.
+
+Enable write-back cluster caching (instead of write-through)
+SP_BIT4
+ Speedup Bit#4 enables write-back cluster caching. If this bit is set
+ the data in the cluster cache that have been changed by an
+ application and have to be written back to disk are not written
+ back immediately - they are kept some time in memory just in order
+ to save disk access when the application again changes the data.
+ This usually causes a significant speedup for write access,
+ especially because dmsdos also delays compression and free space
+ allocation in that case. To be honest, there's a small risk of data
+ loss if the system happens to crash before the data are written
+ back - but since your operating system is Linux a crash is rather
+ unlikely :) So say Y.
+
+Enable read-ahead
+SP_BIT5
+ Speedup Bit#5 enables cluster read-ahead in general. If you say Y,
+ this causes the driver to initiate a low-level disk access for some
+ data when it thinks they are most likely needed in a short time
+ later. This usually speeds up read access a little. The trick is
+ that the driver doesn't wait for the read-ahead disk access
+ to finish. So the disk can position its head (and so on) while the
+ system can do something else. Most likely the disk access has
+ finished when the data are actually needed - this saves some time
+ we otherwise had to wait. Well, at least this is the nice idea
+ of read-ahead. However, since read-ahead relies on a prediction,
+ there may be situations when it is useless or even a loss.
+ Just say Y.
+
+Fast BITFAT allocation
+SP_BIT6
+ Speedup Bit#6 controls the free sector searching algorithm similar
+ to Speedup Bit#2, but in a more drastic way (it simply switches it
+ off). If this bit is set, free space allocation is very fast but
+ does not care about avoiding fragmentation at all. This is not
+ recommended. BE WARNED, it can cause *much* fragmentation in very
+ short time. The "filesystem almost full" warning is suppressed.
+ This switch is meant as a last resort if you cannot tolerate
+ system slowdowns at all. Don't fill the compressed filesystem
+ up to more than 3/4 when this switch is set. Write access may
+ fail horribly and cause data loss due to too high fragmentation.
+ (Well, this is Dos/Win95 strategy - never tell the disadvantages)
+ Usually say N. If unsure about the dangers, say N also. Only say Y
+ if you really know what you are doing.
+
+Use daemon if present
+SP_BIT7
+ Speedup Bit#7 controls what jobs the dmsdos daemon has to do (if it
+ is running). If set, the daemon takes over all the compression of
+ data that have to be written to disk. This can make your system
+ respond a little faster on write access as compression will be
+ mostly done when the system is idle. This is recommended for systems
+ usually running at high processor load, or in general for slow
+ processors. In contrast, if you say N the daemon is just used for
+ dmsdos internal memory management. If unsure, say Y.
+
+Avoid fragmented writes
+SP_BIT8
+ Speedup Bit#8 controls what to do when the filesystem is too
+ fragmented for normal write access. Usually all data for one cluster
+ are stored in contiguous sectors, but if the filesystem is too
+ fragmented there may not be a 'free hole' that is large enough for
+ the data. Speedup Bit#8 controls what to do in that case. If this
+ bit is set dmsdos just returns a 'no space left on device' error and
+ refuses to write to the CVF any more.
+ Drivespace 3 and Stacker know a hack for that situation: they allow
+ storing the data of one cluster in several fragments on the disk.
+ If the bit is clear, the driver tries to write the data in several
+ fragments. Be warned, this makes future filesystem access slower as
+ it needs more disk head movements to read fragmented clusters.
+ *** Note that for Stacker this is currently not implemented ***
+ The default answer is N.
+
+Use internal daemon
+INTERNAL_DAEMON
+ Dmsdos can start a daemon that does delayed compression in the
+ background when the system is idle. It also reduces dmsdos memory
+ consumption when the driver has nothing to do for a long time.
+ The daemon exists in two variants: One is a user-level program and
+ the other one is a kernel process. The user-level program is called
+ "external daemon" while the other is the "internal daemon".
+ When the internal daemon is used, the external one won't run.
+ The internal daemon compresses faster (because the data needn't be
+ copied from kernel space to user space and back), but seems to eat
+ up much system time - use it only on *really fast* machines. Also,
+ it is started automatically by the system when it is needed (and
+ killed when it is no longer needed). On the other hand, the external
+ daemon eats up minimal system time and ressources, but must be
+ started and killed by hand.
+ *** If you have a fast processor and lots of memory (_real RAM_)
+ you probably don't need any dmsdos daemon at all.
+ Say N if unsure.
+
+Internal daemon wakeup time
+IDMSDOSD_TIME
+ Time interval in seconds for internal daemon - it awakes
+ periodically and looks for data to compress or memory to free up
+ in this interval. Set it to 30 if you are unsure. This option has
+ only an effect if the internal compression daemon is used.
+
+Disable logging completely
+NOLOG
+ Say Y if you need a really small module - this leaves out the
+ logging messages from the object code of the module. You cannot
+ use a loglevel other than 0 in this case. Usually say N.
+ Especially say N when debugging.
+
+Sequence number logging
+SEQLOG
+ Say Y to generate a sequence number for each log line. The numbers
+ are guaranteed to be uniqe and in order of call. This is for
+ debugging in order to be sure no messages have been lost (There seem
+ to be some black holes in syslog that eat messages when they occur
+ massively.)
+ Say N if you are not debugging. If you find that messages are lost,
+ change the #define LOG_BUF_LEN to at least 65536 in file
+ linux/kernel/printk.c and recompile your kernel. Please note that
+ the LOG_BUF_LEN value is expected to be a power of 2 by the Linux
+ kernel code.
+
+Writable mmap support
+DMSDOS_USE_READPAGE
+ Say Y to use the standard readpage interface (for writable mmap).
+ Say N to use old mmap interface which is read-only. Say Y except
+ you run into strange problems with writable mmaps.
+ WARNING: Old mmap interface is broken in dmsdos under some newer
+ 2.1.xx and 2.2.x kernels (it doesn't compile). It may be removed
+ from dmsdos some day.
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..61ab1b6
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,242 @@
+# If you want or need to change something herein, please look approx. 30 lines
+# below for the 'User Config Section'
+
+########################################################################
+# The next lines are hacks for automatic detection of different kernel setups.
+# These things must happen first, please don't change them here. They can be
+# overridden later.
+#
+# we try to guess some settings from kernel configuration :)
+-include /usr/src/linux/.config
+#
+# guess some special flags for CPU type - used for optimization only
+CPUFLAGS=
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -m386 -DCPU=386 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -m486 -DCPU=486 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+# this one is new in 2.2.x kernels...
+ifeq ($(CONFIG_M586TSC),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+# this one is new in 2.3.x kernels...
+ifeq ($(CONFIG_MK7),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+#
+# Okay, that's it :)
+########################################################################
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# The next lines should detect SMP configuration automatically.
+# Comment them out or set it manually if it fails.
+ifeq ($(SMP),1)
+CPUFLAGS+= -D__SMP__
+endif
+# 2.2.x kernels have this one...
+ifeq ($(CONFIG_SMP),y)
+CPUFLAGS+= -D__SMP__
+endif
+#
+# The next lines are for libc6 and enable some compatibility hacks -
+# uncomment the line with -D__FOR_LIBC6 if the code does not compile
+# *** Currently not used - code should compile under libc6 without hacks
+LIBC6FLAGS=
+#LIBC6FLAGS= -D__FOR_LIBC6
+#
+# CFLAGS for dmsdos module
+# note: some macro expansions require at least -O
+CFLAGS= -Wall -Wstrict-prototypes -O3 -fomit-frame-pointer -D__KERNEL__ -DMODULE
+CFLAGS+= $(CPUFLAGS)
+#
+# The next lines add some stuff automatically for people using modversions
+# if they fail, comment them out and set the required flags manually
+ifeq ($(CONFIG_MODVERSIONS),y)
+CFLAGS+= -DMODVERSIONS -include /usr/include/linux/modversions.h
+endif
+#
+# CFLAGS for the dmsdos daemon
+# note: some macro expansions require at least -O
+DCFLAGS= -Wall -O3 -D__DMSDOS_DAEMON__
+DCFLAGS+= $(CPUFLAGS)
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__ -DUSE_FLOCK
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+# uncomment the next line if you want a shared library
+# LIB_SHARED = 1
+#
+# locations where to install the module, the executables and the manpages
+# note: `uname -r` expands to the *currently running* kernel version - if it
+# is different from that in /usr/src/linux you'd better edit the next line :)
+MODULE_PREFIX=/lib/modules/`uname -r`/fs
+#MODULE_PREFIX=/lib/modules/misc
+EXEC_PREFIX=/usr/local/bin
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h dmsdos.o dutil dmsdosd $(LIBDMSDOS) dcread dmsdosfsck \
+ mcdmsdos cvflist cvftest
+
+min: dmsdos-config.h dmsdos.o dutil
+
+dmsdos-config.h:
+ /bin/bash conf-wrapper.sh
+ make dep
+
+config:
+ /bin/bash Configure
+ make dep
+
+menuconfig:
+ /bin/bash Menuconfig
+ make dep
+
+dep:
+ ./check.sh
+ $(CC) -w -E -MM -D__KERNEL__ -D__MODULE__ -DMODULE dblspace*.c dstacker*.c > depend
+
+depend:
+ @touch depend
+
+DBL_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+
+$(DBL_OBJS): dmsdos.h dmsdos-config.h
+
+dblspace_fileops.o : dblspace_fileops.c dblspace_fileops.c-2.0.33 \
+ dblspace_fileops.c-2.1.80 dblspace_fileops.c-2.3.10
+
+dmsdos.o: $(DBL_OBJS)
+ ld -r -o dmsdos.o $(DBL_OBJS)
+
+dutil: dutil.c dmsdos.h dmsdos-config.h
+ $(CC) $(DCFLAGS) -o dutil dutil.c
+
+DAEMON_OBJS = daemon_actions.do dblspace_compr.do dstacker_compr.do \
+ dblspace_methsq.do
+
+$(DAEMON_OBJS): dmsdos.h dmsdos-config.h
+
+%.do: %.c ; $(CC) -o $@ $(DCFLAGS) -c $<
+
+dmsdosd: $(DAEMON_OBJS)
+ $(CC) -o dmsdosd $^
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so *.so.* dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+install: all
+ ./check.sh
+ install dmsdos.o -m 644 $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+ install dmsdosd $(EXEC_PREFIX)/dmsdosd
+ install dmsdosfsck $(EXEC_PREFIX)/dmsdosfsck
+ install mcdmsdos $(EXEC_PREFIX)/mcdmsdos
+ install cvflist $(EXEC_PREFIX)/cvflist
+ install cvftest $(EXEC_PREFIX)/cvftest
+
+install-min: min
+ ./check.sh
+ install dmsdos.o $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+
+uninstall:
+ rm -f $(MODULE_PREFIX)/dmsdos.o
+ rm -f $(EXEC_PREFIX)/dutil
+ rm -f $(EXEC_PREFIX)/dmsdosd
+ rm -f $(EXEC_PREFIX)/dmsdosfsck
+ rm -f $(EXEC_PREFIX)/mcdmsdos
+ rm -f $(EXEC_PREFIX)/cvflist
+ rm -f $(EXEC_PREFIX)/cvftest
+
+messages:
+ grep DMSDOS ../doc/messages.doc | sort -d -b -f > /tmp/messdoc
+ ./listmsg.sh -LOG > /tmp/messsrc
+ diff -d -U 0 -w /tmp/messdoc /tmp/messsrc | grep DMSDOS
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+ifndef LIB_SHARED
+
+LIBDMSDOS = libdmsdos.a
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ar rcs $(LIBDMSDOS) $^
+ ranlib $(LIBDMSDOS)
+
+else
+
+%.lo: %.c ; $(CC) --shared -o $@ $(LCFLAGS) -c $<
+
+LIBDMSDOS = libdmsdos.so.0.9.2
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ld --shared --soname=$(LIBDMSDOS) -o $(LIBDMSDOS) $^
+ ln -s $(LIBDMSDOS) libdmsdos.so
+
+endif
+
+dcread: dcread.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+cvftest: cvftest.c
+ $(CC) -Wall -o cvftest cvftest.c
+
+cvflist: cvflist.c
+ $(CC) -Wall -o cvflist cvflist.c
+
+-include depend
diff --git a/src/Makefile.cygwinb20 b/src/Makefile.cygwinb20
new file mode 100644
index 0000000..9ef003a
--- /dev/null
+++ b/src/Makefile.cygwinb20
@@ -0,0 +1,103 @@
+# Makefile for cygwin beta 20 compiler
+#
+# If you want or need to change something herein, please look
+# below for the 'User Config Section'
+
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the empty CPUFLAGS line to
+# switch it off - there are some gas versions around that don't like the
+# cpu specific asm instructions in dmsdos...
+# There's no way to detect this automatically in _all_ Win32 OS versions
+CPUFLAGS= -DCPU=486 -m486 -DUSE_ASM
+#CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+# cygwin seems to have neither flock nor sopen support (Uuhhh....)
+# well it's bad to compile code without file locking but we have no choice
+#LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__ -DUSE_FLOCK
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+# uncomment the next line if you want a shared library
+# does not work yet under win32
+# LIB_SHARED = 1
+#
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h $(LIBDMSDOS) dcread dmsdosfsck mcdmsdos
+
+dmsdos-config.h:
+ bash Configure
+
+config:
+ bash Configure
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so *.so.* dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+dep:
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+ifndef LIB_SHARED
+
+LIBDMSDOS = libdmsdos.a
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ar rcs $(LIBDMSDOS) $^
+ ranlib $(LIBDMSDOS)
+
+else
+
+%.lo: %.c ; $(CC) --shared -o $@ $(LCFLAGS) -c $<
+
+LIBDMSDOS = libdmsdos.so.0.9.2
+
+$(LIBDMSDOS): $(LIB_OBJS)
+ ld --shared --soname=$(LIBDMSDOS) -o $(LIBDMSDOS) $^
+ ln -s $(LIBDMSDOS) libdmsdos.so
+
+endif
+
+dcread: dcread.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c $(LIBDMSDOS) dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+
diff --git a/src/Makefile.kernel b/src/Makefile.kernel
new file mode 100644
index 0000000..0134f1a
--- /dev/null
+++ b/src/Makefile.kernel
@@ -0,0 +1,54 @@
+#
+# Makefile for dmsdos support.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -DUSE_ASM
+endif
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+CFLAGS+= $(CPUFLAGS)
+
+O_TARGET := dmsdos.o
+O_OBJS := dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+OX_OBJS :=
+M_OBJS := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/src/Makefile.module b/src/Makefile.module
new file mode 100644
index 0000000..5ebb5ef
--- /dev/null
+++ b/src/Makefile.module
@@ -0,0 +1,28 @@
+# Where to look for kernel
+KERNEL_LOCATION=/usr/src/linux
+# Target object file if any
+O_TARGET := dmsdos.o
+# Regular object files
+O_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+# Objects with exported symbols (-DEXPORT_SYMTAB)
+OX_OBJS =
+# Module objects
+M_OBJS = $(O_TARGET)
+# Module only objects with exported symbols (-DEXPORT_SYMTAB)
+MX_OBJS =
+# Kernel only objects
+L_OBJS =
+# Kernel only objects with exported symbols (-DEXPORT_SYMTAB)
+LX_OBJS =
+# Additional CFLAGS
+EXTRA_CFLAGS = -DUSE_ASM
+
+my_modules:
+ DIR=`pwd`; (cd $(KERNEL_LOCATION); make SUBDIRS=$$DIR modules)
+
+include $(KERNEL_LOCATION)/Rules.make
diff --git a/src/Makefile.old b/src/Makefile.old
new file mode 100644
index 0000000..69eb38c
--- /dev/null
+++ b/src/Makefile.old
@@ -0,0 +1,215 @@
+
+# If you want or need to change something herein, please look approx. 30 lines
+# below for the 'User Config Section'
+
+########################################################################
+# The next lines are hacks for automatic detection of different kernel setups.
+# These things must happen first, please don't change them here. They can be
+# overridden later.
+#
+# we try to guess some settings from kernel configuration :)
+-include /usr/src/linux/.config
+#
+# guess some special flags for CPU type - used for optimization only
+CPUFLAGS=
+ifeq ($(CONFIG_M386),y)
+CPUFLAGS= -m386 -DCPU=386 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M486),y)
+CPUFLAGS= -m486 -DCPU=486 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M586),y)
+CPUFLAGS= -m486 -DCPU=586 -DUSE_ASM
+endif
+ifeq ($(CONFIG_M686),y)
+CPUFLAGS= -m486 -DCPU=686 -DUSE_ASM
+endif
+#
+# Okay, that's it :)
+########################################################################
+
+########################################################################
+# *** User Config Section ***
+# Here you can look for settings to change if the code refuses to compile
+# out of the box. There are so many different systems. So many versions
+# of compilers. So many different bugs. :(
+#
+# Select compiler
+CC=gcc
+#
+# If cpu specific optimization fails, uncomment the next line to switch it
+# off - there are some gas versions around that don't like the cpu specific
+# asm instructions in dmsdos...
+# CPUFLAGS=
+#
+# Some gnu assembler versions have a bug in the syntax for the xlat
+# instruction. This enables a workaround. It should only be needed for very
+# old binutils versions. I don't know for which ones :(
+# CPUFLAGS+= -DGAS_XLAT_BUG
+#
+# The next lines should detect SMP configuration automatically.
+# Comment them out or set it manually if it fails.
+ifeq ($(SMP),1)
+CPUFLAGS+= -D__SMP__
+endif
+#
+# The next lines are for libc6 and enable some compatibility hacks -
+# uncomment the line with -D__FOR_LIBC6 if the code does not compile
+# *** Currently not used - code should compile under libc6 without hacks
+LIBC6FLAGS=
+#LIBC6FLAGS= -D__FOR_LIBC6
+#
+# CFLAGS for dmsdos module
+# note: some macro expansions require at least -O
+CFLAGS= -Wall -Wstrict-prototypes -O3 -fomit-frame-pointer -D__KERNEL__ -DMODULE
+CFLAGS+= $(CPUFLAGS)
+#
+# The next lines add some stuff automatically for people using modversions
+# if they fail, comment them out and set the required flags manually
+ifeq ($(CONFIG_MODVERSIONS),y)
+CFLAGS+= -DMODVERSIONS -include /usr/include/linux/modversions.h
+endif
+#
+# CFLAGS for the dmsdos daemon
+# note: some macro expansions require at least -O
+DCFLAGS= -Wall -O3 -D__DMSDOS_DAEMON__
+DCFLAGS+= $(CPUFLAGS)
+#
+# CFLAGS for the dmsdos library
+# note: some macro expansions require at least -O
+LCFLAGS= -Wall -O -ggdb -D__DMSDOS_LIB__
+LCFLAGS+= $(CPUFLAGS) $(LIBC6FLAGS)
+#
+# locations where to install the module, the executables and the manpages
+# note: `uname -r` expands to the *currently running* kernel version - if it
+# is different from that in /usr/src/linux you'd better edit the next line :)
+MODULE_PREFIX=/lib/modules/`uname -r`/fs
+#MODULE_PREFIX=/lib/modules/misc
+EXEC_PREFIX=/usr/local/bin
+#
+# Okay, that's the end of the User Config Section.
+##########################################################################
+
+##########################################################################
+# The rest of this file are rules how to build which programs.
+
+all: dmsdos-config.h dmsdos.o dutil dmsdosd libdmsdos.a dcread dmsdosfsck \
+ mcdmsdos cvflist cvftest
+
+min: dmsdos-config.h dmsdos.o dutil
+
+dmsdos-config.h:
+ /bin/bash conf-wrapper.sh
+ make dep
+
+config:
+ /bin/bash Configure
+ make dep
+
+menuconfig:
+ /bin/bash Menuconfig
+ make dep
+
+dep:
+ ./check.sh
+ $(CC) -w -E -MM -D__KERNEL__ -D__MODULE__ -DMODULE dblspace*.c dstacker*.c > depend
+
+depend:
+ @touch depend
+
+DBL_OBJS = dblspace_compr.o dblspace_tables.o \
+ dblspace_alloc.o dblspace_dec.o dblspace_virtual.o \
+ dblspace_buffer.o dblspace_methsq.o \
+ dblspace_chk.o dblspace_interface.o \
+ dstacker_alloc.o dstacker_compr.o dstacker_dec.o \
+ dblspace_fileops.o dblspace_ioctl.o
+
+$(DBL_OBJS): dmsdos.h dmsdos-config.h
+
+dmsdos.o: $(DBL_OBJS)
+ ld -r -o dmsdos.o $(DBL_OBJS)
+
+dutil: dutil.c dmsdos.h dmsdos-config.h
+ $(CC) $(DCFLAGS) -o dutil dutil.c
+
+DAEMON_OBJS = daemon_actions.do dblspace_compr.do dstacker_compr.do \
+ dblspace_methsq.do
+
+$(DAEMON_OBJS): dmsdos.h dmsdos-config.h
+
+%.do: %.c ; $(CC) -o $@ $(DCFLAGS) -c $<
+
+dmsdosd: $(DAEMON_OBJS)
+ $(CC) -o dmsdosd $^
+
+clean:
+ rm -f *.o *.a *.i *.s *.lo *.do *.so dutil dmsdosd dcread \
+ dmsdosfsck cvftest cvflist mcdmsdos
+
+mrproper: clean
+ rm -f core *~ depend
+ rm -f .config* dmsdos-config.h .menuconfig*
+
+install: all
+ ./check.sh
+ install dmsdos.o -m 644 $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+ install dmsdosd $(EXEC_PREFIX)/dmsdosd
+ install dmsdosfsck $(EXEC_PREFIX)/dmsdosfsck
+ install mcdmsdos $(EXEC_PREFIX)/mcdmsdos
+ install cvflist $(EXEC_PREFIX)/cvflist
+ install cvftest $(EXEC_PREFIX)/cvftest
+
+install-min: min
+ ./check.sh
+ install dmsdos.o $(MODULE_PREFIX)/dmsdos.o
+ install dutil $(EXEC_PREFIX)/dutil
+
+uninstall:
+ rm -f $(MODULE_PREFIX)/dmsdos.o
+ rm -f $(EXEC_PREFIX)/dutil
+ rm -f $(EXEC_PREFIX)/dmsdosd
+ rm -f $(EXEC_PREFIX)/dmsdosfsck
+ rm -f $(EXEC_PREFIX)/mcdmsdos
+ rm -f $(EXEC_PREFIX)/cvflist
+ rm -f $(EXEC_PREFIX)/cvftest
+
+messages:
+ grep DMSDOS ../doc/messages.doc | sort -d -b -f > /tmp/messdoc
+ ./listmsg.sh -LOG > /tmp/messsrc
+ diff -d -U 0 -w /tmp/messdoc /tmp/messsrc | grep DMSDOS
+
+LIB_OBJS = lib_interface.lo dblspace_interface.lo dblspace_dec.lo \
+ dblspace_compr.lo dblspace_methsq.lo dblspace_alloc.lo \
+ dblspace_chk.lo dblspace_tables.lo dstacker_compr.lo \
+ dstacker_dec.lo dstacker_alloc.lo
+
+$(LIB_OBJS): dmsdos.h dmsdos-config.h lib_interface.h
+
+%.lo: %.c ; $(CC) -o $@ $(LCFLAGS) -c $<
+
+dmsdos_library.lo: $(LIB_OBJS)
+ ld -r -o dmsdos_library.lo $^
+
+libdmsdos.a: dmsdos_library.lo
+ ar rcs libdmsdos.a dmsdos_library.lo
+
+libdmsdos.so: dmsdos_library.lo
+ ld -shared -o libdmsdos.so dmsdos_library.lo
+
+dcread: dcread.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+
+mcdmsdos: mcdmsdos.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+dmsdosfsck: dmsdosfsck.c libdmsdos.a dmsdos.h dmsdos-config.h
+ $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+cvftest: cvftest.c
+ $(CC) -Wall -o cvftest cvftest.c
+
+cvflist: cvflist.c
+ $(CC) -Wall -o cvflist cvflist.c
+
+-include depend
diff --git a/src/Menuconfig b/src/Menuconfig
new file mode 100644
index 0000000..196b4fa
--- /dev/null
+++ b/src/Menuconfig
@@ -0,0 +1,1187 @@
+#! /bin/sh
+#
+# This script is used to configure dmsdos options.
+# It is shamelessly stolen from the Linux kernel.
+# For a list of credits see there.
+#
+#----------------------------------------------------------------------------
+
+#
+# Make sure we're really running bash.
+#
+[ -z "$BASH" ] && { echo "Menuconfig requires bash" 1>&2; exit 1; }
+
+#
+# Cache function definitions, turn off posix compliance
+#
+set -h +o posix
+
+
+#
+# If you prefer all kernel options listed in a single menu rather than
+# the standard menu hierarchy, set SINGLE_MENU_MODE to "TRUE" in your
+# environment.
+#
+single_menu_mode="${SINGLE_MENU_MODE:-FALSE}"
+
+
+#
+# Load the functions used by the config.in files.
+#
+# I do this because these functions must be redefined depending
+# on whether they are being called for interactive use or for
+# saving a configuration to a file.
+#
+# Thank the heavens bash supports nesting function definitions.
+#
+load_functions () {
+
+#
+# Additional comments
+#
+function comment () {
+ comment_ctr=$[ comment_ctr + 1 ]
+ echo -ne "': $comment_ctr' '--- $1' " >>MCmenu
+}
+
+#
+# Don't need this yet, but we don't want to puke either.
+#
+function define_bool () {
+ :
+}
+
+#
+# Create a boolean (Yes/No) function for our current menu
+# which calls our local bool function.
+#
+function bool () {
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y|m) flag="*" ;;
+ n) flag=" " ;;
+ esac
+
+ echo -ne "'$2' '[$flag] $1' " >>MCmenu
+
+ echo -e "function $2 () { l_bool '$2' \"\$1\" ;}\n" >>MCradiolists
+}
+
+#
+# Create a tristate (Yes/No/Module) radiolist function
+# which calls our local tristate function.
+#
+# Collapses to a boolean (Yes/No) if module support is disabled.
+#
+function tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]
+ then
+ bool "$1" "$2"
+ else
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y) flag="*" ;;
+ m) flag="M" ;;
+ *) flag=" " ;;
+ esac
+
+ echo -ne "'$2' '<$flag> $1' " >>MCmenu
+
+ echo -e "
+ function $2 () { l_tristate '$2' \"\$1\" ;}" >>MCradiolists
+ fi
+}
+
+#
+# Create a tristate radiolist function which is dependent on
+# another kernel configuration option.
+#
+# Quote from the original configure script:
+#
+# If the option we depend upon is a module,
+# then the only allowable options are M or N. If Y, then
+# this is a normal tristate. This is used in cases where modules
+# are nested, and one module requires the presence of something
+# else in the kernel.
+#
+function dep_tristate () {
+ if [ "$CONFIG_MODULES" != "y" ]
+ then
+ bool "$1" "$2"
+ else
+ if eval [ "_$3" != "_m" ]
+ then
+ tristate "$1" "$2" $3
+ else
+ mod_bool "$1" "$2"
+ fi
+ fi
+}
+
+#
+# Add a menu item which will call our local int function.
+#
+function int () {
+ eval $2=\${$2:-"$3"} x=\$$2
+
+ echo -ne "'$2' '($x) $1' " >>MCmenu
+
+ echo -e "function $2 () { l_int '$1' '$2' '$3' '$x' ;}" >>MCradiolists
+}
+
+#
+# Add a menu item which will call our local int function.
+#
+function hex () {
+ eval $2=\${$2:-"$3"} x=\${$2##*[x,X]}
+
+ echo -ne "'$2' '($x) $1' " >>MCmenu
+
+ echo -e "function $2 () { l_hex '$1' '$2' '$3' '$x' ;}" >>MCradiolists
+}
+
+#
+# Add a menu item which will call our local One-of-Many choice list.
+#
+function choice () {
+ #
+ # Need to remember params cause they're gonna get reset.
+ #
+ title=$1
+ choices=$2
+ default=$3
+ current=
+
+ #
+ # Find out if one of the choices is already set.
+ # If it's not then make it the default.
+ #
+ set -- $choices
+ firstchoice=$2
+
+ while [ -n "$2" ]
+ do
+ if eval [ "_\$$2" = "_y" ]
+ then
+ current=$1
+ break
+ fi
+ shift ; shift
+ done
+
+ : ${current:=$default}
+
+ echo -ne "'$firstchoice' '($current) $title' " >>MCmenu
+
+ echo -e "
+ function $firstchoice () \
+ { l_choice '$title' \"$choices\" $current ;}" >>MCradiolists
+}
+
+} # END load_functions()
+
+
+
+
+
+#
+# Extract available help for an option from Configure.help
+# and send it to standard output.
+#
+# Most of this function was borrowed from the original kernel
+# Configure script.
+#
+function extract_help () {
+ if [ -f Configure.help ]
+ then
+ #first escape regexp special characters in the argument:
+ var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+ #now pick out the right help text:
+ text=$(sed -n "/^$var[ ]*\$/,\${
+ /^$var[ ]*\$/d
+ /^#.*/d
+ /^[ ]*\$/q
+ s/^ //
+ p
+ }" Configure.help)
+
+ if [ -z "$text" ]
+ then
+ echo "There is no help available for this dmsdos option."
+ return 1
+ else
+ echo "$text"
+ fi
+ else
+ echo "There is no help available for this dmsdos option."
+ return 1
+ fi
+}
+
+#
+# Activate a help dialog.
+#
+function help () {
+ if extract_help $1 >help.out
+ then
+ $DIALOG --backtitle "$backtitle" --title "$2"\
+ --textbox help.out $ROWS $COLS
+ else
+ $DIALOG --backtitle "$backtitle" \
+ --textbox help.out $ROWS $COLS
+ fi
+ rm help.out
+}
+
+#
+# Show the README file.
+#
+function show_readme () {
+ $DIALOG --backtitle "$backtitle" \
+ --textbox scripts/README.Menuconfig $ROWS $COLS
+}
+
+#
+# Begin building the dialog menu command and Initialize the
+# Radiolist function file.
+#
+function menu_name () {
+ echo -ne "$DIALOG --title '$1'\
+ --backtitle '$backtitle' \
+ --menu '$menu_instructions' \
+ $ROWS $COLS $((ROWS-10)) \
+ '$default' " >MCmenu
+ >MCradiolists
+}
+
+#
+# Add a submenu option to the menu currently under construction.
+#
+function submenu () {
+ echo -ne "'activate_menu $2' '$1 --->' " >>MCmenu
+}
+
+#
+# Create a menu entry to handle the traditional sound configuration.
+#
+function soundcfg () {
+ echo -ne "'l_soundcfg' "\
+ "'Old configuration script "\
+ "(For: SM Wave, PSS & AudioTrix Pro) -->' " >>MCmenu
+}
+
+#
+# Startup the traditional sound configuration program.
+#
+function l_soundcfg () {
+ clear
+ $MAKE -C drivers/sound config
+}
+
+#
+# Handle a boolean (Yes/No) option.
+#
+function l_bool () {
+ if [ -n "$2" ]
+ then
+ case "$2" in
+ y|m) eval $1=y ;;
+ c) eval x=\$$1
+ case $x in
+ y) eval $1=n ;;
+ n) eval $1=y ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Same as bool() except options are (Module/No)
+#
+function mod_bool () {
+ eval $2=\${$2:-'n'} x=\$$2
+
+ case $x in
+ y|m) flag='M' ;;
+ *) flag=' ' ;;
+ esac
+
+ echo -ne "'$2' '<$flag> $1' " >>MCmenu
+
+ echo -e "function $2 () { l_mod_bool '$2' \"\$1\" ;}" >>MCradiolists
+}
+
+#
+# Same as l_bool() except options are (Module/No)
+#
+function l_mod_bool() {
+ if [ -n "$2" ]
+ then
+ case "$2" in
+ y) echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "\
+This feature depends on another which has been configured as a module. \
+As a result, this feature will be built as a module." 4 70
+ sleep 5
+ eval $1=m ;;
+ m) eval $1=m ;;
+ c) eval x=\$$1
+ case $x in
+ m) eval $1=n ;;
+ n) eval $1=m ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Handle a tristate (Yes/No/Module) option.
+#
+function l_tristate () {
+ if [ -n "$2" ]
+ then
+ eval x=\$$1
+
+ case "$2" in
+ y) eval $1=y ;;
+ m) eval $1=m ;;
+ c) eval x=\$$1
+ case $x in
+ y) eval $1=n ;;
+ n) eval $1=m ;;
+ m) eval $1=y ;;
+ esac ;;
+ *) eval $1=n ;;
+ esac
+ else
+ echo -ne "\007"
+ fi
+}
+
+#
+# Create a dialog for entering an integer into a kernel option.
+#
+function l_int () {
+ while true
+ do
+ if $DIALOG --title "$1" \
+ --backtitle "$backtitle" \
+ --inputbox "$inputbox_instructions_int" \
+ 10 75 "$4" 2>MCdialog.out
+ then
+ answer="`cat MCdialog.out`"
+ answer="${answer:-$3}"
+
+ # Avoid problems with GNU vs POSIX expr semantics.
+ if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
+ then
+ eval $2="$answer"
+ else
+ eval $2="$3"
+ echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "You have made an invalid entry." 3 43
+ sleep 2
+ fi
+
+ break
+ fi
+
+ help "$2" "$1"
+ done
+}
+
+#
+# Create a dialog for entering a hexadecimal into a kernel option.
+#
+function l_hex () {
+ while true
+ do
+ if $DIALOG --title "$1" \
+ --backtitle "$backtitle" \
+ --inputbox "$inputbox_instructions_hex" \
+ 10 75 "$4" 2>MCdialog.out
+ then
+ answer="`cat MCdialog.out`"
+ answer="${answer:-$3}"
+ answer="${answer##*[x,X]}"
+
+ # Avoid problems with GNU vs POSIX expr semantics.
+ if expr "$answer" : '[0-9a-fA-F][0-9a-fA-F]*$' >/dev/null
+ then
+ eval $2="$answer"
+ else
+ eval $2="$3"
+ echo -en "\007"
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "You have made an invalid entry." 3 43
+ sleep 2
+ fi
+
+ break
+ fi
+
+ help "$2" "$1"
+ done
+}
+
+#
+# Handle a one-of-many choice list.
+#
+function l_choice () {
+ #
+ # Need to remember params cause they're gonna get reset.
+ #
+ title="$1"
+ choices="$2"
+ current="$3"
+
+ #
+ # Scan current value of choices and set radiolist switches.
+ #
+ list=
+ set -- $choices
+ firstchoice=$2
+ while [ -n "$2" ]
+ do
+ case "$1" in
+ "$current") list="$list $2 $1 ON " ;;
+ *) list="$list $2 $1 OFF " ;;
+ esac
+
+ shift ; shift
+ done
+
+ while true
+ do
+ if $DIALOG --title "$title" \
+ --backtitle "$backtitle" \
+ --radiolist "$radiolist_instructions" \
+ 15 70 6 $list 2>MCdialog.out
+ then
+ choice=`cat MCdialog.out`
+ break
+ fi
+
+ help "$firstchoice" "$title"
+ done
+
+ #
+ # Now set the boolean value of each option based on
+ # the selection made from the radiolist.
+ #
+ set -- $choices
+ while [ -n "$2" ]
+ do
+ if [ "$2" = "$choice" ]
+ then
+ eval $2="y"
+ else
+ eval $2="n"
+ fi
+
+ shift ; shift
+ done
+}
+
+
+#
+# A faster awk based recursive parser. (I hope)
+#
+function parser1 () {
+awk '
+BEGIN {
+ menu_no = 0
+ comment_is_option = 0
+ parser("'$CONFIG_IN'","MCmenu0")
+}
+
+function parser(ifile,menu) {
+
+ while (getline <ifile) {
+ if ($1 == "mainmenu_option") {
+ comment_is_option = "1"
+ }
+ else if ($1 == "comment" && comment_is_option == "1") {
+ comment_is_option= "0"
+ sub($1,"",$0)
+ ++menu_no
+
+ printf("submenu %s MCmenu%s\n", $0, menu_no) >>menu
+
+ printf( "function MCmenu%s () {\n"\
+ "default=$1\n"\
+ "menu_name %s\n",\
+ menu_no, $0) >"MCmenu"menu_no
+
+ parser(ifile, "MCmenu"menu_no)
+ }
+ else if ($1 ~ "endmenu") {
+ printf("}\n") >>menu
+ return
+ }
+ else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
+ printf("") >>menu
+ }
+ else if ($1 == "source") {
+ # Yuk! Blah! Phooey!
+ if ($2 ~ "drivers/sound") {
+ printf("soundcfg\n") >>menu
+ }
+
+ parser($2,menu)
+ }
+ else {
+ print >>menu
+ }
+ }
+}'
+}
+
+#
+# Secondary parser for single menu mode.
+#
+function parser2 () {
+awk '
+BEGIN {
+ parser("'$CONFIG_IN'","MCmenu0")
+}
+
+function parser(ifile,menu) {
+
+ while (getline <ifile) {
+ if ($1 ~ /mainmenu_option|endmenu/) {
+ printf("") >>menu
+ }
+ else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
+ printf("") >>menu
+ }
+ else if ($1 == "source") {
+ if ($2 ~ "drivers/sound") {
+ printf("soundcfg\n") >>menu
+ }
+ parser($2,menu)
+ }
+ else {
+ print >>menu
+ }
+ }
+}'
+}
+
+#
+# Parse all the config.in files into mini scripts.
+#
+function parse_config_files () {
+ rm -f MCmenu*
+
+ echo "function MCmenu0 () {" >MCmenu0
+ echo 'default=$1' >>MCmenu0
+ echo "menu_name 'Main Menu'" >>MCmenu0
+
+ if [ "_$single_menu_mode" = "_TRUE" ]
+ then
+ parser2
+ else
+ parser1
+ fi
+
+ echo "comment ''" >>MCmenu0
+ echo "g_alt_config" >>MCmenu0
+ echo "s_alt_config" >>MCmenu0
+
+ echo "}" >>MCmenu0
+
+ #
+ # These mini scripts must be sourced into the current
+ # environment in order for all of this to work. Leaving
+ # them on the disk as executables screws up the recursion
+ # in activate_menu(), among other things. Once they are
+ # sourced we can discard them.
+ #
+ for i in MCmenu*
+ do
+ source ./$i
+ done
+
+ rm -f MCmenu*
+}
+
+#
+# This is the menu tree's bootstrap.
+#
+# Executes the parsed menus on demand and creates a set of functions,
+# one per configuration option. These functions will in turn execute
+# dialog commands or recursively call other menus.
+#
+function activate_menu () {
+ while true
+ do
+ comment_ctr=0 #So comment lines get unique tags
+
+ $1 "$default" #Create the lxdialog menu & functions
+
+ if [ "$?" != "0" ]
+ then
+ clear
+ cat <<EOM
+
+Menuconfig has encountered a possible error in one of the kernel's
+configuration files and is unable to continue.
+
+Please report this to the author <roadcapw@titus.org>. You may also
+send a problem report to linux-kernel@vger.rutgers.edu or post a
+message to the linux.dev.kernel news group.
+
+Please indicate the kernel version you are trying to configure and
+which menu you were trying to enter when this error occurred.
+
+EOM
+ cleanup
+ exit 1
+ fi
+
+ . ./MCradiolists #Source the menu's functions
+
+ . ./MCmenu 2>MCdialog.out #Activate the lxdialog menu
+ ret=$?
+
+ read selection <MCdialog.out
+
+ case "$ret" in
+ 0|3|4|5|6)
+ defaults="$selection$defaults" #pseudo stack
+ case "$ret" in
+ 0) eval $selection ;;
+ 3) eval $selection y ;;
+ 4) eval $selection n ;;
+ 5) eval $selection m ;;
+ 6) eval $selection c ;;
+ esac
+ default="${defaults%%*}" defaults="${defaults#*}"
+ ;;
+ 2)
+ default="${selection%%\ *}"
+
+ case "$selection" in
+ *"-->"*|*"alt_config"*)
+ show_readme ;;
+ *)
+ eval help $selection ;;
+ esac
+ ;;
+ 255|1)
+ break
+ ;;
+ 139)
+ stty sane
+ clear
+ cat <<EOM
+
+There seems to be a problem with the lxdialog companion utility which is
+built prior to running Menuconfig. Usually this is an indicator that you
+have upgraded/downgraded your ncurses libraries and did not remove the
+old ncurses header file(s) in /usr/include or /usr/include/ncurses.
+
+It is VERY important that you have only one set of ncurses header files
+and that those files are properly version matched to the ncurses libraries
+installed on your machine.
+
+You may also need to rebuild lxdialog. This can be done by moving to
+the /usr/src/linux/scripts/lxdialog directory and issuing the
+"make clean all" command.
+
+If you have verified that your ncurses install is correct, you may email
+the author <roadcapw@titus.org> or post a message on the linux.dev.kernel
+news group for additional assistance.
+
+EOM
+ cleanup
+ exit 139
+ ;;
+ esac
+ done
+}
+
+#
+# Create a menu item to load an alternate configuration file.
+#
+g_alt_config () {
+ echo -n "get_alt_config 'Load an Alternate Configuration File' "\
+ >>MCmenu
+}
+
+#
+# Get alternate config file name and load the
+# configuration from it.
+#
+get_alt_config () {
+ set -f ## Switch file expansion OFF
+
+ while true
+ do
+ ALT_CONFIG="${ALT_CONFIG:-$_CONFIG}"
+
+ $DIALOG --backtitle "$backtitle" \
+ --inputbox "\
+Enter the name of the configuration file you wish to load. \
+Accept the name shown to restore the configuration you \
+last retrieved. Leave blank to abort."\
+ 11 55 "$ALT_CONFIG" 2>MCdialog.out
+
+ if [ "$?" = "0" ]
+ then
+ ALT_CONFIG=`cat MCdialog.out`
+
+ [ "_" = "_$ALT_CONFIG" ] && break
+
+ if eval [ -r "$ALT_CONFIG" ]
+ then
+ eval load_config_file "$ALT_CONFIG"
+ break
+ else
+ echo -ne "\007"
+ $DIALOG --backtitle "$backtitle" \
+ --infobox "File does not exist!" 3 38
+ sleep 2
+ fi
+ else
+ cat <<EOM >help.out
+
+For various reasons, one may wish to keep several different kernel
+configurations available on a single machine.
+
+If you have saved a previous configuration in a file other than the
+kernel's default, entering the name of the file here will allow you
+to modify that configuration.
+
+If you are uncertain, then you have probably never used alternate
+configuration files. You should therefor leave this blank to abort.
+
+EOM
+ $DIALOG --backtitle "$backtitle"\
+ --title "Load Alternate Configuration"\
+ --textbox help.out $ROWS $COLS
+ fi
+ done
+
+ set +f ## Switch file expansion ON
+ rm -f help.out MCdialog.out
+}
+
+#
+# Create a menu item to store an alternate config file.
+#
+s_alt_config () {
+ echo -n "save_alt_config 'Save Configuration to an Alternate File' "\
+ >>MCmenu
+}
+
+#
+# Get an alternate config file name and save the current
+# configuration to it.
+#
+save_alt_config () {
+ set -f ## Switch file expansion OFF
+
+ while true
+ do
+
+ $DIALOG --backtitle "$backtitle" \
+ --inputbox "\
+Enter a filename to which this configuration should be saved \
+as an alternate. Leave blank to abort."\
+ 10 55 "$ALT_CONFIG" 2>MCdialog.out
+
+ if [ "$?" = "0" ]
+ then
+ ALT_CONFIG=`cat MCdialog.out`
+
+ [ "_" = "_$ALT_CONFIG" ] && break
+
+ if eval touch $ALT_CONFIG 2>/dev/null
+ then
+ eval save_configuration $ALT_CONFIG
+ load_functions ## RELOAD
+ break
+ else
+ echo -ne "\007"
+ $DIALOG --backtitle "$backtitle" \
+ --infobox "Can't create file! Probably a nonexistent directory." 3 60
+ sleep 2
+ fi
+ else
+ cat <<EOM >help.out
+
+For various reasons, one may wish to keep different
+configurations available on a single machine.
+
+Entering a file name here will allow you to later retrieve, modify
+and use the current configuration as an alternate to whatever
+configuration options you have selected at that time.
+
+If you are uncertain what all this means then you should probably
+leave this blank.
+EOM
+ $DIALOG --backtitle "$backtitle"\
+ --title "Save Alternate Configuration"\
+ --textbox help.out $ROWS $COLS
+ fi
+ done
+
+ set +f ## Switch file expansion ON
+ rm -f help.out MCdialog.out
+}
+
+
+#
+# Load config file into the environment converting all
+# "# OPTION is not set" lines to "OPTION=n".
+#
+# The $ARCH defaults are loaded first so "new"/previously
+# unconfigured parameters are assigned the proper defaults.
+#
+function load_config_file () {
+ eval "`sed -e 's/# \(.*\) is not set.*/\1=n/' dmsdos-config.default $1`"
+}
+
+
+#
+# Just what it says.
+#
+save_configuration () {
+ ${DIALOG} --backtitle "$backtitle" \
+ --infobox "Saving dmsdos configuration..." 3 40
+
+ #
+ # Now, let's redefine the configuration functions for final
+ # output to the config files.
+ #
+ # Nested function definitions, YIPEE!
+ #
+ function bool () {
+ eval define_bool "$2" "\${$2:-n}"
+ }
+
+ function tristate () {
+ eval define_bool "$2" "\${$2:-n}"
+ }
+
+ function dep_tristate () {
+ eval x=\${$2:-n}
+
+ if eval [ "_$3" = "_m" ]
+ then
+ if [ "$x" = "y" ]
+ then
+ x="m"
+ fi
+ fi
+
+ define_bool "$2" "$x"
+ }
+
+ function int () {
+ eval x=\${$2:-"$3"}
+ echo "$2=$x" >>$CONFIG
+ echo "#define $2 ($x)" >>$CONFIG_H
+ }
+
+ function hex () {
+ eval x=\${$2:-"$3"}
+ echo "$2=$x" >>$CONFIG
+ echo "#define $2 0x${x##*[x,X]}" >>$CONFIG_H
+ }
+
+ function define_bool () {
+ eval $1="$2"
+
+ case "$2" in
+ y)
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ ;;
+
+ m)
+ if [ "$CONFIG_MODULES" = "y" ]
+ then
+ echo "$1=m" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ echo "#define $1_MODULE 1" >>$CONFIG_H
+ else
+ echo "$1=y" >>$CONFIG
+ echo "#define $1 1" >>$CONFIG_H
+ fi
+ ;;
+
+ n)
+ echo "# $1 is not set" >>$CONFIG
+ echo "#undef $1" >>$CONFIG_H
+ ;;
+ esac
+ }
+
+ function choice () {
+ #
+ # Find the first choice that's already set to 'y'
+ #
+ choices="$2"
+ default="$3"
+ current=
+
+ set -- $choices
+ while [ -n "$2" ]
+ do
+ if eval [ "_\$$2" = "_y" ]
+ then
+ current=$1
+ break
+ fi
+ shift ; shift
+ done
+
+ #
+ # Use the default if none were set.
+ #
+ : ${current:=$default}
+
+ #
+ # Then extract the actual option from the list of choices.
+ #
+ current=${choices#*$current} ; set $current
+
+ define_bool "$1" "y"
+ }
+
+ function mainmenu_name () {
+ :
+ }
+
+ function mainmenu_option () {
+ comment_is_option=TRUE
+ }
+
+ function endmenu () {
+ :
+ }
+
+ function comment () {
+ if [ "$comment_is_option" ]
+ then
+ comment_is_option=
+ echo >>$CONFIG
+ echo "#" >>$CONFIG
+ echo "# $1" >>$CONFIG
+ echo "#" >>$CONFIG
+
+ echo >>$CONFIG_H
+ echo "/*" >>$CONFIG_H
+ echo " * $1" >>$CONFIG_H
+ echo " */" >>$CONFIG_H
+ fi
+ }
+
+ DEF_CONFIG="${1:-$_CONFIG}"
+ DEF_CONFIG_H="$AUTOCONF_H"
+
+ CONFIG=.tmpconfig
+ CONFIG_H=.tmpconfig.h
+
+ echo "#" >$CONFIG
+ echo "# Automatically generated by make menuconfig: don't edit" >>$CONFIG
+ echo "#" >>$CONFIG
+
+ echo "/*" >$CONFIG_H
+ echo " * Automatically generated by make menuconfig: don't edit" >>$CONFIG_H
+ echo " * Use make menuconfig instead." >>$CONFIG_H
+ echo " */" >>$CONFIG_H
+
+ MAKE=: #To prevent sound Makefile from running.
+
+ if . $CONFIG_IN >>.menuconfig.log 2>&1
+ then
+ if [ -f "$DEF_CONFIG" ]
+ then
+ rm -f ${DEF_CONFIG}.old
+ mv $DEF_CONFIG ${DEF_CONFIG}.old
+ fi
+
+ mv $CONFIG $DEF_CONFIG
+ mv $CONFIG_H $DEF_CONFIG_H
+
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+#
+# Remove temporary files
+#
+cleanup () {
+ cleanup1
+ cleanup2
+ stty $S_TERMIO
+}
+
+cleanup1 () {
+ rm -f MCmenu* MCradiolists MCdialog.out help.out
+}
+
+cleanup2 () {
+ rm -f .tmpconfig .tmpconfig.h
+}
+
+set_geometry () {
+ # Some distributions export these with incorrect values
+ # which can really screw up some ncurses programs.
+ LINES= COLUMNS=
+
+ ROWS=${1:-24} COLS=${2:-80}
+
+ # Just in case the nasty rlogin bug returns.
+ #
+ [ $ROWS = 0 ] && ROWS=24
+ [ $COLS = 0 ] && COLS=80
+
+ if [ $ROWS -lt 19 -o $COLS -lt 80 ]
+ then
+ echo -e "\n\007Your display is too small to run Menuconfig!"
+ echo "It must be at least 19 lines by 80 columns."
+ exit 0
+ fi
+
+ ROWS=$((ROWS-4)) COLS=$((COLS-5))
+}
+
+S_TERMIO=`stty -g`
+
+set_geometry `stty size 2>/dev/null`
+
+menu_instructions="\
+Arrow keys navigate the menu. \
+<Enter> selects submenus --->. \
+Highlighted letters are hotkeys. \
+Pressing <Y> includes, <N> excludes features. \
+Press <Esc><Esc> to exit, <?> for Help. \
+Legend: [*] built-in [ ] excluded "
+
+radiolist_instructions="\
+Use the arrow keys to navigate this window or \
+press the hotkey of the item you wish to select \
+followed by the <SPACE BAR>.
+Press <?> for additional information about this option."
+
+inputbox_instructions_int="\
+Please enter a decimal value. \
+Fractions will not be accepted. \
+Use the <TAB> key to move from the input field to the buttons below it."
+
+inputbox_instructions_hex="\
+Please enter a hexadecimal value. \
+Use the <TAB> key to move from the input field to the buttons below it."
+
+DIALOG="${DIALOG:-/usr/src/linux/scripts/lxdialog/lxdialog}"
+
+if [ ! -x $DIALOG ];
+then
+ echo "Compiling lxdialog in kernel source tree..."
+ make -C /usr/src/linux/scripts/lxdialog lxdialog
+ if [ "$?" != "0" ];
+ then
+ echo "Something's wrong here. Sorry, I don't know what."
+ exit 1
+ fi
+fi
+
+backtitle="Dmsdos CVF-FAT Module Configuration"
+
+trap "cleanup ; rm -f .menuconfig ; exit 1" 1 2 15
+
+
+#
+# Locate default files.
+#
+DEFAULTS="dmsdos-config.default"
+
+CONFIG_IN="${1:-./Config.in}"
+
+_CONFIG="${2:-.config}"
+
+AUTOCONF_H="dmsdos-config.h"
+
+if [ -f "$_CONFIG" ]; then
+ DEFAULTS=$_CONFIG
+fi
+
+if [ -f $DEFAULTS ]
+then
+ echo
+ echo "Using defaults found in" $DEFAULTS
+ load_config_file $DEFAULTS
+else
+ echo
+ echo "No defaults found"
+fi
+
+# Fresh new log.
+>.menuconfig.log
+
+$DIALOG --backtitle "$backtitle" \
+ --infobox "Preparing configuration scripts..." 3 40
+
+# Load the functions used by the config.in files.
+load_functions
+
+#
+# Read config.in files and parse them into one shell function per menu.
+#
+parse_config_files $CONFIG_IN
+
+#
+# Start the ball rolling from the top.
+#
+activate_menu MCmenu0
+
+#
+# All done!
+#
+cleanup1
+
+#
+# Confirm and Save
+#
+if $DIALOG --backtitle "$backtitle" \
+ --yesno "Do you wish to save your new dmsdos configuration?" 5 60
+
+then
+ save_configuration
+ stty $S_TERMIO
+ clear
+
+ cat <<EOM
+
+
+The dmsdos module is now configured. Do a 'make clean; make' to (re)compile.
+
+EOM
+else
+ clear
+ stty $S_TERMIO
+ echo -e "Your dmsdos configuration changes were NOT saved.\n"
+ exit 1
+fi
+
+
+exit 0
+
diff --git a/src/cctest.c b/src/cctest.c
new file mode 100644
index 0000000..ba88500
--- /dev/null
+++ b/src/cctest.c
@@ -0,0 +1,14 @@
+#include<stdio.h>
+
+void main(void)
+{ int x=1;
+ char*p=(char*)&x;
+
+ printf("sizeof(char)=%d\n",sizeof(char));
+ printf("sizeof(int)=%d\n",sizeof(int));
+ printf("sizeof(short)=%d\n",sizeof(short));
+ printf("sizeof(long)=%d\n",sizeof(long));
+
+ if(*p==0)printf("big endian\n");
+ else printf("little endian\n");
+} \ No newline at end of file
diff --git a/src/check.sh b/src/check.sh
new file mode 100644
index 0000000..e26da29
--- /dev/null
+++ b/src/check.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+echo "Checking kernel configuration..."
+
+if [ ! -f /usr/src/linux/Makefile ];
+then
+ echo "Hmm, no Linux sources found in /usr/src/linux"
+ exit 0
+fi
+
+head -3 /usr/src/linux/Makefile | tr -d " " > /tmp/help.$$
+source /tmp/help.$$
+rm /tmp/help.$$
+VPS=`echo "$VERSION.$PATCHLEVEL.$SUBLEVEL"`
+VP=`echo "$VERSION.$PATCHLEVEL"`
+
+if [ ! -f /usr/src/linux/.config ];
+then
+ echo "Hmm, kernel is not configured."
+ exit 0
+fi
+
+CONFIG_MODULES=n
+CONFIG_BLK_DEV_LOOP=n
+CONFIG_FAT_FS=n
+CONFIG_UMSDOS_FS=n
+
+source /usr/src/linux/.config
+
+WARN=n
+
+if [ "$CONFIG_MODULES" = "n" -o "$CONFIG_BLK_DEV_LOOP" = "n" -o "$CONFIG_FAT_FS" = "n" ];
+then
+ echo
+ echo "WARNING: Your kernel is not configured correctly for dmsdos."
+ echo "The following feature(s) is(are) missing:"
+if [ "$CONFIG_MODULES" = "n" ];
+then
+ echo " - loadable module support"
+fi
+if [ "$CONFIG_BLK_DEV_LOOP" = "n" ];
+then
+ echo " - loopback block device"
+fi
+if [ "$CONFIG_FAT_FS" = "n" ];
+then
+ echo " - fat filesystem"
+fi
+ echo "Please correct this later since dmsdos won't run without."
+ WARN=y
+fi
+
+if [ "$VP" = "2.1" -a "$CONFIG_UMSDOS_FS" != "n" -a "$SUBLEVEL" -lt 94 ];
+then
+ echo
+ echo "ERROR: you have enabled UMSDOS in $VPS kernel."
+ echo "This configuration cannot work due to incompatibility."
+ echo "Please upgrade your kernel to at least 2.1.94 or disable UMSDOS."
+ echo "Please read the documentation for details."
+ exit 1
+fi
+
+if [ "$VP" = "2.0" -a "$SUBLEVEL" -lt 29 ];
+then
+ echo
+ echo "WARNING: your kernel version $VPS is horribly outdated."
+ echo "This configuration has never been tested. If it fails please"
+ echo "upgrade your kernel to 2.0.29 or newer."
+ echo "Please read the documentation for details."
+ WARN=y
+fi
+
+if [ "$VP" = "2.1" -a "$SUBLEVEL" -lt 76 ];
+then
+ echo
+ echo "ERROR: your kernel version $VPS is too old for CVF-FAT."
+ echo "This configuration is unsupported."
+ echo "Please upgrade your kernel to 2.1.94 or newer or use 2.0.xx (xx>=29)."
+ echo "Please read the documentation for details."
+ exit 1
+fi
+
+if [ "$WARN" = "y" ];
+then
+ echo
+ echo "Press ENTER to continue."
+ read junk
+else
+ echo "kernel configuration is OK."
+fi
+
+exit 0
diff --git a/src/cmpdisk.README b/src/cmpdisk.README
new file mode 100644
index 0000000..bace4de
--- /dev/null
+++ b/src/cmpdisk.README
@@ -0,0 +1,3 @@
+cmpdisk is a DOS program. See doc/testing.doc for details. The sources are in
+cmpdisk.cpp. You must compile them under DOS, preferrably with a 16 bit dos
+C compiler. The code was written and tested under Borland C++ 3.1.
diff --git a/src/cmpdisk.cpp b/src/cmpdisk.cpp
new file mode 100644
index 0000000..d2335c7
--- /dev/null
+++ b/src/cmpdisk.cpp
@@ -0,0 +1,131 @@
+
+/* BC 3.1 source code for something similar to simple diff */
+
+#include <string.h>
+#include <dos.h>
+#include <dir.h>
+#include <stdio.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#define COMPAREBUFLEN (1024*16)
+char comparebuf1[COMPAREBUFLEN];
+char comparebuf2[COMPAREBUFLEN];
+
+int TestAllFiles(char *path1,char *path2)
+{
+ int old_path_len1,old_path_len2;
+ int done,file1,file2,i;
+ struct ffblk s;
+ int len1,len2;
+ long int pos;
+
+ old_path_len1=strlen(path1);
+ old_path_len2=strlen(path2);
+ strcat(path1,"*.*");
+ done=findfirst(path1,&s,0x2f);
+ path1[old_path_len1]=0;
+
+ while(!done)
+ {
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ strcat(path1,s.ff_name);
+ strcat(path2,s.ff_name);
+
+ printf("File in progress %s",path1);
+
+ if ((file1=open(path1, O_RDONLY | O_BINARY , S_IREAD))==-1)
+ {printf("\nCannot open file: %s \n",path1);goto error;}
+ else if ((file2=open(path2, O_RDONLY | O_BINARY , S_IREAD))==-1)
+ {printf("\nCannot open file: %s \n",path2);close(file1);goto error;}
+ else
+ {
+ len2=len1=COMPAREBUFLEN;pos=0;
+ while((len1==COMPAREBUFLEN)&&(len2==COMPAREBUFLEN))
+ {
+ if((len1=read(file1,comparebuf1,COMPAREBUFLEN))==-1)
+ {
+ printf("\ndisk IO error!: cannot read file %s\n",path1);
+ goto error_close;
+ }
+ if((len2=read(file2,comparebuf2,COMPAREBUFLEN))==-1)
+ {
+ printf("\ndisk IO error!: cannot read file %s\n",path2);
+ goto error_close;
+ };
+ if(len1) if(memcmp(comparebuf1,comparebuf2,len1))
+ {
+ for(i=0;(i<len1)&&(*(comparebuf1+i)==*(comparebuf2+i));i++);
+ printf("\nCompare Error %s on pos %X!\n",path1,pos+i);
+ goto error_close;
+ }
+ pos+=len1;
+ }
+ if(len1!=len2)
+ {
+ printf("\nDifferent lengths of files %s and %s",path1,path2);
+ error_close:
+ close(file2);
+ close(file1);
+ error:
+ if(getchar()=='q') return(1);
+ break;
+ }else{
+ printf(" ... compared %ld bytes\n",pos);
+ close(file2);
+ close(file1);
+ };
+ }
+ done=findnext(&s);
+ }
+
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ strcat(path1,"*.*");
+ done=findfirst(path1,&s,FA_DIREC | 0x7);
+ path1[old_path_len1]=0;
+
+
+ while(!done)
+ {
+ if(*s.ff_name != '.')
+ {
+ if((s.ff_attrib & FA_DIREC)!=0)
+ {
+ strcat(path1,s.ff_name);
+ strcat(path1,"\\");
+ path1[old_path_len1+strlen(s.ff_name)+1]=0;
+ strcat(path2,s.ff_name);
+ strcat(path2,"\\");
+ path1[old_path_len2+strlen(s.ff_name)+1]=0;
+ TestAllFiles(path1,path2);
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+ }
+ }
+ done=findnext(&s);
+ }
+ path1[old_path_len1]=0;
+ path2[old_path_len2]=0;
+
+ return 0;
+}
+
+int main(int argc,char *argv[])
+{
+ if(argc<3)
+ {
+ printf("usage: cmpdisk <d>: <d>:\n");
+ return(0);
+ };
+
+ {
+ char path1[255],path2[255];
+ strcpy(path1,argv[1]);
+ strcpy(path2,argv[2]);
+ TestAllFiles(path1,path2);
+ };
+ return(0);
+}; \ No newline at end of file
diff --git a/src/conf-wrapper.sh b/src/conf-wrapper.sh
new file mode 100644
index 0000000..daa1d4a
--- /dev/null
+++ b/src/conf-wrapper.sh
@@ -0,0 +1,14 @@
+#/bin/sh
+
+echo
+echo "dmsdos seems not configured. I'm going to call dmsdos configuration now."
+echo "If you do not want me to do this, press CTRL-C. Press Enter to continue."
+read junk
+
+CT=config
+if [ -f /usr/src/linux/scripts/lxdialog/lxdialog ];
+then
+CT=menuconfig
+fi
+
+make $CT
diff --git a/src/config.debug b/src/config.debug
new file mode 100644
index 0000000..966307d
--- /dev/null
+++ b/src/config.debug
@@ -0,0 +1,62 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+# USE_READA_LIST is not set
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+SEQLOG=y
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+# INTERNAL_DAEMON is not set
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+SP_BIT3=y
+# SP_BIT4 is not set
+# SP_BIT5 is not set
+# SP_BIT6 is not set
+# SP_BIT7 is not set
diff --git a/src/config.high-traffic-write b/src/config.high-traffic-write
new file mode 100644
index 0000000..936c0ea
--- /dev/null
+++ b/src/config.high-traffic-write
@@ -0,0 +1,64 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+INTERNAL_DAEMON=y
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+SP_BIT2=y
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
diff --git a/src/config.low-traffic-write b/src/config.low-traffic-write
new file mode 100644
index 0000000..5140819
--- /dev/null
+++ b/src/config.low-traffic-write
@@ -0,0 +1,64 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+INTERNAL_DAEMON=y
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+# SP_BIT7 is not set
diff --git a/src/config.small-module b/src/config.small-module
new file mode 100644
index 0000000..36fb7e3
--- /dev/null
+++ b/src/config.small-module
@@ -0,0 +1,63 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+NOLOG=y
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal Compression Daemon
+#
+# INTERNAL_DAEMON is not set
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+SP_BIT3=y
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
diff --git a/src/cvflist.c b/src/cvflist.c
new file mode 100644
index 0000000..ca86cac
--- /dev/null
+++ b/src/cvflist.c
@@ -0,0 +1,264 @@
+/*
+cvflist.c
+
+DMSDOS: utility that lists the names of CVFs inside a FAT12 or FAT16 MSDOS fs
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<string.h>
+#include<malloc.h>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include<unistd.h>
+#include<fcntl.h>
+
+#define fat_boot_sector msdos_boot_sector
+
+/* some interface hacks */
+#include"lib_interface.h"
+#undef MALLOC
+#undef FREE
+#undef CURRENT_TIME
+#undef memcpy
+#undef memset
+#define MALLOC malloc
+#define FREE free
+#define kmalloc(x,y) malloc(x)
+#define kfree free
+#define CURRENT_TIME time(NULL)
+
+#ifndef cpu_to_le16
+/* works only for old kernels and little endian architecture */
+#define cpu_to_le16(v) (v)
+#define cpu_to_le32(v) (v)
+#endif
+
+#define MSDOS_FAT12 4078 /* maximum number of clusters in a 12 bit FAT */
+
+struct buffer_head* raw_bread(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(lseek(fd,block*512,SEEK_SET)<0)return NULL;
+ bh=malloc(sizeof(struct buffer_head));
+ if(bh==NULL)return NULL;
+
+ bh->b_data=malloc(512);
+ if(bh->b_data==NULL)
+ { free(bh);
+ return NULL;
+ }
+
+ bh->b_blocknr=block;
+
+ if(read(fd,bh->b_data,512)==512)return bh;
+
+ free(bh->b_data);
+ free(bh);
+
+ return NULL;
+}
+
+void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+{ if(bh==NULL)return;
+ free(bh->b_data);
+ free(bh);
+}
+
+int list_cvfs(struct super_block*sb)
+{ int i,j,testvers;
+ struct buffer_head* bh;
+ struct msdos_dir_entry* data;
+ char cvfname[20];
+
+ /* scan the root directory for a CVF */
+
+ for(i=0;i<MSDOS_SB(sb)->dir_entries/MSDOS_DPS;++i)
+ { bh=raw_bread(sb,MSDOS_SB(sb)->dir_start+i);
+ if(bh==NULL)
+ { fprintf(stderr,"unable to read msdos root directory\n");
+ return -1;
+ }
+ data=(struct msdos_dir_entry*) bh->b_data;
+
+ for(j=0;j<MSDOS_DPS;++j)
+ { testvers=0;
+ if(strncmp(data[j].name,"DRVSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"DBLSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"STACVOL ",8)==0)testvers=2;
+ if(testvers)
+ { if( ( data[j].name[8]>='0'&&data[j].name[8]<='9'
+ &&data[j].name[9]>='0'&&data[j].name[9]<='9'
+ &&data[j].name[10]>='0'&&data[j].name[10]<='9'
+ ) | (testvers==2&&strncmp(data[j].name+8,"DSK",3)==0)
+ )
+ { /* it is a CVF */
+ strncpy(cvfname,data[j].name,9-testvers);
+ cvfname[9-testvers]='\0';
+ strcat(cvfname,".");
+ strncat(cvfname,data[j].ext,3);
+ printf("%s\n",cvfname);
+ }
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ return 0;
+}
+
+/*okay, first thing is setup super block*/
+/* stolen from fatfs */
+/* Read the super block of an MS-DOS FS. */
+
+int read_super(struct super_block *sb)
+{
+ struct buffer_head *bh;
+ struct fat_boot_sector *b;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int error,fat=0;
+ int blksize = 512;
+
+ MSDOS_SB(sb)->cvf_format=NULL;
+ MSDOS_SB(sb)->private_data=NULL;
+
+ blksize = 512;
+
+ bh = raw_bread(sb, 0);
+ if (bh == NULL) {
+ raw_brelse (sb, bh);
+ sb->s_dev = 0;
+ fprintf(stderr,"cannot read file\n");
+ return -1;
+ }
+ b = (struct fat_boot_sector *) bh->b_data;
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ *
+ * DOS 3 partition managers got around this problem by faking a
+ * larger sector size, ie treating multiple physical sectors as
+ * a single logical sector.
+ *
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+ /* don't divide by zero */
+
+ logical_sector_size =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->sector_size));
+ sector_mult = logical_sector_size >> SECTOR_BITS;
+ MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
+ MSDOS_SB(sb)->fats = b->fats;
+ MSDOS_SB(sb)->fat_start = cpu_to_le16(b->reserved)*sector_mult;
+ MSDOS_SB(sb)->fat_length = cpu_to_le16(b->fat_length)*sector_mult;
+ MSDOS_SB(sb)->dir_start = (cpu_to_le16(b->reserved)+b->fats*cpu_to_le16(
+ b->fat_length))*sector_mult;
+ MSDOS_SB(sb)->dir_entries =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->dir_entries));
+ MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
+ MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
+ sector_mult);
+ data_sectors = cpu_to_le16(get_unaligned((unsigned short *) &b->sectors));
+ if (!data_sectors) {
+ data_sectors = cpu_to_le32(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
+ error = !b->cluster_size || !sector_mult;
+ if (!error) {
+ MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
+ b->cluster_size/sector_mult : 0;
+ MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
+ MSDOS_FAT12 ? 16 : 12;
+ fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+ MSDOS_SB(sb)->fat_bits;
+ /* this doesn't compile. I don't understand it either...
+ error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
+ (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+ MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+ || !b->secs_track || !b->heads;
+ */
+ }
+ raw_brelse(sb, bh);
+
+ if(error)goto c_err;
+
+ /*
+ This must be done after the brelse because the bh is a dummy
+ allocated by fat_bread (see buffer.c)
+ */
+ sb->s_blocksize = blksize; /* Using this small block size solves */
+ /* the misfit with buffer cache and cluster */
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+
+ return list_cvfs(sb);
+
+ c_err:
+ fprintf(stderr,"Not a FAT12 or FAT16 MSDOS filesystem.\n");
+ return -1;
+}
+
+int main(int argc, char*argv[])
+{ struct super_block *sb;
+ int fd;
+ int ret;
+
+ if(argc!=2)
+ { fprintf(stderr,"list names of CVFs in a FAT12 or FAT16 MSDOS fs.\n");
+ fprintf(stderr,"usage: %s filename\n",argv[0]);
+ return -1;
+ }
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror("open");
+ return -1;
+ }
+
+ sb=malloc(sizeof(struct super_block));
+ if(sb==NULL)
+ { fprintf(stderr,"malloc failed\n");
+ close(fd);
+ return -1;
+ }
+
+ sb->s_flags=0;
+ sb->s_flags|=MS_RDONLY;
+ sb->s_dev=fd;
+ sb->directlist=NULL;
+ sb->directlen=NULL;
+
+ ret=read_super(sb);
+ close(fd);
+ free(sb);
+ return ret;
+}
+
diff --git a/src/cvftest.c b/src/cvftest.c
new file mode 100644
index 0000000..ede517c
--- /dev/null
+++ b/src/cvftest.c
@@ -0,0 +1,120 @@
+/*
+cvftest.c
+
+DMSDOS: doublespace/drivespace/stacker CVF identification tool.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<string.h>
+
+int main(int argc, char*argv[])
+{ unsigned char sb[512];
+ int i;
+ FILE*f;
+ char*fn;
+
+ if(argc<2||argc>3)
+ {
+ usage:
+ fprintf(stderr,"usage: %s filename [-v]\n",argv[0]);
+ fprintf(stderr,"detect CVFs according to header\n");
+ fprintf(stderr," -v: be verbose i.e. print result to stdout\n");
+ fprintf(stderr," \"-\" as filename means read data from stdin\n");
+ fprintf(stderr,"exit code: 0 = CVF detected, 1 = no CVF, >1 = some error occured\n");
+ exit(20);
+ }
+
+ fn=argv[1];
+
+ if(argc==3)
+ { if(strcmp(argv[1],"-v")==0)fn=argv[2];
+ else if(strcmp(argv[2],"-v")!=0)goto usage;
+ }
+
+ if(strcmp(fn,"-")==0)f=stdin;
+ else
+ { f=fopen(fn,"rb");
+ if(f==NULL)
+ { perror("open failed");
+ exit(4);
+ }
+ }
+
+ for(i=0;i<512;++i)sb[i]=fgetc(f);
+
+ if(fgetc(f)==EOF)
+ { if(ferror(f))
+ { perror("error reading file");
+ exit(3);
+ }
+ if(argc==3)goto nocvf;
+ return 1;
+ }
+
+ if(argc==2)
+ {
+ if(strncmp(sb+3,"MSDBL6.0",8)==0||strncmp(sb+3,"MSDSP6.0",8)==0
+ ||strncmp(sb,"STACKER",7)==0)return 0;
+
+ return 1;
+ }
+
+ if(strncmp(sb+3,"MSDBL6.0",8)==0||strncmp(sb+3,"MSDSP6.0",8)==0)
+ { if(sb[51]==2&&sb[13]==16)printf("drivespace CVF (version 2)\n");
+ else if((sb[51]==3||sb[51]==0)&&sb[13]==64)printf("drivespace 3 CVF\n");
+ else if(sb[51]<2&&sb[13]==16)printf("doublespace CVF (version 1)\n");
+ else printf("unknown (new? damaged?) doublespace or drivespace CVF\n");
+ return 0;
+ }
+ else if(strncmp(sb,"STACKER",7)==0)
+ { int i;
+ unsigned char b,c;
+ unsigned char * p;
+ int StacVersion;
+
+ /* decode super block */
+ for(i=0x30,p=sb+0x50,b=sb[0x4c];i--;p++)
+ { b=0xc4-b;
+ b=b<0x80?b*2:b*2+1;
+ b^=c=*p;
+ *p=b;b=c;
+ }
+ if(sb[0x4e]!=0xa||sb[0x4f]!=0x1a)
+ printf("unknown (new? damaged?) stacker CVF\n");
+ else
+ { StacVersion=sb[0x60];
+ StacVersion&=0xff;
+ StacVersion|=sb[0x61]<<8;
+ StacVersion&=0xffff;
+ if(StacVersion>=410)printf("stacker version 4 CVF\n");
+ else printf("stacker version 3 CVF\n");
+ }
+ return 0;
+ }
+
+ nocvf:
+ printf("not a known CVF\n");
+ return 1;
+}
diff --git a/src/daemon_actions.c b/src/daemon_actions.c
new file mode 100644
index 0000000..89e15e8
--- /dev/null
+++ b/src/daemon_actions.c
@@ -0,0 +1,301 @@
+/*
+daemon_actions.c
+
+DMSDOS CVF-FAT module: external dmsdos daemon.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include "dmsdos.h"
+#include<sys/ioctl.h>
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<fcntl.h>
+#include<string.h>
+#include<errno.h>
+#include<signal.h>
+#include<unistd.h>
+
+/* default compression level minus 1 for dmsdosd */
+int cfaktor=11;
+
+int debug=0;
+
+int exit_signaled=0;
+
+#define CONTINUE 0
+#define EXIT_AND_FINISH 1
+#define EXIT_IMMEDIATELY 2
+
+/* time to sleep between two idle calls (in seconds)
+ for slower systems you can use a higher value in order to reduce
+ unnecessary polling */
+#define SLEEPTIME 30
+
+#define PIDFILE "/var/run/dmsdosd.pid"
+
+typedef struct
+{ long val1;
+ long val2;
+ long val3;
+ unsigned char data[32*1024];
+} Cdata;
+
+Cdata cdata;
+Cdata ckdata;
+
+void signal_handler(int a)
+{ if(a==SIGINT)exit_signaled=EXIT_AND_FINISH;
+ if(a==SIGTERM)exit_signaled=EXIT_IMMEDIATELY;
+ /* reactivate again */
+ signal(SIGINT,signal_handler);
+ signal(SIGTERM,signal_handler);
+ signal(SIGUSR1,signal_handler); /* this should just wake up */
+}
+
+void pidfile(void)
+{ struct stat buf;
+ int pid=0;
+ char str[100];
+ FILE*f;
+
+ if(stat(PIDFILE,&buf)>=0)
+ { if(debug)fprintf(stderr,"pidfile exists");
+ f=fopen(PIDFILE,"r");
+ if(f)
+ { fscanf(f,"%d",&pid);
+ fclose(f);
+ sprintf(str,"/proc/%d/stat",pid);
+ if(stat(str,&buf)>=0)
+ { fprintf(stderr,"dmsdosd already running\n");
+ exit(1);
+ }
+ }
+ unlink(PIDFILE);
+ }
+
+ f=fopen(PIDFILE,"w");
+ if(f==NULL)
+ { fprintf(stderr,"cannot write pidfile %s\n",PIDFILE);
+ exit(1);
+ }
+ fprintf(f,"%d\n",getpid());
+ fclose(f);
+}
+
+#include <stdarg.h>
+int printk(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ char*p=buf;
+ int i;
+
+ va_start(ap, fmt);
+ i=vsnprintf(buf, 500, fmt, ap);
+ va_end(ap);
+
+ if(p[0]=='<'&&p[1]>='0'&&p[1]<='7'&&p[2]=='>')p+=3;
+ if(strncmp(p,"DMSDOS: ",8)==0)p+=8;
+
+ fprintf(stderr,"dmsdosd: %s",p);
+
+ return i;
+}
+
+int errm=0;
+
+int get_and_compress_one(int fd)
+{ int ret;
+ int handle;
+ int length;
+ int size;
+ int method;
+
+ /* get cluster to compress */
+ if(exit_signaled!=CONTINUE)return 2;
+ if(debug)fprintf(stderr,"dmsdosd: Trying to read...\n");
+ ret=ioctl(fd,DMSDOS_D_READ,&cdata);
+ if(ret!=1)
+ { if(debug)fprintf(stderr,"dmsdosd: nothing there - D_READ ioctl returned %d\n",ret);
+ return ret;
+ }
+
+ handle=cdata.val1;
+ length=cdata.val2;
+ size=(length-1)/512+1;
+ method=cdata.val3;
+ if(debug)fprintf(stderr,"dmsdosd: compressing...\n");
+
+ if(method==SD_3||method==SD_4)
+ {
+#ifdef DMSDOS_CONFIG_STAC
+ ret=stac_compress(cdata.data,length,ckdata.data,
+ sizeof(ckdata.data),method,cfaktor);
+#else
+ if(errm==0)
+ { errm=1;
+ fprintf(stderr,"dmsdosd: stacker compression requested, but not compiled in!\n");
+ }
+ ret=-1;
+#endif
+ }
+ else
+ ret=dbl_compress(ckdata.data,cdata.data,size,method,cfaktor)*512;
+
+ if(debug)fprintf(stderr,"dmsdosd: compress %X from %d returned %d\n",
+ method,length,ret);
+ if(ret<0)ret=0; /* compression failed */
+ ckdata.val1=handle;
+ ckdata.val2=ret;
+ if(debug)fprintf(stderr,"dmsdosd: writing...\n");
+ ioctl(fd,DMSDOS_D_WRITE,&ckdata);
+
+ return 1; /* one cluster compressed */
+}
+
+void do_dmsdosd_actions(int fd)
+{
+ /* register dmsdosd */
+ if(debug)fprintf(stderr,"dmsdosd: calling D_ASK...\n");
+ if(ioctl(fd,DMSDOS_D_ASK,getpid()))
+ { fprintf(stderr,"dmsdosd: can't get permissions (internal daemon running?)\n");
+ return;
+ }
+
+ signal(SIGINT,signal_handler);
+ signal(SIGTERM,signal_handler);
+ signal(SIGUSR1,signal_handler); /* this should just wake up */
+
+ do
+ { while(get_and_compress_one(fd)==1);
+ /* don't kill the system performance when nothing to compress */
+ if(exit_signaled==CONTINUE)
+ { if(debug)fprintf(stderr,"dmsdosd: sleeping...\n");
+ sleep(SLEEPTIME);
+ /* throw away long idle mdfat/dfat/bitfat sectors */
+ ioctl(fd,DMSDOS_FREE_IDLE_CACHE,NULL);
+ }
+ }
+ while(exit_signaled==CONTINUE);
+
+ if(debug)fprintf(stderr,"dmsdosd: calling D_EXIT...\n");
+ ioctl(fd,DMSDOS_D_EXIT,NULL);
+
+ if(exit_signaled==EXIT_AND_FINISH)
+ { exit_signaled=CONTINUE;
+ while(get_and_compress_one(fd)==1);
+ }
+
+}
+
+int scan(char*arg)
+{ int w;
+
+ if(strncmp(arg,"0x",2)==0)sscanf(arg+2,"%x",&w);
+ else sscanf(arg,"%d",&w);
+
+ return w;
+}
+
+int main(int argc, char*argv[])
+{ Dblsb dblsb;
+ int fd;
+ int ret;
+
+ if(argc<2||argc>4)
+ {
+ fprintf(stderr,"DMSDOS daemon (C) 1996-1998 Frank Gockel, Pavel Pisa\n");
+ fprintf(stderr,"compiled " __DATE__ " " __TIME__ " under dmsdos version %d.%d.%d%s\n\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL,DMSDOS_VLT);
+ fprintf(stderr,"Usage: %s (directory)\n",argv[0]);
+ fprintf(stderr," %s (directory) cf\n",argv[0]);
+ fprintf(stderr," %s (directory) cf debug\n",argv[0]);
+ return 1;
+ }
+
+ if(getuid())
+ { fprintf(stderr,"Sorry, you must be root to run me.\n");
+ exit(1);
+ }
+
+ if(argc==4)debug=1;
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror("open");
+ return 1;
+ }
+
+ if(argc==3)
+ { cfaktor=scan(argv[2])-1;
+ if(cfaktor<0||cfaktor>11)
+ { fprintf(stderr,"cf parameter out of range\n");
+ close(fd);
+ return 1;
+ }
+ }
+
+ /* this hack enables reverse version check */
+ /* it must not be changed in order to recognize incompatible older versions */
+ /* this also depends on s_dcluster being the first record in Dblsb */
+ dblsb.s_dcluster=DMSDOS_VERSION;
+
+ ret=ioctl(fd,DMSDOS_GET_DBLSB,&dblsb);
+ if(ret<0)
+ { printf("This is not a DMSDOS directory.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret!=DMSDOS_VERSION)printf("You are running DMSDOS driver version %d.%d.%d.\n",(ret&0xff0000)>>16,
+ (ret&0x00ff00)>>8,ret&0xff);
+ /*printf("debug: ret=0x%08x\n",ret);*/
+ if(ret!=DMSDOS_VERSION)printf("This program was compiled for DMSDOS version %d.%d.%d",
+ (DMSDOS_VERSION&0xff0000)>>16,(DMSDOS_VERSION&0x00ff00)>>8,DMSDOS_VERSION&0xff);
+ if(ret&0x0f000000)
+ { printf("\nSorry, this program is too old for the actual DMSDOS driver version.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret<0x00000902)
+ { printf("\nSorry, this program requires at least DMSDOS driver version 0.9.2.\n");
+ close(fd);
+ return 1;
+ }
+ if(ret!=DMSDOS_VERSION)printf(" but should still work.\n\n");
+
+
+ if(!debug)
+ { if(fork())exit(0);
+ }
+
+ pidfile();
+
+ do_dmsdosd_actions(fd);
+
+ close(fd);
+
+ unlink(PIDFILE);
+
+ return 0;
+}
diff --git a/src/dblspace_alloc.c b/src/dblspace_alloc.c
new file mode 100644
index 0000000..ab6c18a
--- /dev/null
+++ b/src/dblspace_alloc.c
@@ -0,0 +1,710 @@
+/*
+dblspace_alloc.c
+
+DMSDOS CVF-FAT module: memory and sector allocation functions
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/msdos_fs.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+extern unsigned long dmsdos_speedup;
+
+#define NEAR_AREA 512
+/* no change for doublespace, but fixes drivespace 3 problems (was 50) */
+#define BIG_HOLE (dblsb->s_sectperclust*3+2)
+
+/* experimental new memory allocation routines */
+#if defined(USE_XMALLOC) && !defined(__DMSDOS_LIB__)
+
+int told=0;
+
+#include<linux/mm.h>
+#include<linux/malloc.h>
+
+#ifndef MAX_KMALLOC_SIZE
+#define MAX_KMALLOC_SIZE 128*1024
+#endif
+
+void* xmalloc(unsigned long size)
+{ void* result;
+
+ if(size<=MAX_KMALLOC_SIZE)
+ { result=kmalloc(size,GFP_KERNEL);
+ if(result)
+ { /* the xfree routine recognizes kmalloc'd memory due to that it is
+ usually not page-aligned -- we double-check here to be sure */
+ if ( ( ((unsigned long)result) & (PAGE_SIZE-1) ) ==0 )
+ { /* uhhh.... the trick is broken...
+ tell the user and go safe using vmalloc */
+ kfree(result);
+ if(told++)printk(KERN_ERR "DMSDOS: page-aligned memory returned by kmalloc - please disable XMALLOC\n");
+ }
+ else return result;
+ }
+ }
+ result=vmalloc(size);
+ /* again check alignment to be 100% sure */
+ if ( ((unsigned long)result) & (PAGE_SIZE-1) )
+ panic("DMSDOS: vmalloc returned unaligned memory - please disable XMALLOC\n");
+ return result;
+}
+
+void xfree(void * data)
+{ unsigned long address=(unsigned long)data;
+
+ /* we rely on the fact that kmalloc'ed memory is unaligned but
+ vmalloc'ed memory is page-aligned */
+ /* we are causing SERIOUS kernel problems if the wrong routine is called -
+ therefore xmalloc tests kmalloc's return address above */
+ if(address&(PAGE_SIZE-1))kfree(data);
+ else vfree(data);
+}
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* we don't need locking in the library */
+void lock_mdfat_alloc(Dblsb*dblsb) {}
+void unlock_mdfat_alloc(Dblsb*dblsb) {}
+#endif
+
+#ifdef __KERNEL__
+void lock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ down(sem);
+}
+void unlock_mdfat_alloc(Dblsb*dblsb)
+{ struct semaphore*sem;
+
+ sem=dblsb->mdfat_alloc_semp;
+ up(sem);
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+void u_free_cluster_sectors(struct super_block*sb, int clusternr,
+ unsigned long* undo_list)
+{
+ Mdfat_entry mde,dummy,newmde;
+ int newval=0;
+ int i;
+ int sectors;
+ int sectornr;
+ int undo_pnt=0;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_ALLOC("DMSDOS: free_cluster_sectors: freeing cluster %d\n",clusternr);
+
+ /* read mdfat entry and clear */
+ newmde.sector_minus_1=0;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy);
+ sectors=mde.size_lo_minus_1+1;
+ sectornr=mde.sector_minus_1+1;
+ if(mde.flags&2)
+ { if(mde.unknown&2)
+ { /* free fragmented cluster */
+ struct buffer_head*bh;
+ int fragcount;
+ int fragpnt;
+ int sec;
+ int cnt;
+
+ bh=raw_bread(sb,sectornr);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: fragmentation list unreadable in cluster %d\n",
+ clusternr);
+ goto nfree;
+ }
+ fragcount=bh->b_data[0];
+ if(fragcount<=0||fragcount>dblsb->s_sectperclust||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: error in fragmentation list in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { if(sec!=sectornr||cnt!=sectors)
+ { printk(KERN_ERR "DMSDOS: free_cluster_sectors: first fragment wrong in cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto nfree;
+ }
+ }
+
+ for(i=0;i<cnt;++i)
+ { dbl_bitfat_value(sb,sec+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sec+i;
+ }
+ }
+ }
+ else
+ { /* free sectors in bitfat */
+ nfree:
+ for(i=0;i<sectors;++i)
+ { dbl_bitfat_value(sb,sectornr+i,&newval);
+ if(undo_list)undo_list[undo_pnt++]=sectornr+i;
+ }
+ }
+
+ dblsb->s_full=0;
+ }
+ else
+ { LOG_CLUST("DMSDOS: stale MDFAT entry for cluster %d, zeroing.\n",
+ clusternr);
+ }
+
+ if(undo_list)undo_list[undo_pnt]=0;
+}
+
+void free_cluster_sectors(struct super_block*sb, int clusternr)
+{ lock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+ u_free_cluster_sectors(sb,clusternr,NULL);
+ unlock_mdfat_alloc(MSDOS_SB(sb)->private_data);
+}
+#endif
+
+/* for statistics - just for interest */
+int nearfound=0;
+int bigfound=0;
+int exactfound=0;
+int anyfound=0;
+int notfound=0;
+int fragfound=0;
+
+/* this function must be called locked */
+int find_free_bitfat(struct super_block*sb, int sectornr, int size)
+{ int testsek;
+ int i;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_free_sectors<0||dblsb->s_free_sectors>0x2000000)
+ { printk(KERN_NOTICE "DMSDOS: find_free_bitfat: free sectors=%d, cannot believe this. Counting...\n",
+ dblsb->s_free_sectors);
+ check_free_sectors(sb);
+ printk(KERN_NOTICE "DMSDOS: counted free sectors=%d\n",dblsb->s_free_sectors);
+ }
+
+ /* we needn't try in that case... */
+ if(dblsb->s_free_sectors<size)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+if(dmsdos_speedup&SP_FAST_BITFAT_ALLOC)
+{ /* new strategy: find any fitting hole beginning with last result */
+
+ testsek=dblsb->s_lastnear;
+ if(testsek<dblsb->s_datastart||testsek>dblsb->s_dataend-size)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++nearfound;
+ dblsb->s_lastnear=testsek+size;
+ dblsb->s_full=0;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, continue */
+ goto tryany;
+
+} /* end of new strategy */
+else
+{ /* old strategy: do a search in near environment first */
+
+ if(sectornr==0)sectornr=dblsb->s_lastnear;
+
+ if(sectornr>=dblsb->s_datastart&&sectornr<=dblsb->s_dataend-size)
+ { /* search exactly fitting hole near sectornr */
+ testsek=sectornr;
+ while(testsek<sectornr+NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+ testsek=sectornr;
+ while(testsek>sectornr-NEAR_AREA)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { --testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek-i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++nearfound;
+ dblsb->s_lastnear=testsek-i+1;
+ return testsek-i+1;
+ }
+ testsek-=i;
+ }
+ }
+ /* not found, continue */
+} /* end of old strategy */
+
+ /* search for a big hole */
+ if(dblsb->s_lastbig==-1)goto nobighole;
+
+ testsek=dblsb->s_lastbig;
+ if(testsek<dblsb->s_datastart||testsek+size>dblsb->s_dataend)
+ testsek=dblsb->s_datastart;
+
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<BIG_HOLE&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==BIG_HOLE)
+ { dblsb->s_full=0;
+ ++bigfound;
+ dblsb->s_lastbig=testsek;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ if(dblsb->s_lastbig==0)
+ dblsb->s_lastbig=-1; /*there's no big hole any more*/
+ else
+ dblsb->s_lastbig=0; /* next time try from the beginning */
+
+nobighole:
+
+if((dmsdos_speedup&SP_NO_EXACT_SEARCH)==0)
+{
+ /* search for an exactly fitting hole */
+ /* hmmm... now the search code becomes awfully slow */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<=size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { dblsb->s_full=0;
+ ++exactfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+}
+
+ if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+
+tryany:
+ /* last trial: search for any hole >= size */
+ testsek=dblsb->s_datastart;
+ while(testsek<=dblsb->s_dataend-size)
+ { if(dbl_bitfat_value(sb,testsek,NULL))
+ { ++testsek;
+ continue;
+ }
+ i=1;
+ while(i<size&&dbl_bitfat_value(sb,testsek+i,NULL)==0)++i;
+ if(i==size)
+ { ++anyfound;
+ return testsek;
+ }
+ testsek+=i;
+ }
+
+ /* not found, means disk full or MDFAT too fragmented */
+ ++notfound;
+
+ if(dblsb->s_cvf_version==DRVSP3)
+ { if(dblsb->s_full==0)
+ { printk(KERN_CRIT "DMSDOS: CVF almost full or highly fragmented at MDFAT level.\n");
+ dblsb->s_full=1;
+ }
+ }
+ else /* this is for CVFs that cannot fragment cluster data */
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full or too fragmented at MDFAT level.\n");
+ dblsb->s_full=2;
+ }
+ return 0;
+}
+
+void log_found_statistics()
+{ printk(KERN_INFO "DMSDOS: free sector finding statistics:\n");
+ printk(KERN_INFO "nearfound=%d bigfound=%d exactfound=%d anyfound=%d fragfound=%d notfound=%d\n",
+ nearfound,bigfound,exactfound,anyfound,fragfound,notfound);
+}
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+int try_fragmented(struct super_block*sb,int anear,int nr,
+ unsigned char*fraglist)
+{
+ int i;
+ int sector=anear;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int again=1;
+ int frags;
+ int cnt;
+
+ /* if you never want dmsdos to write fragmented clusters as a last resort
+ then uncomment the next return statement */
+
+ /* return -ENOSPC; */
+
+ if(dblsb->s_free_sectors<nr)
+ { if(dblsb->s_full<2)printk(KERN_CRIT "DMSDOS: CVF full.\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+
+ printk(KERN_DEBUG "DMSDOS: trying to allocate fragmented space...\n");
+ LOG_ALLOC("DMSDOS: try_fragmented: start, anear=%d nr=%d\n",anear,nr);
+
+ if(anear==0)sector=dblsb->s_lastnear;
+
+ if(sector<dblsb->s_datastart||sector>dblsb->s_dataend)
+ { sector=dblsb->s_datastart;
+ again=0;
+ }
+
+ retry:
+ frags=0;
+ fraglist[0]=0;
+ fraglist[1]=0;
+ fraglist[2]=0;
+ fraglist[3]=0;
+ cnt=nr;
+
+ while(cnt>0&&sector<=dblsb->s_dataend)
+ { if(dbl_bitfat_value(sb,sector,NULL))
+ { ++sector;
+ continue;
+ }
+ /* free sector found */
+ i=1;
+ while(dbl_bitfat_value(sb,sector+i,NULL)==0&&i<cnt)++i;
+ /* i=number of free sectors :) */
+ ++frags;
+ fraglist[frags*4]=sector-1;
+ fraglist[frags*4+1]=(sector-1)>>8;
+ fraglist[frags*4+2]=(sector-1)>>16;
+ fraglist[frags*4+3]=(sector-1)>>24;
+ fraglist[frags*4+3]|=(i-1)<<2;
+ fraglist[0]=frags;
+ sector+=i+1;
+ cnt-=i;
+ }
+ if(cnt>0&&again!=0)
+ { sector=dblsb->s_datastart;
+ again=0;
+ goto retry;
+ }
+
+ /* now evaluate the result, check for strange things */
+ if(cnt>0)
+ { if(dblsb->s_full<2)
+ printk(KERN_CRIT "DMSDOS: CVF full (cannot even allocate fragmented space)\n");
+ dblsb->s_full=2;
+ return -ENOSPC;
+ }
+ if(cnt<0)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: cnt<0 ? This is a bug.\n");
+ return -EIO;
+ }
+ if(frags<2||frags>dblsb->s_sectperclust+1)
+ { printk(KERN_ERR "DMSDOS: try_fragmented: frags=%d ? Cannot happen.\n",frags);
+ return -EIO;
+ }
+
+ /* correct statistics */
+ ++fragfound;--notfound;
+ dblsb->s_lastnear=sector;
+ dblsb->s_full=1; /* uhh... 0 might be dangerous... */
+
+ /* fraglist must be written to disk in *any* case in order to
+ still represent a correct filesystem
+ this is handled by dbl_write_cluster to prevent too much overhead */
+
+ LOG_ALLOC("DMSDOS: try_fragmented: success, frags=%d\n",frags);
+ return 0;
+}
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+
+#ifdef DMSDOS_CONFIG_DBL
+/* replaces an existing cluster;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ if fraglist!=NULL fragmented clusters are allowed for drivespace 3
+ returns first sector nr
+ changes mde and fraglist
+*/
+
+#define MAX_UNDO_LIST 70
+
+int dbl_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde,
+ unsigned char*fraglist)
+{ Mdfat_entry old_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ unsigned long undo_list[MAX_UNDO_LIST];
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ mde->unknown=0; /* ensure fragmented bit is clear */
+ if(old_mde.flags&2)
+ {
+ /* test whether same length (and not fragmented in drivespace 3) */
+ if(old_size==new_size&&
+ (dblsb->s_cvf_version!=DRVSP3||(old_mde.unknown&2)==0)
+ )
+ { LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same length, ok\n");
+ sector=old_sector;
+ goto mdfat_update;
+ }
+ if(dblsb->s_cvf_version==DRVSP3&&(old_mde.unknown&2)!=0&&fraglist!=NULL)
+ { /*old cluster is fragmented and new *is allowed* to be fragmentd */
+ struct buffer_head*bh=raw_bread(sb,old_sector);
+ if(bh)
+ { int fragcount;
+ int cnt;
+ int sec;
+ int fragpnt;
+ int sects;
+ int m_cnt=0;
+ int m_sec=0;
+
+ fragcount=bh->b_data[0];
+ sects=0;
+ if(fragcount<2||fragcount>dblsb->s_sectperclust+1||
+ bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0
+ )
+ { raw_brelse(sb,bh);
+ goto check_failed;
+ }
+
+ for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { cnt=bh->b_data[fragpnt*4+3];
+ cnt&=0xff;
+ cnt/=4;
+ cnt+=1;
+ sec=bh->b_data[fragpnt*4];
+ sec&=0xff;
+ sec+=bh->b_data[fragpnt*4+1]<<8;
+ sec&=0xffff;
+ sec+=bh->b_data[fragpnt*4+2]<<16;
+ sec&=0xffffff;
+ sec+=1;
+
+ if(fragpnt==1)
+ { m_cnt=cnt;
+ m_sec=sec;
+ if(sec!=old_mde.sector_minus_1+1||cnt!=old_mde.size_lo_minus_1+1)
+ { printk(KERN_ERR "DMSDOS: dbl_replace_existing_cluster: checking old fraglist: first fragment wrong in cluster %d\n",
+ cluster);
+ raw_brelse(sb,bh);
+ goto check_failed;
+ }
+ }
+
+ sects+=cnt;
+ }
+ raw_brelse(sb,bh);
+ if(sects-1/*subtract space for fraglist*/==new_size)
+ { /* we can reuse it */
+ memcpy(fraglist,bh->b_data,4*(fragcount+1));
+ mde->unknown|=2;
+ mde->size_lo_minus_1=m_cnt-1;
+ sector=m_sec;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: same fragmented size, ok.\n");
+ goto mdfat_update;
+ }
+ check_failed: ; /*Win32 compiler wants a semicolon here */
+ /* fall through */
+ }
+ }
+ /* different length, replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing old sectors...\n");
+ u_free_cluster_sectors(sb,cluster,undo_list);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ {
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(dblsb->s_cvf_version==DRVSP3&&fraglist!=NULL
+ &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0)
+ { i=try_fragmented(sb,near_sector,new_size+1,fraglist);/*yes one sector more*/
+ if(i==0) /* success */
+ { /* scan fraglist */
+ int frags;
+ int seccount;
+ int usector;
+ int j;
+
+ frags=fraglist[0];
+ for(i=1;i<=frags;++i)
+ { seccount=fraglist[i*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ usector=fraglist[i*4];
+ usector&=0xff;
+ usector+=fraglist[i*4+1]<<8;
+ usector&=0xffff;
+ usector+=fraglist[i*4+2]<<16;
+ usector&=0xffffff;
+ usector+=1;
+
+ if(i==1) /* note values for mdfat */
+ { mde->size_lo_minus_1=seccount-1;
+ sector=usector;
+ }
+
+ /* allocate in bitfat */
+ newval=1;
+ for(j=0;j<seccount;++j)
+ { /* check whether sectors are really free */
+ if(dbl_bitfat_value(sb,usector+j,NULL))
+ { printk(KERN_EMERG "DMSDOS: try_fragmented returned non-free sectors!\n");
+ /* WARNING: bitfat is corrupt now */
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ dbl_bitfat_value(sb,usector+j,&newval);
+ }
+ }
+ mde->unknown|=2; /* set fragmented bit */
+ goto mdfat_update;
+ }
+ /* try_fragmented failed: fall through */
+ }
+#endif /* DMSDOS_CONFIG_DRVSP3 */
+ if(old_mde.flags&2)
+ { /* undo bitfat free */
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: undoing bitfat free...\n");
+ newval=1;
+ for(i=0;undo_list[i]!=0;++i)
+ dbl_bitfat_value(sb,undo_list[i],&newval);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: dbl_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+mdfat_update:
+ mde->sector_minus_1=sector-1;
+ mde->flags|=2;
+ LOG_ALLOC("DMSDOS: dbl_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+#endif
diff --git a/src/dblspace_buffer.c b/src/dblspace_buffer.c
new file mode 100644
index 0000000..9ce6288
--- /dev/null
+++ b/src/dblspace_buffer.c
@@ -0,0 +1,195 @@
+/*
+dblspace_buffer.c
+
+DMSDOS CVF-FAT module: low-level buffered read-write access functions
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifndef __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include "dmsdos.h"
+
+/* This is just cut'n'paste from the fat fs original :)
+ We don't do a virtual sector translation here */
+
+struct buffer_head *raw_bread (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+
+ /* Note that the blocksize is 512 or 1024, but the first read
+ is always of size 1024. Doing readahead may be counterproductive
+ or just plain wrong. */
+ if (sb->s_blocksize == 512) {
+ ret = bread (sb->s_dev,block,512);
+ } else {
+ struct buffer_head *real = bread (sb->s_dev,block>>1,1024);
+
+ if (real != NULL){
+ ret = (struct buffer_head *)
+ kmalloc (sizeof(struct buffer_head), GFP_KERNEL);
+ if (ret != NULL) {
+ /* #Specification: msdos / strategy / special device / dummy blocks
+ Many special device (Scsi optical disk for one) use
+ larger hardware sector size. This allows for higher
+ capacity.
+
+ Most of the time, the MsDOS file system that sit
+ on this device is totally unaligned. It use logically
+ 512 bytes sector size, with logical sector starting
+ in the middle of a hardware block. The bad news is
+ that a hardware sector may hold data own by two
+ different files. This means that the hardware sector
+ must be read, patch and written almost all the time.
+
+ Needless to say that it kills write performance
+ on all OS.
+
+ Internally the linux msdos fs is using 512 bytes
+ logical sector. When accessing such a device, we
+ allocate dummy buffer cache blocks, that we stuff
+ with the information of a real one (1k large).
+
+ This strategy is used to hide this difference to
+ the core of the msdos fs. The slowdown is not
+ hidden though!
+ */
+ /*
+ The memset is there only to catch errors. The msdos
+ fs is only using b_data
+ */
+ memset (ret,0,sizeof(*ret));
+ ret->b_data = real->b_data;
+ if (block & 1) ret->b_data += 512;
+ ret->b_next = real;
+ }else{
+ brelse (real);
+ }
+ }
+ }
+ return ret;
+}
+struct buffer_head *raw_getblk (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head *ret = NULL;
+ if (sb->s_blocksize == 512){
+ ret = getblk (sb->s_dev,block,512);
+ }else{
+ /* #Specification: msdos / special device / writing
+ A write is always preceded by a read of the complete block
+ (large hardware sector size). This defeat write performance.
+ There is a possibility to optimize this when writing large
+ chunk by making sure we are filling large block. Volunteer ?
+ */
+ ret = raw_bread (sb,block);
+ }
+ return ret;
+}
+
+void raw_brelse (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (bh != NULL){
+ if (sb->s_blocksize == 512){
+ brelse (bh);
+ }else{
+ brelse (bh->b_next);
+ /* We can free the dummy because a new one is allocated at
+ each fat_getblk() and fat_bread().
+ */
+ kfree (bh);
+ }
+ }
+}
+
+void raw_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val)
+{
+
+#ifdef DBL_WRITEACCESS
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_dirty (bh,dirty_val);
+#else
+printk(KERN_NOTICE "DMSDOS: write access not compiled in, ignored\n");
+#endif
+
+}
+
+void raw_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ mark_buffer_uptodate(bh, val);
+}
+int raw_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if (sb->s_blocksize != 512){
+ bh = bh->b_next;
+ }
+ return buffer_uptodate(bh);
+}
+
+/* we really need this for read-ahead */
+void raw_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ if (sb->s_blocksize == 512){
+ ll_rw_block(opr,nbreq,bh);
+ }else{
+ struct buffer_head *tmp[32];
+ int i;
+ for (i=0; i<nbreq; i++){
+ tmp[i] = bh[i]->b_next;
+ }
+ ll_rw_block(opr,nbreq,tmp);
+ }
+}
diff --git a/src/dblspace_chk.c b/src/dblspace_chk.c
new file mode 100644
index 0000000..fe28d75
--- /dev/null
+++ b/src/dblspace_chk.c
@@ -0,0 +1,559 @@
+/*
+dblspace_chk.c
+
+DMSDOS CVF-FAT module: bitfat check routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#endif
+
+#define MAXMSG 20
+
+extern unsigned long loglevel;
+extern unsigned long dmsdos_speedup;
+extern int daemon_present;
+
+#ifdef DMSDOS_CONFIG_STAC
+/* reads stacker BITFAT sumary informations */
+__u8 *stac_bitfat_sumary(struct super_block*sb,
+ struct buffer_head**pbh)
+{ int pos,sector;
+ struct buffer_head *bh;
+ int bitfat2;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ bitfat2=dblsb->s_cvf_version>STAC3;
+ pos=dblsb->s_dataend-dblsb->s_datastart; /* number of data sectors */
+ pos=(pos+(bitfat2?4:8))>>(bitfat2?2:3);
+ pos=(pos+0xF)&~0xF;
+ sector=pos/SECTOR_SIZE+dblsb->s_mdfatstart; /* here it's AMAP start !!! */
+ *pbh=bh=raw_bread(sb,sector);
+ if(bh==NULL) return(NULL);
+ return(bh->b_data+pos%SECTOR_SIZE);
+};
+
+/* sets bitfat state, 0=query only, 1=clean, 2=dirty, 3=bad, 11=force clean */
+int stac_bitfat_state(struct super_block*sb,int new_state)
+{ int old_state;
+ __u8* pp;
+ struct buffer_head *bh;
+ static __u8 bitfat_up_to_date_fl[4]={0xAA,0xBB,0xAA,0xAA};
+ static __u8 bitfat_changing_fl[4]={0xAA,0xBB,0x55,0x55};
+ static __u8 bitfat_bad_fl[4]={0xAA,0xBB,0x00,0x00};
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_cvf_version<STAC3) return 0;
+ if((pp=stac_bitfat_sumary(sb,&bh))==NULL)
+ { printk(KERN_ERR "DMSDOS: read BITFAT state error\n");
+ return -2;
+ };
+
+ if(!memcmp(pp,bitfat_up_to_date_fl,sizeof(bitfat_up_to_date_fl)))
+ old_state=1;
+ else if(!memcmp(pp,bitfat_changing_fl,sizeof(bitfat_changing_fl)))
+ old_state=2;
+ else old_state=3;
+
+ if(new_state&&(dblsb->s_comp!=READ_ONLY)
+ &&((old_state!=3)||(new_state&0xF0)))
+ {
+ if((new_state&0xF)==1)
+ memcpy(pp,bitfat_up_to_date_fl,sizeof(bitfat_up_to_date_fl));
+ else if((new_state&0xF)==2)
+ memcpy(pp,bitfat_changing_fl,sizeof(bitfat_changing_fl));
+ else memcpy(pp,bitfat_bad_fl,sizeof(bitfat_bad_fl));
+ raw_mark_buffer_dirty(sb,bh,1);
+ };
+
+ raw_brelse(sb,bh);
+ return old_state;
+};
+
+/* prepared for repair of BITFAT */
+int stac_simple_check(struct super_block*sb, int repair)
+{
+ unsigned char *sect_array;
+ int clust,i,j,val,sect;
+ int non_lin_alloc;
+ Stac_cwalk cw;
+ struct buffer_head*bh;
+ __u8* pp;
+ int free_sects;
+ int deleted_clusts;
+ int bitfat_dirty;
+ static __u8 inc_tab[4]={1,4,16,64};
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+#define set_bits(field,nr,val) field[nr>>2]|=val<<((nr&3)<<1)
+#define get_bits(field,nr) ((field[nr>>2]>>((nr&3)<<1))&3)
+#define inc_bits(field,nr) field[nr>>2]+=inc_tab[nr&3]
+
+ bitfat_dirty=0;
+ if(dblsb->s_comp==READ_ONLY) repair=0;
+
+ /* check bitfat mount id */
+
+ {
+
+ if((pp=stac_bitfat_sumary(sb,&bh))==NULL)
+ { printk(KERN_ERR "DMSDOS: simple_check: read BITFAT sumary error\n");
+ return -2;
+ };
+
+ if((i=stac_bitfat_state(sb,0))!=1)
+ { if(i>2)
+ { printk(KERN_WARNING "DMSDOS: simple_check: BITFAT abnormal state: ");
+ for(i=0;i<16;i++) printk(" %02X",(int)pp[i]);
+ printk("\n");
+ } else printk(KERN_NOTICE "DMSDOS: simple_check: BITFAT mounted/dirty\n");
+ if(repair)
+ { printk(KERN_INFO "DMSDOS: Updating BITFAT\n");
+ stac_bitfat_state(sb,0x12);
+ bitfat_dirty=1;
+ };
+ };
+
+ printk(KERN_INFO "DMSDOS: Sumary: info1 = %d\n",(int)CHL((pp+4)));
+ printk(KERN_INFO "DMSDOS: Sumary: info2 = %d\n",(int)(CHL((pp+8)))-(0xF<<28));
+ raw_brelse(sb,bh);
+ };
+
+ /* check mdfat */
+
+ val=dblsb->s_dataend/4 + 1;
+ sect_array=(unsigned char*)vmalloc(val);
+ if(sect_array==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)\n");
+ return 2;
+ }
+ for(i=0;i<val;++i)sect_array[i]=0;
+
+ deleted_clusts=0;non_lin_alloc=0;
+ for(clust=2;clust<=dblsb->s_max_cluster2;++clust)
+ { i=stac_cwalk_init(&cw,sb,clust,0);
+ if(i>0)
+ { if (cw.flags&0x40) deleted_clusts++;
+ while((sect=stac_cwalk_sector(&cw))>0)
+ {
+ if(sect>dblsb->s_dataend||sect<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid (cluster %d, sect %d)\n",
+ clust,sect);
+ mde_sect_error:
+ stac_cwalk_done(&cw);
+ vfree(sect_array);
+ return -2;
+ }
+ val=get_bits(sect_array,sect);
+ if(val)
+ { if(dblsb->s_cvf_version==STAC3||
+ (((cw.flags&0xA0)!=0xA0)&&(cw.flen||cw.fcnt)))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink detected (cluster %d)\n",
+ clust);
+ goto mde_sect_error;
+ };
+ if(((cw.flags&0xA0)!=0xA0)&&!non_lin_alloc)
+ { non_lin_alloc++;
+ printk(KERN_NOTICE "DMSDOS: Interesting MDFAT non-lin subalocation (cluster %d)\n",
+ clust);
+ };
+ };
+ inc_bits(sect_array,sect);
+ };
+ stac_cwalk_done(&cw);
+ }
+ else if (i<0)
+ { printk(KERN_ERR "DMSDOS: MDFAT bad allocation (cluster %d)\n",
+ clust);
+ vfree(sect_array);
+ return -2;
+ };
+ };
+
+ /* check bitfat */
+
+ j=0; /* count BITFAT mismatches */
+ free_sects=0;
+
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { if((val=get_bits(sect_array,i))==0) free_sects++;
+ if(dbl_bitfat_value(sb,i,NULL)!=val)
+ { if(repair)
+ { if(!j)
+ { /* repair of first mitchmatch in BITFAT */
+ printk(KERN_INFO "DMSDOS: Updating BITFAT.\n");
+ if(stac_bitfat_state(sb,0x12)<=0)
+ { printk(KERN_ERR "DMSDOS: simple_check: BITFAT state error\n");
+ vfree(sect_array);
+ return -3;
+ };
+ /* mark bitfat as dirty */
+ bitfat_dirty=1;
+ };
+ dbl_bitfat_value(sb,i,&val);
+ j++;
+ }
+ else
+ { printk(KERN_ERR "DMSDOS: BITFAT mismatches MDFAT (sector %d is %d and should be %d)\n",
+ i,dbl_bitfat_value(sb,i,NULL),(unsigned)get_bits(sect_array,i));
+ j++;
+ if(j==MAXMSG)
+ { vfree(sect_array);
+ printk(KERN_ERR "DMSDOS: Too many BITFAT mismatches in CVF, check aborted.\n");
+ return -3;
+ }
+ }
+ }
+ }
+ if(bitfat_dirty)
+ { printk(KERN_INFO "DMSDOS: Updating BITFAT finished\n");
+ stac_bitfat_state(sb,2);
+ };
+
+ if((dblsb->s_free_sectors!=-1)&&
+ (dblsb->s_free_sectors!=free_sects))
+ printk(KERN_INFO "DMSDOS: adapting free sectors count\n");
+
+ dblsb->s_free_sectors=free_sects;
+
+ printk(KERN_INFO "DMSDOS: Sumary: Free sectors = %d\n",free_sects);
+ printk(KERN_INFO "DMSDOS: Sumary: Deleted clusters = %d\n",deleted_clusts);
+
+ vfree(sect_array);
+ if(j!=0&&repair==0)return -3;
+ return 0;
+}
+#endif
+
+/* simple fs check routines (DON'T mount obviously damaged filesystems rw)
+*/
+int simple_check(struct super_block*sb,int repair)
+{ /* unsigned char field[512*1024]; grr... panics (stack overflow) */
+ unsigned char *field;
+ unsigned char bits[8]={1,2,4,8,16,32,64,128};
+ int i,val;
+ Mdfat_entry mde,dummy;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int mdfat_dead_msg_count=0;
+ int maxmsg=0;
+ int errorcode=0;
+#ifdef DMSDOS_CONFIG_DBL
+ int free_sects;
+ int j;
+#endif
+
+ /* can't repair ro filesystems */
+ if(dblsb->s_comp==READ_ONLY)repair=0;
+
+#define setbit(nr) field[nr/8]|=bits[nr%8]
+#define getbit(nr) (field[nr/8]&bits[nr%8])
+
+ /* check fat */
+
+ /* get memory for field */
+ val=dblsb->s_max_cluster2/8 + 1;
+ field=(unsigned char*)vmalloc(val);
+ if(field==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check aborted (no memory)\n");
+ return 1;
+ }
+ for(i=0;i<val;++i)field[i]=0;
+
+ for(i=2;i<=dblsb->s_max_cluster2&&maxmsg<=MAXMSG;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ dbl_mdfat_value(sb,i,NULL,&mde);
+ if(val!=0&&val!=-1)
+ {
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: FAT crosslink or loop in CVF detected (cluster %d), giving up.\n",
+ i);
+ ++maxmsg;
+ repair=0; /* unable to fix this - refuse to do further repair */
+ errorcode=-1;
+ break; /* we cannot continue here */
+ }
+ setbit(val);
+ }
+ if(val==0&&(mde.flags&2)!=0)
+ { if(repair==0)
+ { if(dblsb->s_cvf_version<STAC3)
+ { printk(KERN_ERR "DMSDOS: MDFAT-level dead sectors found in CVF (cluster %d)\n",
+ i);
+ ++maxmsg;
+ errorcode=-2;
+ }
+ }
+ else
+ { if(mdfat_dead_msg_count++==0) /* print message only once */
+ { if(dblsb->s_cvf_version<STAC3)
+ printk(KERN_NOTICE "DMSDOS: MDFAT-level dead sectors found, removing...\n");
+ else
+ printk(KERN_INFO "DMSDOS: Deleted clusters found, removing...\n");
+ }
+ mde.flags=0;
+ mde.size_lo_minus_1=0;
+ mde.size_hi_minus_1=0;
+ mde.sector_minus_1=(dblsb->s_cvf_version<STAC3)?0:-1;
+ dbl_mdfat_value(sb,i,&mde,&dummy);
+ }
+ }
+ }
+
+ vfree(field);
+
+ if(maxmsg>MAXMSG)
+ { printk(KERN_ERR "DMSDOS: giving up after %d errors. There may be more errors.\n",
+ maxmsg);
+ }
+ if(errorcode)
+ { printk(KERN_ERR "DMSDOS: part 1 of filesystem check failed, aborting.\n");
+ return errorcode;
+ }
+
+#ifdef DMSDOS_CONFIG_STAC
+ if(dblsb->s_cvf_version>=STAC3)
+ return stac_simple_check(sb,repair);
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+ /* check mdfat */
+
+ val=dblsb->s_dataend/8 + 1;
+ field=(unsigned char*)vmalloc(val);
+ if(field==NULL)
+ { printk(KERN_WARNING "DMSDOS: simple_check: MDFAT+BITFAT test skipped (no memory)\n");
+ return 2;
+ }
+ for(i=0;i<val;++i)field[i]=0;
+
+ for(i=2;i<=dblsb->s_max_cluster2&&maxmsg<=MAXMSG;++i)
+ { dbl_mdfat_value(sb,i,NULL,&mde);
+ if(mde.flags&2) /* 'used' bit set */
+ {
+ val=mde.sector_minus_1;
+ if(val+mde.size_lo_minus_1>=dblsb->s_dataend||
+ val+1<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid in CVF (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0; /* refuse to repair */
+ errorcode=-2;
+ }
+ else
+ for(j=0;j<=mde.size_lo_minus_1;++j)
+ { ++val;
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink in CVF detected (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ setbit(val);
+ }
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ /* check fragmented clusters */
+ if(mde.unknown&2)
+ { /* cluster is fragmented */
+ int fragcount;
+ int fragpnt;
+ int sector_minus_1;
+ int seccount_minus_1;
+ struct buffer_head*bh;
+
+ bh=raw_bread(sb,mde.sector_minus_1+1);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read fragmentation list of cluster %d.\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ else
+ {
+ fragcount=bh->b_data[0];
+ if(bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0
+ ||fragcount<=0||fragcount>dblsb->s_sectperclust)
+ { printk(KERN_ERR "DMSDOS: illegal fragcount in cluster %d\n",i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ raw_brelse(sb,bh);
+ }
+ else /* read list and scan all fragments */
+ { for(fragpnt=1;fragpnt<=fragcount;++fragpnt)
+ { sector_minus_1=bh->b_data[fragpnt*4+0];
+ sector_minus_1&=0xff;
+ sector_minus_1+=bh->b_data[fragpnt*4+1]<<8;
+ sector_minus_1&=0xffff;
+ sector_minus_1+=bh->b_data[fragpnt*4+2]<<16;
+ sector_minus_1&=0xffffff;
+ seccount_minus_1=bh->b_data[fragpnt*4+3];
+ seccount_minus_1&=0xff;
+ seccount_minus_1/=4;
+
+ /* test range */
+ val=sector_minus_1;
+ if(val+seccount_minus_1>=dblsb->s_dataend||
+ val+1<dblsb->s_datastart)
+ { printk(KERN_ERR "DMSDOS: MDFAT entry invalid in CVF (fragmented cluster %d fragpnt %d)\n",
+ i,fragpnt);
+ ++maxmsg;
+ repair=0; /* refuse to repair */
+ errorcode=-2;
+ break;
+ }
+ if(fragpnt==1)
+ { /* first is the sector itself */
+ if(sector_minus_1!=mde.sector_minus_1
+ ||seccount_minus_1!=mde.size_lo_minus_1)
+ { printk(KERN_ERR "DMSDOS: fraglist!=mde cluster %d sector %d!=%ld or count %d!=%d\n",
+ i,sector_minus_1+1,mde.sector_minus_1+1,
+ seccount_minus_1+1,mde.size_lo_minus_1);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ }
+ else for(j=0;j<=seccount_minus_1;++j)
+ { ++val;
+ if(getbit(val))
+ { printk(KERN_ERR "DMSDOS: MDFAT crosslink in CVF detected (cluster %d)\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ setbit(val);
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ }
+ } /* end check fragmented cluster */
+#endif
+
+ }
+/* Hmmm... this doesn't seem to be an error... dos tolerates this...
+ else / 'used' bit NOT set /
+ { if(mde.sector_minus_1!=0||mde.size_lo_minus_1!=0||mde.size_hi_minus_1!=0)
+ { printk(KERN_NOTICE "DMSDOS: non-zero unused(?) MDFAT entry in cluster %d\n",
+ i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+ }
+*/
+/* Hmmm... unknown bits seem to contain nothing useful but are sometimes set...
+ if(mde.unknown) / treat unknown cases as error unless we know it better /
+ { printk(KERN_NOTICE "DMSDOS: unknown bits set to %d in cluster %d\n",
+ mde.unknown,i);
+ ++maxmsg;
+ repair=0;
+ errorcode=-2;
+ }
+*/
+ }
+
+ if(maxmsg>MAXMSG)
+ { printk(KERN_ERR "DMSDOS: giving up after %d errors. There may be more errors.\n",
+ maxmsg);
+ }
+ if(errorcode)
+ { vfree(field);
+ printk(KERN_ERR "DMSDOS: part 2 of filesystem check failed, aborting.\n");
+ return errorcode;
+ }
+
+ /* check bitfat */
+
+ /* dataend-1 problem corrected above - dmsdos doesn't touch the
+ last sector now because it seems to be reserved... */
+
+ j=0; /* count BITFAT mismatches */
+ free_sects=0; /* count free sectors */
+
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { val=dbl_bitfat_value(sb,i,NULL);
+ if(val==0)++free_sects;
+ if(val!=(getbit(i)?1:0))
+ { if(repair)
+ { int newval;
+
+ if(j==0)printk(KERN_NOTICE "DMSDOS: BITFAT mismatches MDFAT, repairing...\n");
+ newval=(getbit(i)?1:0);
+ dbl_bitfat_value(sb,i,&newval);
+ ++j;
+ }
+ else
+ {
+ printk(KERN_ERR "DMSDOS: BITFAT mismatches MDFAT (sector %d)\n",
+ i);
+ ++j;
+ if(j==MAXMSG)
+ { vfree(field);
+ printk(KERN_ERR "DMSDOS: Too many BITFAT mismatches, check aborted.\n");
+ return -3;
+ }
+ }
+
+ }
+ }
+
+ vfree(field);
+ if(j!=0&&repair==0)return -3;
+ dblsb->s_free_sectors=free_sects;
+ printk(KERN_INFO "DMSDOS: free sectors=%d\n",dblsb->s_free_sectors);
+#endif
+
+ return 0;
+}
+
diff --git a/src/dblspace_compr.c b/src/dblspace_compr.c
new file mode 100644
index 0000000..e403d58
--- /dev/null
+++ b/src/dblspace_compr.c
@@ -0,0 +1,711 @@
+/*
+dblspace_compr.c
+
+DMSDOS CVF-FAT module: [dbl|drv]space cluster write and compression routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/malloc.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <asm/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+extern unsigned long dmsdos_speedup;
+#endif
+
+#ifdef __DMSDOS_DAEMON__
+extern int cfaktor;
+void panic(char*);
+#include <malloc.h>
+#include <asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+#define SECTOR_SIZE 512
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+int c_maxtrycount[12]={ 1, 2, 3, 4, 6, 8,10,14,18,22,28,40};
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers do not like inline */
+#define INLINE static
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define cpu_to_le16(v) (v)
+#endif
+
+
+/* we always need at least DS compression */
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already written bits to buf */
+ __u16 *pd; /* data write pointer */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+/* initializes writting to bitstream */
+INLINE void dblb_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+}
+
+/* writes n<=16 bits to bitstream *pbits */
+INLINE void dblb_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->buf|=cod<<pbits->pb;
+ if((pbits->pb+=n)>=16)
+ {
+ if(pbits->pd<pbits->pe)
+ *(pbits->pd++)=cpu_to_le16((__u16)pbits->buf);
+ else if(pbits->pd==pbits->pe) pbits->pd++; /* output overflow */
+ pbits->buf>>=16;
+ pbits->pb-=16;
+ }
+}
+
+void write_byte(bits_t *pbits,int byte,int method)
+{ if(method!=JM_0_0&&method!=JM_0_1)
+ { if(byte<128)dblb_wrn(pbits,2,2);
+ else dblb_wrn(pbits,1,2);
+ }
+ else /* JM_0_0 */
+ { if(byte<128)dblb_wrn(pbits,0,1);
+ else dblb_wrn(pbits,3,2);
+ }
+ dblb_wrn(pbits,byte&0x7F,7);
+}
+
+void write_temp(bits_t *pbits,
+ unsigned char*tempstr,int len,int diff,int method)
+{ if(len==1)
+ { write_byte(pbits,tempstr[0],method);
+ return;
+ }
+ if(len==2&&(method==JM_0_0||method==JM_0_1))
+ { write_byte(pbits,tempstr[0],method);
+ write_byte(pbits,tempstr[1],method);
+ return;
+ }
+ if(method!=JM_0_0&&method!=JM_0_1)
+ { ++len; /* corrects different counting scheme */
+ if(diff<64)dblb_wrn(pbits,0,2);
+ else dblb_wrn(pbits,3,2);
+ }
+ else /* JM_0_0 */
+ { dblb_wrn(pbits,1,2);
+ dblb_wrn(pbits,(diff<64)?0:1,1);
+ }
+ if(diff<64)dblb_wrn(pbits,diff,6);
+ else
+ { if(diff<320)
+ { dblb_wrn(pbits,0,1);
+ dblb_wrn(pbits,diff-64,8);
+ }
+ else
+ { dblb_wrn(pbits,1,1);
+ dblb_wrn(pbits,diff-320,12);
+ }
+ }
+ /* okay, now encode len */
+ if(len==3)
+ { dblb_wrn(pbits,1,1);
+ return;
+ }
+ if(len<6)
+ { dblb_wrn(pbits,1<<1,2);
+ dblb_wrn(pbits,len-4,1);
+ return;
+ }
+ if(len<10)
+ { dblb_wrn(pbits,1<<2,3);
+ dblb_wrn(pbits,len-6,2);
+ return;
+ }
+ if(len<18)
+ { dblb_wrn(pbits,1<<3,4);
+ dblb_wrn(pbits,len-10,3);
+ return;
+ }
+ if(len<34)
+ { dblb_wrn(pbits,1<<4,5);
+ dblb_wrn(pbits,len-18,4);
+ return;
+ }
+ if(len<66)
+ { dblb_wrn(pbits,1<<5,6);
+ dblb_wrn(pbits,len-34,5);
+ return;
+ }
+ if(len<130)
+ { dblb_wrn(pbits,1<<6,7);
+ dblb_wrn(pbits,len-66,6);
+ return;
+ }
+ if(len<258)
+ { dblb_wrn(pbits,1<<7,8);
+ dblb_wrn(pbits,len-130,7);
+ return;
+ }
+ dblb_wrn(pbits,1<<8,9);
+ dblb_wrn(pbits,len-258,8);
+}
+
+void write_marker(bits_t *pbits,int method)
+{ if(method==JM_0_0||method==JM_0_1)dblb_wrn(pbits,13,4);
+ else /* DS_0_x */ dblb_wrn(pbits,7,3);
+ dblb_wrn(pbits,0xFFF,12);
+}
+
+typedef __u16 hash_t;
+
+/* hashing function is only 2 char because of DS min rep */
+INLINE unsigned dbl_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<4)^((__u16)p[1]<<0))&0x3FF;
+};
+
+/* adds new hash and returns previous occurence */
+INLINE hash_t dbl_newhash(__u8*clusterd ,int pos,
+ hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+dbl_hash(&(clusterd[pos]));
+ hash_cur=*hash_ptr; /* find previous occurence of hash */
+ *hash_ptr=pos; /* store new occurence */
+ *(hash_hist+(pos&hash_mask))=hash_cur;
+ /* store previous in history table */
+ return(hash_cur);
+};
+
+/* compresses a doublespace/drivespace cluster
+ gets uncompressed size (number of used sectors)
+ returns compressed size (in number of used sectors) or -1 if failed
+*/
+int dbl_compress(__u8* clusterk, __u8* clusterd, int size,
+ int method,int cf)
+{
+ bits_t bits;
+ int i;
+ int pos;
+ int mark_pos; /* position of next marker */
+ hash_t *hash_tab;
+ /* [0x400] pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* [0x800] previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ int hash_cur;
+ int max_hash=0; /* GCC likes this */
+ int match;
+ int max_match;
+ int try_cn;
+ int try_count=c_maxtrycount[cf]; /* tunable parameter */
+
+ switch(method)
+ { case DS_0_0:
+ case DS_0_1:
+ case DS_0_2: /* handled together with JM_0_0 because similar */
+ case JM_0_0:
+ case JM_0_1:
+
+ /* maximal compression */
+ if(cf>8)hash_mask=0xFFF;
+
+ /* Input size in bytes */
+ size=size*SECTOR_SIZE;
+
+ /* initialize bitstream */
+ dblb_wri(&bits,clusterk,size-SECTOR_SIZE);
+
+ /* put magic number */
+ dblb_wrn(&bits,method,32);
+
+ hash_tab=MALLOC(0x400*sizeof(hash_t));
+ if(hash_tab==NULL) return -1;
+ hash_hist=MALLOC((hash_mask+1)*sizeof(hash_t));
+ if(hash_hist==NULL) {FREE(hash_tab);return -1;}
+
+ for(i=0;i<0x400;i++) hash_tab[i]=0xFFFF;
+ for(i=0;i<=hash_mask;i++) hash_hist[i]=0xFFFF;
+
+ pos=mark_pos=0;
+ while(pos<size)
+ { mark_pos+=SECTOR_SIZE;
+ while(pos<mark_pos)
+ {
+ if(bits.pd>bits.pe) goto error; /* incompressible data */
+
+ if(pos+1>=size) goto single_char; /* cannot be hashed */
+
+ hash_cur=dbl_newhash(clusterd,pos,hash_tab,hash_hist,hash_mask);
+ if(hash_cur>=pos) goto single_char;
+
+ try_cn=try_count;
+ max_match=1; /* minimal match - 1 */
+ do{ /* longer offsets are not allowed */
+ if(pos-hash_cur>=0x113F) break;
+ /* speedup heuristic : new tested hash occurence must be
+ at least one char longer */
+ if((clusterd[hash_cur+max_match]==clusterd[pos+max_match])&&
+ (clusterd[hash_cur+max_match-1]==clusterd[pos+max_match-1])&&
+ (clusterd[hash_cur]==clusterd[pos]))
+ /* second chars are equal from hash function */
+ {
+ for(match=0;match<mark_pos-pos;match++)
+ if(clusterd[hash_cur+match]!=clusterd[pos+match])break;
+ if(match>max_match) /* find maximal hash */
+ { max_hash=hash_cur;max_match=match;
+ if(match==mark_pos-pos) break;
+ };
+ };
+ i=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[i&hash_mask])<i));
+ if(max_match<2) goto single_char;
+
+ write_temp(&bits,&(clusterd[pos]),max_match,pos-max_hash,method);
+ pos++;max_match--;
+ i=max_match;if(pos+i+1>=size)i=size-pos-1;
+ max_match-=i; /* last char cannot be hashed */
+ while(i--)
+ dbl_newhash(clusterd,pos++,hash_tab,hash_hist,hash_mask);
+ pos+=max_match;
+ continue;
+
+ single_char:
+ write_byte(&bits,clusterd[pos++],method);
+
+ }
+ write_marker(&bits,method);
+ }
+
+ dblb_wrn(&bits,0,15); /* flush last bits from bits.buf */
+ if(bits.pd>bits.pe) goto error;
+ FREE(hash_tab);
+ FREE(hash_hist);
+ return (((__u8*)bits.pd-(__u8*)clusterk)-1)/512+1;
+
+ error:
+ FREE(hash_tab);
+ FREE(hash_hist);
+ return -1;
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case SQ_0_0:
+ size=size*SECTOR_SIZE;
+ /* sq_comp(void* pin,int lin, void* pout, int lout, int flg) */
+ i=sq_comp(clusterd,size,clusterk,size,cf);
+ if((i<=0)||(i+SECTOR_SIZE>size)) return -1;
+ return ((i-1)/SECTOR_SIZE+1);
+#endif
+
+ default:
+ /* sorry, other compression methods currently not available */
+ return -1;
+ }
+
+ return -1;
+}
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+#ifdef DMSDOS_CONFIG_DRVSP3
+int write_fragmented(struct super_block*sb,unsigned char*fraglist,
+ unsigned char*clusterk,Mdfat_entry*mde,int ksize)
+{ int i,j;
+ int frags;
+ int seccount;
+ int sector;
+ int bytecount=ksize*SECTOR_SIZE;
+ int koffset=SECTOR_SIZE;
+ struct buffer_head*bh;
+ int c;
+
+ frags=fraglist[0];
+ if((mde->flags&1)==0)koffset=4*(frags+1);
+
+ LOG_CLUST("DMSDOS: writing fragmented cluster, frags=%d\n",frags);
+
+ /* now we have all information and can write the cluster data */
+ for(i=1;i<=frags;++i)
+ { seccount=fraglist[i*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ sector=fraglist[i*4];
+ sector&=0xff;
+ sector+=fraglist[i*4+1]<<8;
+ sector&=0xffff;
+ sector+=fraglist[i*4+2]<<16;
+ sector&=0xffffff;
+ sector+=1;
+
+ for(j=0;j<seccount;++j)
+ { bh=raw_getblk(sb,sector+j);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: write_fragmented: raw_getblk sector %d failed\n",
+ sector+j);
+ return -EIO;
+ }
+ if(i==1&&j==0)
+ { /* need to copy fraglist first */
+ memcpy(bh->b_data,fraglist,4*(frags+1));
+ if(koffset<SECTOR_SIZE)
+ { c=SECTOR_SIZE-koffset;
+ memcpy(bh->b_data,clusterk,c);
+ bytecount-=c;
+ clusterk+=c;
+ }
+ }
+ else
+ { c=SECTOR_SIZE;
+ if(c>bytecount)c=bytecount;
+ memcpy(bh->b_data,clusterk,c);
+ bytecount-=c;
+ clusterk+=c;
+ }
+
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+
+ return 0;
+}
+#endif
+
+/* write a dmsdos cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==UC_UNCOMPR uncompressed write is forced.
+ if ucflag<0 raw write is forced with compressed size -ucflag (in bytes).
+ if ucflag==UC_TEST simulate write is done (checks for space or reserves
+ space for the cluster in the filesystem but does not actually write it).
+ if ucflag==UC_DIRECT write compressed but don't use daemon - this is to
+ guarantee that the data are on the disk when the function exits.
+
+ *********** This function is doublespace/drivespace specific ******
+*/
+
+#ifdef DMSDOS_CONFIG_DBL
+int dbl_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{ int method;
+ unsigned char* clusterk;
+ int size;
+ Mdfat_entry mde;
+ int sector;
+ int i;
+ int res;
+ int ksize;
+ struct buffer_head*bh;
+ unsigned char fraglist[66*4];
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS dbl_write_cluster clusternr=%d length=%d near_sector=%d\n",
+ clusternr,length,near_sector);
+
+ /* are we deleting a cluster ? */
+ if(clusterd==NULL||length==0)
+ { free_cluster_sectors(sb,clusternr);
+ return 0;
+ }
+
+ if(ucflag==UC_TEST)
+ { if( dblsb->s_full==0 &&
+ /* well, this is estimated */
+ dblsb->s_sectperclust*CCACHESIZE+100<dblsb->s_free_sectors
+ ) return 0;
+ else return -ENOSPC;
+ }
+
+ /* guess compression method if not already known */
+ if(dblsb->s_comp==GUESS)
+ {
+ printk(KERN_INFO "DMSDOS: write_cluster: guessing compression method...\n");
+ { if(dblsb->s_cvf_version==DRVSP)
+ { dblsb->s_comp=JM_0_0;
+ /* no doubt here, there's only this one possible - so exit */
+ /* goto guess_ok; Hmm ... really...???? better let it scan */
+ }
+ if(dblsb->s_cvf_version==DRVSP3)
+ { dblsb->s_comp=SQ_0_0;
+ /* that's only a default in case the scan routine finds nothing */
+ }
+ /* DBLSP: we know nothing, it can be DS_0_2 for dos 6.0 and DS_0_0
+ for win95, let's scan it */
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { dbl_mdfat_value(sb,i,NULL,&mde);
+ /*if((mdfat&0xC0000000)==0x80000000)*/
+ if(mde.flags==2)
+ { bh=raw_bread(sb,mde.sector_minus_1+1);
+ if(bh!=NULL)
+ {
+ res=CHL(bh->b_data);
+ raw_brelse(sb,bh);
+ if(res==DS_0_0){dblsb->s_comp=DS_0_0;goto guess_ok;}
+ if(res==DS_0_1){dblsb->s_comp=DS_0_1;goto guess_ok;}
+ if(res==DS_0_2){dblsb->s_comp=DS_0_2;goto guess_ok;}
+ if(res==JM_0_0){dblsb->s_comp=JM_0_0;goto guess_ok;}
+ if(res==JM_0_1){dblsb->s_comp=JM_0_1;goto guess_ok;}
+ if(res==SQ_0_0){dblsb->s_comp=SQ_0_0;goto guess_ok;}
+ }
+ }
+ }
+ if(dblsb->s_comp==GUESS) /* still unknown ? */
+ { printk(KERN_WARNING "DMSDOS: could not guess compression method for CVF\n");
+ dblsb->s_comp=UNCOMPRESSED;
+ }
+ }
+ guess_ok:
+ printk(KERN_INFO "DMSDOS: write_cluster: guessed 0x%08x.\n",dblsb->s_comp);
+/* we do not need this any longer...
+#ifdef GUESS_HACK
+ if(dblsb->s_comp==SQ_0_0)
+ { dblsb->s_comp=JM_0_1;
+ printk(KERN_WARNING "DMSDOS: guess_hack: guessed SQ-0-0 not supported, using JM-0-1 instead.\n");
+ }
+#endif
+*/
+ }
+
+ method=dblsb->s_comp; /* default compression method */
+
+ size=(length-1)/512+1;
+ if(size==1)method=UNCOMPRESSED; /* it will not become smaller :) */
+ if(ucflag<0||ucflag==UC_UNCOMPR)method=UNCOMPRESSED;/* no need to compress */
+
+ LOG_CLUST("DMSDOS: write_cluster: ucflag=%d, method=0x%x\n",ucflag,method);
+
+ if(method==UNCOMPRESSED) /* includes RAW writes */
+ { clusterk=clusterd;
+ if(ucflag<0)
+ { /* raw write of already compressed data (for dmsdosd/ioctl) */
+ ksize=-ucflag/SECTOR_SIZE; /* must be n*512 for doublespace */
+ mde.size_lo_minus_1=ksize-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=2;
+ }
+ else
+ { /* normal uncompressed write */
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ ksize=size;
+ }
+ }
+ else
+ { if((ucflag==UC_DIRECT)?0:try_daemon(sb,clusternr,length,method))goto wr_uc;
+ clusterk=(unsigned char*)MALLOC(size*SECTOR_SIZE);
+ if(clusterk==NULL)
+ { printk(KERN_WARNING "DMSDOS: write_cluster: no memory for compression, writing uncompressed!\n");
+ wr_uc:
+ clusterk=clusterd;
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ method=UNCOMPRESSED;
+ ksize=size;
+ }
+ else
+ { LOG_CLUST("DMSDOS: write_cluster: compressing...\n");
+ ksize=dbl_compress(clusterk,clusterd,size,method,dblsb->s_cfaktor);
+ LOG_CLUST("DMSDOS: write cluster: compressing finished\n");
+ if(ksize<0)
+ { /* compression failed */
+ FREE(clusterk);
+ clusterk=clusterd;
+ /*mdfat=0xC0000000|((size-1)<<26)|((size-1)<<22);*/
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=3;
+ method=UNCOMPRESSED;
+ ksize=size;
+ }
+ else
+ { /*mdfat=0x80000000|((size-1)<<26)|((ksize-1)<<22);*/
+ mde.size_lo_minus_1=ksize-1;
+ mde.size_hi_minus_1=size-1;
+ mde.flags=2;
+ }
+ }
+ }
+
+ LOG_CLUST("DMSDOS: write_cluster: call dbl_replace_existing_cluster\n");
+ sector=dbl_replace_existing_cluster(sb,clusternr,near_sector,&mde,fraglist);
+ LOG_CLUST("DMSDOS: write_cluster: dbl_replace_existing_cluster returned %d\n",
+ sector);
+ if(sector<0)res=-ENOSPC;
+ else
+ { res=0;
+ /* no SIMULATE write here, this is caught above */
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(mde.unknown&2) /* fragmented */
+ res=write_fragmented(sb,fraglist,clusterk,&mde,ksize);
+ else
+#endif
+ for(i=0;i<ksize;++i)
+ { bh=raw_getblk(sb,sector+i);
+ if(bh==NULL)res=-EIO;
+ else
+ {
+ memcpy(bh->b_data,&(clusterk[SECTOR_SIZE*i]),SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ }
+
+ if(method!=UNCOMPRESSED)FREE(clusterk);
+
+ return res;
+}
+#endif
+
+#define CHECK_INTERVAL 1000
+static int fsc_count=0;
+void check_free_sectors(struct super_block*sb)
+{ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ int i;
+ int c;
+
+ if(fsc_count>CHECK_INTERVAL||dblsb->s_free_sectors<0)
+ { c=0;
+ LOG_ALLOC("DMSDOS: checking free sectors...\n");
+ for(i=dblsb->s_datastart;i<=dblsb->s_dataend;++i)
+ { if(dbl_bitfat_value(sb,i,NULL)==0)++c;
+ }
+ LOG_ALLOC("DMSDOS: free sectors=%d\n",c);
+
+ if(dblsb->s_free_sectors>=0)
+ { if(dblsb->s_free_sectors!=c)
+ { printk(KERN_WARNING "DMSDOS: check_free_sectors: wrong count %d corrected to %d\n",
+ dblsb->s_free_sectors,c);
+ }
+ }
+
+ dblsb->s_free_sectors=c;
+ fsc_count=0;
+ }
+ else
+ ++fsc_count;
+}
+
+/* write a dmsdos cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==1 uncompressed write is forced (only for umsdos --linux-.---)
+ if ucflag<0 raw write is forced with compressed size -ucflag (in bytes)
+ if ucflag==2 simulate write is done (checks for space or reserves space
+ for the cluster in the filesystem but does not actually write it)
+ length==0 or clusterd==NULL means remove the cluster
+ if ucflag==3 perform like ucflag==0 but don't use the daemon
+
+ *********** This function is a generic wrapper ******
+*/
+/* IMPORTANT: if calling ch related routines note that cluster is very
+ likely locked when write is called - it should be locked to prevent
+ modification while being written :)
+*/
+
+int dmsdos_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{ int ret;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: write_cluster clusternr=%d length=%d near_sector=%d\n",
+ clusternr,length,near_sector);
+
+ /* ensure the daemon doesn't use old data and overwrites our data again
+ but don't do this when called by the daemon itself :-/ uuhhh deadlock */
+ /* also don't do it for simulated writes - they change nothing.... */
+ if(ucflag>=0&&ucflag!=2)remove_from_daemon_list(sb,clusternr);
+
+ /* check whether using the daemon is not desired due to speedup bits */
+ if(ucflag==0&&(dmsdos_speedup&SP_USE_DAEMON)==0)ucflag=3;
+
+ check_free_sectors(sb);
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ ret=dbl_write_cluster(sb,clusterd,length,clusternr,near_sector,
+ ucflag);
+ break;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ ret=stac_write_cluster(sb,clusterd,length,clusternr,near_sector,
+ ucflag);
+ break;
+#endif
+ default:
+ printk(KERN_ERR "DMSDOS: write_cluster: illegal cvf_version flag!\n");
+ ret=-EIO;
+ }
+
+ return ret;
+}
+
+#endif /*__KERNEL__ || __DMSDOS_LIB__*/
diff --git a/src/dblspace_dec.c b/src/dblspace_dec.c
new file mode 100644
index 0000000..cec2d76
--- /dev/null
+++ b/src/dblspace_dec.c
@@ -0,0 +1,671 @@
+/*
+dblspace_dec.c
+
+DMSDOS CVF-FAT module: [dbl|drv]space cluster read and decompression routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/locks.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/msdos_fs.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+/* we always need DS decompression */
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+
+#endif
+
+#if !defined(le16_to_cpu)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+#endif
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already readed bits from buf */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+const unsigned dblb_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+/* read next 16 bits from input */
+#define RDN_G16(bits) \
+ { \
+ (bits).buf>>=16; \
+ (bits).pb-=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=((__u32)(le16_to_cpu(*((bits).pd++))))<<16; \
+ }; \
+ }
+
+/* prepares at least 16 bits for reading */
+#define RDN_PR(bits,u) \
+ { \
+ if((bits).pb>=16) RDN_G16(bits); \
+ u=(bits).buf>>(bits).pb; \
+ }
+
+/* initializes reading from bitstream */
+INLINE void dblb_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+}
+
+/* reads n<=16 bits from bitstream *pbits */
+INLINE unsigned dblb_rdn(bits_t *pbits,int n)
+{
+ unsigned u;
+ RDN_PR(*pbits,u);
+ pbits->pb+=n;
+ u&=dblb_bmsk[n];
+ return u;
+}
+
+INLINE int dblb_rdoffs(bits_t *pbits)
+{ unsigned u;
+ RDN_PR(*pbits,u);
+ switch (u&3)
+ {
+ case 0: case 2:
+ pbits->pb+=1+6; return 63&(u>>1);
+ case 1:
+ pbits->pb+=2+8; return (255&(u>>2))+64;
+ }
+ pbits->pb+=2+12; return (4095&(u>>2))+320;
+}
+
+INLINE int dblb_rdlen(bits_t *pbits)
+{ unsigned u;
+ RDN_PR(*pbits,u);
+ switch (u&15)
+ { case 1: case 3: case 5: case 7:
+ case 9: case 11: case 13: case 15:
+ pbits->pb++; return 3;
+ case 2: case 6:
+ case 10: case 14:
+ pbits->pb+=2+1; return (1&(u>>2))+4;
+ case 4: case 12:
+ pbits->pb+=3+2; return (3&(u>>3))+6;
+ case 8:
+ pbits->pb+=4+3; return (7&(u>>4))+10;
+ case 0: ;
+ }
+ switch ((u>>4)&15)
+ { case 1: case 3: case 5: case 7:
+ case 9: case 11: case 13: case 15:
+ pbits->pb+=5+4; return (15&(u>>5))+18;
+ case 2: case 6:
+ case 10: case 14:
+ pbits->pb+=6+5; return (31&(u>>6))+34;
+ case 4: case 12:
+ pbits->pb+=7+6; return (63&(u>>7))+66;
+ case 8:
+ pbits->pb+=8+7; return (127&(u>>8))+130;
+ case 0: ;
+ }
+ pbits->pb+=9;
+ if(u&256) return dblb_rdn(pbits,8)+258;
+ return -1;
+}
+
+INLINE int dblb_decrep(bits_t *pbits, __u8 **p, void *pout, __u8 *pend,
+ int repoffs, int k, int flg)
+{ int replen;
+ __u8 *r;
+
+ if(repoffs==0){LOG_DECOMP("DMSDOS: decrb: zero offset ?\n");return -2;}
+ if(repoffs==0x113f)
+ {
+ int pos=*p-(__u8*)pout;
+ LOG_DECOMP("DMSDOS: decrb: 0x113f sync found.\n");
+ if((pos%512) && !(flg&0x4000))
+ { LOG_DECOMP("DMSDOS: decrb: sync at decompressed pos %d ?\n",pos);
+ return -2;
+ }
+ return 0;
+ }
+ replen=dblb_rdlen(pbits)+k;
+
+ if(replen<=0)
+ {LOG_DECOMP("DMSDOS: decrb: illegal count ?\n");return -2;}
+ if((__u8*)pout+repoffs>*p)
+ {LOG_DECOMP("DMSDOS: decrb: of>pos ?\n");return -2;}
+ if(*p+replen>pend)
+ {LOG_DECOMP("DMSDOS: decrb: output overfill ?\n");return -2;}
+ r=*p-repoffs;
+ M_MOVSB(*p,r,replen);
+ return 0;
+}
+
+/* DS decompression */
+/* flg=0x4000 is used, when called from stacker_dec.c, because of
+ stacker does not store original cluster size and it can mean,
+ that last cluster in file can be ended by garbage */
+int ds_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend;
+ unsigned u, repoffs;
+ int r;
+ bits_t bits;
+
+ dblb_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((dblb_rdn(&bits,16))!=0x5344) return -1;
+
+ u=dblb_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: DS decompression version %d\n",u);
+
+ do
+ { r=0;
+ RDN_PR(bits,u);
+ switch(u&3)
+ {
+ case 0:
+ bits.pb+=2+6;
+ repoffs=(u>>2)&63;
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,-1,flg);
+ break;
+ case 1:
+ bits.pb+=2+7;
+ *(p++)=(u>>2)|128;
+ break;
+ case 2:
+ bits.pb+=2+7;
+ *(p++)=(u>>2)&127;
+ break;
+ case 3:
+ if(u&4) { bits.pb+=3+12; repoffs=((u>>3)&4095)+320; }
+ else { bits.pb+=3+8; repoffs=((u>>3)&255)+64; };
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,-1,flg);
+ break;
+ }
+ }while((r==0)&&(p<pend));
+
+ if(r<0) return r;
+
+ if(!(flg&0x4000))
+ {
+ u=dblb_rdn(&bits,3);if(u==7) u=dblb_rdn(&bits,12)+320;
+ if(u!=0x113f)
+ { LOG_DECOMP("DMSDOS: decrb: final sync not found?\n");
+ return -2;
+ }
+ }
+
+ return p-(__u8*)pout;
+}
+
+/* JM decompression */
+int jm_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend;
+ unsigned u, repoffs;
+ int r;
+ bits_t bits;
+
+ dblb_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((dblb_rdn(&bits,16))!=0x4D4A) return -1;
+
+ u=dblb_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: JM decompression version %d\n",u);
+
+ do
+ { r=0;
+ RDN_PR(bits,u);
+ switch(u&3)
+ {
+ case 0:
+ case 2:
+ bits.pb+=8;
+ *(p++)=(u>>1)&127;
+ break;
+ case 1:
+ bits.pb+=2;
+ repoffs=dblb_rdoffs(&bits);
+ r=dblb_decrep(&bits,&p,pout,pend,repoffs,0,flg);
+ break;
+ case 3:
+ bits.pb+=9;
+ *(p++)=((u>>2)&127)|128;
+ break;
+ }
+ }
+ while((r==0)&&(p<pend));
+
+ if(r<0) return r;
+
+ if(!(flg&0x4000))
+ {
+ u=dblb_rdn(&bits,2);if(u==1) u=dblb_rdoffs(&bits);
+ if(u!=0x113f)
+ { LOG_DECOMP("DMSDOS: decrb: final sync not found?\n");
+ return -2;
+ }
+ }
+
+ return p-(__u8*)pout;
+}
+
+
+/* decompress a compressed doublespace/drivespace cluster clusterk to clusterd
+*/
+int dbl_decompress(unsigned char*clusterd, unsigned char*clusterk,
+ Mdfat_entry*mde)
+{
+ int sekcount;
+ int r, lin, lout;
+
+ sekcount=mde->size_hi_minus_1+1;
+ lin=(mde->size_lo_minus_1+1)*SECTOR_SIZE;
+ lout=(mde->size_hi_minus_1+1)*SECTOR_SIZE;
+
+ switch(clusterk[0]+((int)clusterk[1]<<8)+
+ ((int)clusterk[2]<<16)+((int)clusterk[3]<<24))
+ {
+ case DS_0_0:
+ case DS_0_1:
+ case DS_0_2:
+ LOG_DECOMP("DMSDOS: decompressing DS-0-x\n");
+ r=ds_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: error in DS-0-x compressed data.\n");
+ return -2;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+
+ case JM_0_0:
+ case JM_0_1:
+ LOG_DECOMP("DMSDOS: decompressing JM-0-x\n");
+ r=jm_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: error in JM-0-x compressed data.\n");
+ return -2;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case SQ_0_0:
+ LOG_DECOMP("DMSDOS: decompressing SQ-0-0\n");
+ r=sq_dec(clusterk,lin,clusterd,lout,0);
+ if(r<=0)
+ { printk(KERN_ERR "DMSDOS: SQ-0-0 decompression failed.\n");
+ return -1;
+ }
+ LOG_DECOMP("DMSDOS: decompress finished.\n");
+ return 0;
+#endif
+
+ default:
+ printk(KERN_ERR "DMSDOS: compression method not recognized.\n");
+ return -1;
+
+ } /* end switch */
+
+ return 0;
+}
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+/* read the fragments of a fragmented cluster and assemble them */
+/* warning: this is guessed from low level viewing drivespace 3 disks
+ and may be awfully wrong... we'll see... */
+int read_fragments(struct super_block*sb,Mdfat_entry*mde, unsigned char*data)
+{ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int fragcount;
+ int fragpnt;
+ int offset;
+ int sector;
+ int seccount;
+ int membytes;
+ int safety_counter;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* read first sector */
+ sector=mde->sector_minus_1+1;
+ bh=raw_bread(sb,sector);
+ if(bh==NULL)return -EIO;
+ fragcount=bh->b_data[0];
+ if(bh->b_data[1]!=0||bh->b_data[2]!=0||bh->b_data[3]!=0||fragcount<=0||
+ fragcount>dblsb->s_sectperclust)
+ { printk(KERN_ERR "DMSDOS: read_fragments: cluster does not look fragmented!\n");
+ raw_brelse(sb,bh);
+ return -EIO;
+ }
+ membytes=dblsb->s_sectperclust*SECTOR_SIZE;
+ if(mde->flags&1)
+ { offset=0;
+ safety_counter=0;
+ }
+ else
+ { offset=(fragcount+1)*4;
+ /* copy the rest of the sector */
+ memcpy(data,&(bh->b_data[offset]),SECTOR_SIZE-offset);
+ data+=(SECTOR_SIZE-offset);
+ safety_counter=SECTOR_SIZE-offset;
+ }
+ ++sector;
+ seccount=mde->size_lo_minus_1;
+ fragpnt=1;
+ while(fragpnt<=fragcount)
+ { if(fragpnt>1)
+ { /* read next fragment pointers */
+ seccount=bh->b_data[fragpnt*4+3];
+ seccount&=0xff;
+ seccount/=4;
+ seccount+=1;
+ sector=bh->b_data[fragpnt*4];
+ sector&=0xff;
+ sector+=bh->b_data[fragpnt*4+1]<<8;
+ sector&=0xffff;
+ sector+=bh->b_data[fragpnt*4+2]<<16;
+ sector&=0xffffff;
+ sector+=1;
+ }
+ while(seccount)
+ { bh2=raw_bread(sb,sector);
+ if(bh2==NULL){raw_brelse(sb,bh);return -EIO;}
+ /*printk(KERN_DEBUG "DMSDOS: read_fragments: data=0x%p safety_counter=0x%x sector=%d\n",
+ data,safety_counter,sector);*/
+ if(safety_counter+SECTOR_SIZE>membytes)
+ { int maxbytes=membytes-safety_counter;
+ if(maxbytes<=0)
+ { printk(KERN_WARNING "DMSDOS: read_fragments: safety_counter exceeds membytes!\n");
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ return -EIO;
+ }
+ printk(KERN_DEBUG "DMSDOS: read_fragments: size limit reached.\n");
+ memcpy(data,bh2->b_data,maxbytes);
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ return membytes;
+ }
+ else memcpy(data,bh2->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh2);
+ data+=SECTOR_SIZE;
+ safety_counter+=SECTOR_SIZE;
+ ++sector;
+ --seccount;
+ }
+ ++fragpnt;
+ }
+ raw_brelse(sb,bh);
+
+ return safety_counter;
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+/* read a complete file cluster and decompress it if necessary;
+ this function is unable to read cluster 0 (CVF root directory) */
+/* returns cluster length in bytes or error (<0) */
+/* this function is specific to doublespace/drivespace */
+int dbl_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+{ Mdfat_entry mde;
+ unsigned char*clusterk;
+ int nr_of_sectors;
+ int i;
+ struct buffer_head*bh;
+ int membytes;
+ int sector;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: dbl_read_cluster %d\n",clusternr);
+
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+
+ if((mde.flags&2)==0)
+ { /* hmm, cluster is unused (it's a lost or ghost cluster)
+ and contains undefined data, but it *is* readable */
+ /* oh no, it contains ZEROD data per definition...
+ this is really important */
+ if(clusterd) /*clusterd==NULL means read_ahead - don't do anything*/
+ memset(clusterd,0,dblsb->s_sectperclust*SECTOR_SIZE);
+ LOG_CLUST("DMSDOS: lost cluster %d detected\n",clusternr);
+ return 0; /* yes, has length zero */
+ }
+
+ sector=mde.sector_minus_1+1;
+ nr_of_sectors=mde.size_lo_minus_1+1;/* real sectors on disk */
+ if(nr_of_sectors>dblsb->s_sectperclust)
+ { printk(KERN_WARNING "DMSDOS: read_cluster: mdfat sectors > sectperclust, cutting\n");
+ nr_of_sectors=dblsb->s_sectperclust;
+ }
+
+ if(clusterd==NULL)
+ { /* read-ahead */
+ dblspace_reada(sb,sector,nr_of_sectors);
+ return 0;
+ }
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+ if(mde.unknown&2)
+ { /* we suppose this bit indicates a fragmented cluster */
+ /* this is *not sure* and may be awfully wrong - reports
+ whether success or not are welcome
+ */
+
+ LOG_CLUST("DMSDOS: cluster %d has unknown bit #1 set. Assuming fragmented cluster.\n",
+ clusternr);
+
+ if(mde.flags&1) /* not compressed */
+ { LOG_CLUST("DMSDOS: uncompressed fragmented cluster\n");
+ i=read_fragments(sb,&mde,clusterd);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: read_fragments failed!\n");
+ return i;
+ }
+ }
+ else
+ { LOG_CLUST("DMSDOS: compressed fragmented cluster\n");
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+
+ clusterk=(unsigned char*)MALLOC(membytes);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: no memory for decompression!\n");
+ return -2;
+ }
+ /* returns length in bytes */
+ i=read_fragments(sb,&mde,clusterk);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: read_fragments failed!\n");
+ return i;
+ }
+ /* correct wrong size_lo information (sq_dec needs it) */
+ if(i>0)mde.size_lo_minus_1=(i-1)/SECTOR_SIZE;
+ i=dbl_decompress(clusterd,clusterk,&mde);
+
+ FREE(clusterk);
+
+ if(i)
+ { printk(KERN_ERR "DMSDOS: decompression of cluster %d in CVF failed.\n",
+ clusternr);
+ return i;
+ }
+
+ }
+
+ /* the slack must be zerod out */
+ if(mde.size_hi_minus_1+1<dblsb->s_sectperclust)
+ { memset(clusterd+(mde.size_hi_minus_1+1)*SECTOR_SIZE,0,
+ (dblsb->s_sectperclust-mde.size_hi_minus_1-1)*
+ SECTOR_SIZE);
+ }
+
+ return (mde.size_hi_minus_1+1)*SECTOR_SIZE;
+
+ } /* end of read routine for fragmented cluster */
+#endif
+
+ if(mde.flags&1)
+ { /* cluster is not compressed */
+ for(i=0;i<nr_of_sectors;++i)
+ { bh=raw_bread(sb,sector+i);
+ if(bh==NULL)return -EIO;
+ memcpy(&clusterd[i*SECTOR_SIZE],bh->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh);
+ }
+ }
+ else
+ { /* cluster is compressed */
+
+ membytes=SECTOR_SIZE*nr_of_sectors;
+
+ clusterk=(unsigned char*)MALLOC(membytes);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: no memory for decompression!\n");
+ return -2;
+ }
+
+ for(i=0;i<nr_of_sectors;++i)
+ { bh=raw_bread(sb,sector+i);
+ if(bh==NULL)
+ { FREE(clusterk);
+ return -EIO;
+ }
+ memcpy(&clusterk[i*SECTOR_SIZE],bh->b_data,SECTOR_SIZE);
+ raw_brelse(sb,bh);
+ }
+
+ i=dbl_decompress(clusterd,clusterk,&mde);
+
+ FREE(clusterk);
+
+ if(i)
+ { printk(KERN_ERR "DMSDOS: decompression of cluster %d in CVF failed.\n",
+ clusternr);
+ return i;
+ }
+
+ }
+
+ /* the slack must be zerod out */
+ if(mde.size_hi_minus_1+1<dblsb->s_sectperclust)
+ { memset(clusterd+(mde.size_hi_minus_1+1)*SECTOR_SIZE,0,
+ (dblsb->s_sectperclust-mde.size_hi_minus_1-1)*
+ SECTOR_SIZE);
+ }
+
+ return (mde.size_hi_minus_1+1)*SECTOR_SIZE;
+}
+#endif
+
+/* read a complete file cluster and decompress it if necessary;
+ it must be able to read directories
+ this function is unable to read cluster 0 (CVF root directory) */
+/* returns cluster length in bytes or error (<0) */
+/* this function is a generic wrapper */
+int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr)
+{ int ret;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_CLUST("DMSDOS: read_cluster %d\n",clusternr);
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ ret=dbl_read_cluster(sb,clusterd,clusternr);
+ break;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ ret=stac_read_cluster(sb,clusterd,clusternr);
+ break;
+#endif
+ default:
+ printk(KERN_ERR "DMSDOS: read_cluster: illegal cvf version flag!\n");
+ ret=-EIO;
+ }
+
+ return ret;
+}
diff --git a/src/dblspace_fileops.c b/src/dblspace_fileops.c
new file mode 100644
index 0000000..fb37e8a
--- /dev/null
+++ b/src/dblspace_fileops.c
@@ -0,0 +1,42 @@
+/*
+dblspace_fileops.c
+
+DMSDOS CVF-FAT module: file operation routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+/* UUHHHH .... too much has changed in 2.1.xx... we need a complete rewrite */
+
+#include"dmsdos.h"
+
+#ifdef __FOR_KERNEL_2_3_10
+ #include "dblspace_fileops.c-2.3.10"
+#else
+ #ifdef __FOR_KERNEL_2_1_80
+ #include "dblspace_fileops.c-2.1.80"
+ #else
+ #include "dblspace_fileops.c-2.0.33"
+ #endif
+#endif
+
diff --git a/src/dblspace_fileops.c-2.0.33 b/src/dblspace_fileops.c-2.0.33
new file mode 100644
index 0000000..eff9e76
--- /dev/null
+++ b/src/dblspace_fileops.c-2.0.33
@@ -0,0 +1,660 @@
+/*
+dblspace_fileops.c-2.0.33
+
+DMSDOS CVF-FAT module: file operation routines (for kernel 2.0.33).
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct inode *inode,
+ struct file *filp,
+ char *buf,
+ int count)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_read: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_read: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ if(count<=0)return 0;
+
+ if(filp->f_pos>=inode->i_size)return 0;
+
+ if(filp->f_pos+count>inode->i_size)count=inode->i_size-filp->f_pos;
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=filp->f_pos&(membytes-1);
+ clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_read;
+
+ do
+ { ch=ch_read(sb,clusternr,0);
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk(KERN_DEBUG "DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ filp->f_pos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(filp->f_pos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+
+text_read:
+ /* ok, let's do it byte for byte..... */
+ bytes_read=0;
+ need_cluster=1;
+ ch=NULL;
+ while(count>0&&inode->i_size>filp->f_pos)
+ { if(need_cluster)
+ { clusternr=get_cluster(inode,filp->f_pos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: get_cluster failed (FAT problem ?)\n");
+ return -EIO;
+ }
+ ch=ch_read(sb,clusternr,0);
+ if(ch==NULL)return -EIO;
+ if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ clusternr=dbl_fat_nextcluster(sb,clusternr,NULL);
+ need_cluster=0;
+ }
+
+ datum=ch->c_data[offset++];
+
+ ++(filp->f_pos);
+ if(datum!=13)
+ { put_fs_byte(datum,&(buf[bytes_read]));
+ ++bytes_read;
+ --count;
+ }
+ if(offset==membytes)
+ { need_cluster=1;
+ ch_free(ch);
+ ch=NULL;
+ offset=0;
+ }
+ }
+
+ if(ch)ch_free(ch);
+ return bytes_read;
+}
+
+int dblspace_file_write(struct inode *inode, struct file *filp,
+ const char *buf, int count)
+{ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ int cr;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) filp->f_pos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+
+ offset=filp->f_pos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_write;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above*/
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_DEBUG "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_WARNING "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ filp->f_pos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(filp->f_pos>inode->i_size)
+ { inode->i_size=filp->f_pos;
+ inode->i_dirt=1;
+ }
+
+ return (written==0)?ret:written;
+
+text_write:
+ written=0;
+ need_cluster=1;
+ cr=0;
+ ch=NULL;
+ while(count>0||cr==1)
+ { if(need_cluster)
+ { LOG_CLUST("DMSDOS: text_write: need_cluster=%d\n",
+ cluster);
+ /* we cannot simply calculate here...
+ so we never know and must always read the cluster */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ need_cluster=0;
+ }
+
+ if(cr==1)
+ { datum=10;
+ cr=0;
+ }
+ else
+ { datum=get_fs_byte(&(buf[written]));
+ ++written;
+ if(datum==10)
+ { datum=13;
+ cr=1;
+ }
+ --count;
+ }
+
+ ch->c_data[offset++]=datum;
+
+ ++(filp->f_pos);
+ if(offset>ch->c_length)ch->c_length=offset;
+
+ if(offset==clustersize)
+ { /* cluster is full and must be written back */
+ /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write :) */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ /*check whether end reached */
+ if(count==0&&cr==0)
+ { offset=0; /* tells that there's no rest */
+ break;
+ }
+ /* check whether a new cluster is needed */
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ {
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,filp->f_pos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we called with KEEP_LOCK above */
+ ch->c_length=SECTOR_SIZE;
+ }
+ else need_cluster=1;
+ offset=0;
+ }
+ }
+
+ /* check whether there's a rest to be written */
+ if(offset)
+ { /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ if(ret==0)ret=-EIO;
+ }
+ }
+
+ if(ch)ch_free(ch);
+
+ if(filp->f_pos>inode->i_size)
+ { inode->i_size=filp->f_pos;
+ inode->i_dirt=1;
+ }
+
+ return (written==0)?ret:written;
+}
+
+#ifdef DMSDOS_USE_READPAGE
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = dblspace_file_read (inode,&filp,
+ (char*)address,
+ need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk (KERN_ERR "DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+int dblspace_readpage(struct inode *inode, struct page *page)
+{ unsigned long address;
+ int error = -1;
+
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ page->count++;
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+ error=read_the_page(address,page->offset,inode);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* DMSDOS_USE_READPAGE */
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ {
+ unsigned long cur_fs = get_fs();
+ set_fs (KERNEL_DS);
+ cur_read = dblspace_file_read (inode,&filp,(char*)page
+ ,need_read);
+ set_fs (cur_fs);
+ }
+ if (cur_read != need_read){
+ printk (KERN_ERR "DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct inode*inode,struct file*file,struct vm_area_struct*vma)
+{
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ inode->i_dirt = 1;
+ }
+
+ vma->vm_inode = inode;
+ inode->i_count++;
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+#endif /* else / DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_fileops.c-2.1.80 b/src/dblspace_fileops.c-2.1.80
new file mode 100644
index 0000000..554043c
--- /dev/null
+++ b/src/dblspace_fileops.c-2.1.80
@@ -0,0 +1,722 @@
+/*
+dblspace_fileops.c-2.1.80
+
+DMSDOS CVF-FAT module: file operation routines (for kernel>=2.1.80).
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct inode *inode;
+ struct super_block*sb;
+ Dblsb*dblsb;
+
+ LOG_FS("DMSDOS: file_read start...\n");
+
+ inode = filp->f_dentry->d_inode;
+ LOG_FS("DMSDOS: file_read: got inode\n");
+ sb=inode->i_sb;
+ LOG_FS("DMSDOS: file_read: got sb\n");
+ dblsb=MSDOS_SB(sb)->private_data;
+ LOG_FS("DMSDOS: file_read: got dblsb\n");
+
+ if (!inode) {
+ printk(KERN_ERR "DMSDOS: file_read: inode = NULL, rejected.\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "DMSDOS: file_read: mode = %07o, rejected.\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ LOG_FS("DMSDOS: file_read init complete...\n");
+
+ if(count<=0)return 0;
+
+ if(*ppos>=inode->i_size)return 0;
+
+ if(*ppos+count>inode->i_size)count=inode->i_size-*ppos;
+
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=*ppos&(membytes-1);
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_read;
+
+ do
+ { LOG_CLUST("DMSDOS: file_read: calling ch_read...\n");
+ ch=ch_read(sb,clusternr,0);
+ LOG_CLUST("DMSDOS: file_read: after ch_read\n");
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk("DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ *ppos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(*ppos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+
+text_read:
+ /* ok, let's do it byte for byte..... */
+ bytes_read=0;
+ need_cluster=1;
+ ch=NULL;
+ while(count>0&&inode->i_size>*ppos)
+ { if(need_cluster)
+ { clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: get_cluster failed (FAT problem ?)\n");
+ return -EIO;
+ }
+ ch=ch_read(sb,clusternr,0);
+ if(ch==NULL)return -EIO;
+ if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ clusternr=dbl_fat_nextcluster(sb,clusternr,NULL);
+ need_cluster=0;
+ }
+
+ datum=ch->c_data[offset++];
+
+ ++(*ppos);
+ if(datum!=13)
+ { /*put_fs_byte(datum,&(buf[bytes_read]));*/
+ memcpy_tofs(&(buf[bytes_read]),&datum,1);
+ ++bytes_read;
+ --count;
+ }
+ if(offset==membytes)
+ { need_cluster=1;
+ ch_free(ch);
+ ch=NULL;
+ offset=0;
+ }
+ }
+
+ if(ch)ch_free(ch);
+ return bytes_read;
+}
+
+int dblspace_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ int cr;
+ char datum;
+ int need_cluster;
+ Cluster_head*ch;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) *ppos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+
+ offset=*ppos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ if(MSDOS_I(inode)->i_binary==0)goto text_write;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_ERR "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_ERR "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ *ppos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+
+text_write:
+ written=0;
+ need_cluster=1;
+ cr=0;
+ ch=NULL;
+ while(count>0||cr==1)
+ { if(need_cluster)
+ { LOG_CLUST("DMSDOS: text_write: need_cluster=%d\n",
+ cluster);
+ /* we cannot simply calculate here...
+ so we never know and must always read the cluster */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ need_cluster=0;
+ }
+
+ if(cr==1)
+ { datum=10;
+ cr=0;
+ }
+ else
+ { /*datum=get_fs_byte(&(buf[written]));*/
+ memcpy_fromfs(&datum,&(buf[written]),1);
+ ++written;
+ if(datum==10)
+ { datum=13;
+ cr=1;
+ }
+ --count;
+ }
+
+ ch->c_data[offset++]=datum;
+
+ ++(*ppos);
+ if(offset>ch->c_length)ch->c_length=offset;
+
+ if(offset==clustersize)
+ { /* cluster is full and must be written back */
+ /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ /*check whether end reached */
+ if(count==0&&cr==0)
+ { offset=0; /* tells that there's no rest */
+ break;
+ }
+ /* check whether a new cluster is needed */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ {
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we called with KEEP_LOCK above */
+ ch->c_length=SECTOR_SIZE;
+ }
+ else need_cluster=1;
+ offset=0;
+ }
+ }
+
+ /* check whether there's a rest to be written */
+ if(offset)
+ { /*unlock_ch(ch);*/
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ if(ret==0)ret=-EIO;
+ }
+ }
+
+ if(ch)ch_free(ch);
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+}
+
+
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+#ifdef DMSDOS_USE_READPAGE
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: read_the_page: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: read_the_page: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,
+ (char*)address,
+ need_read,
+ &(filp.f_pos));
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+#ifdef READPAGE_DENTRY
+int dblspace_readpage(struct dentry*dentry, struct page *page)
+{ unsigned long address;
+ int error = -1;
+ struct inode*inode=dentry->d_inode;
+#else
+int dblspace_readpage(struct inode *inode, struct page *page)
+{ unsigned long address;
+ int error = -1;
+#endif
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ atomic_inc(&page->count);
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+ error=read_the_page(address,page->offset,inode);
+
+ LOG_FS("DMSDOS: readpage: read_the_page returned %d\n",error);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* from: ifdef DMSDOS_USE_READPAGE */
+
+/* this is supposed to be obsolete stuff for older kernels... */
+
+#if LINUX_VERSION_CODE >= LVC(2,1,90)
+#error kernel >=2.1.90 requires readpage interface, rerun dmsdos configuration
+#endif
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+ LOG_FS("DMSDOS: file_mmap_nopage: got page\n");
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: file_mmap_nopage: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: file_mmap_nopage: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,(char*)page,
+ need_read,&filp.f_pos);
+ LOG_FS("DMSDOS: file_mmap_nopage: file_read returned\n");
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ LOG_FS("DMSDOS: file_mmap_nopage: end\n");
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct file*file,struct vm_area_struct*vma)
+{ struct inode *inode = file->f_dentry->d_inode;
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ /*inode->i_dirt = 1;*/
+ mark_inode_dirty(inode);
+ }
+
+ vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+
+#endif /* from: else / ifdef DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_fileops.c-2.3.10 b/src/dblspace_fileops.c-2.3.10
new file mode 100644
index 0000000..a5b9b51
--- /dev/null
+++ b/src/dblspace_fileops.c-2.3.10
@@ -0,0 +1,563 @@
+/*
+dblspace_fileops.c-2.1.80
+
+DMSDOS CVF-FAT module: file operation routines (for kernel>=2.1.80).
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include "dmsdos.h"
+
+extern unsigned long dmsdos_speedup;
+
+#define MIN(x,y) ( ( (x)<(y) ) ? (x) : (y) )
+
+void do_cluster_reada(struct super_block*sb,int clusternr)
+{ /* read one cluster ahead without waiting for the data */
+ int nextclust;
+
+ nextclust=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(nextclust>0)
+ { /* no need to read-ahead if it is in cache */
+ /* for a simple search for existence we needn't lock the cache */
+ if(find_in_ccache(sb,nextclust,NULL,NULL)==NULL)
+ dmsdos_read_cluster(sb,NULL,nextclust);
+ }
+}
+
+int dblspace_file_read(
+ struct file *filp,
+ char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int clusternr;
+ /*unsigned char*clusterd;*/
+ int offset;
+ int bytes_read;
+ int membytes;
+ int membytes_bits;
+ int ret;
+ char * b;
+ int toread;
+ Cluster_head*ch;
+ struct inode *inode;
+ struct super_block*sb;
+ Dblsb*dblsb;
+
+ LOG_FS("DMSDOS: file_read start...\n");
+
+ inode = filp->f_dentry->d_inode;
+ LOG_FS("DMSDOS: file_read: got inode\n");
+ sb=inode->i_sb;
+ LOG_FS("DMSDOS: file_read: got sb\n");
+ dblsb=MSDOS_SB(sb)->private_data;
+ LOG_FS("DMSDOS: file_read: got dblsb\n");
+
+ if (!inode) {
+ printk(KERN_ERR "DMSDOS: file_read: inode = NULL, rejected.\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "DMSDOS: file_read: mode = %07o, rejected.\n",inode->i_mode);
+ return -EINVAL;
+ }
+
+ LOG_FS("DMSDOS: file_read init complete...\n");
+
+ if(count<=0)return 0;
+
+ if(*ppos>=inode->i_size)return 0;
+
+ if(*ppos+count>inode->i_size)count=inode->i_size-*ppos;
+
+ ret=verify_area(VERIFY_WRITE, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ membytes_bits=SECTOR_BITS+dblsb->s_spc_bits;
+
+ /* calculate clusternr for cluster to read */
+ offset=*ppos&(membytes-1);
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(clusternr<=0)
+ { printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ return 0;
+ }
+
+ bytes_read=0;
+ b=buf;
+
+ do
+ { LOG_CLUST("DMSDOS: file_read: calling ch_read...\n");
+ ch=ch_read(sb,clusternr,0);
+ LOG_CLUST("DMSDOS: file_read: after ch_read\n");
+ ret=(ch==NULL)?-EIO:0;
+ if(ret>=0)
+ { if(count>READA_THRESHOLD&&(dmsdos_speedup&SP_USE_READ_AHEAD)!=0)
+ do_cluster_reada(inode->i_sb,clusternr);
+ toread=(membytes-offset>count) ? count : membytes-offset;
+ /*printk("DMSDOS file_readx: memcpy_tofs(0x%08x,0x%08x,0x%08x)\n",
+ b,clusterd+offset,toread);*/
+ memcpy_tofs(b,ch->c_data+offset,toread);
+ bytes_read+=toread;
+ *ppos+=toread;
+ count-=toread;
+ ch_free(ch);
+ if(count>0)
+ { b+=toread;
+ offset=0;
+ LOG_CLUST("DMSDOS: file_read: get_cluster...\n");
+ clusternr=get_cluster(inode,*ppos>>membytes_bits);
+ if(*ppos&(membytes-1))
+ panic("DMSDOS: read_file bug: f_pos not cluster-aligned");
+ if(clusternr<=0)
+ { ret=-EIO;
+ printk(KERN_ERR "DMSDOS: file_readx: FAT mismatches file size for ino=%ld\n",
+ inode->i_ino);
+ }
+ }
+ }
+ }
+ while(count>0&&ret>=0);
+
+ return (bytes_read==0&&ret<0)?ret:bytes_read;
+}
+
+int dblspace_file_write(
+ struct file *filp,
+ const char *buf,
+ size_t count,
+ loff_t *ppos)
+{
+ int cluster;
+ int ret=0;
+ unsigned int offset;
+ const unsigned char *b;
+ int canwrite;
+ int written;
+ int clustersize;
+ int clustersize_bits;
+ int uc;
+ Cluster_head*ch;
+ struct inode *inode = filp->f_dentry->d_inode;
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if (!inode) {
+ printk(KERN_ERR "dmsdos_file_write: inode = NULL\n");
+ return -EINVAL;
+ }
+ /* S_ISLNK allows for UMSDOS. Should never happen for normal MSDOS */
+ if (!S_ISREG(inode->i_mode) && !S_ISLNK(inode->i_mode)) {
+ printk(KERN_ERR "dmsdos_file_write: mode = %07o\n",inode->i_mode);
+ return -EINVAL;
+ }
+/*
+ * ok, append may not work when many processes are writing at the same time
+ * but so what. That way leads to madness anyway.
+ */
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: file_write: READ-ONLY filesystem\n");
+ return -EROFS;
+ }
+
+ if(dblsb->s_comp==READ_ONLY)return -EPERM;
+
+ if (filp->f_flags & O_APPEND) *ppos = inode->i_size;
+ if (count <= 0) return 0;
+
+ ret=verify_area(VERIFY_READ, buf, count);
+ if(ret<0)return ret;
+ ret=0;
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+ clustersize_bits=dblsb->s_spc_bits+SECTOR_BITS;
+
+ uc=0;
+ #if 0
+ if(dmsdos_speedup&SP_NO_EMD_COMPR)
+ uc=(MSDOS_I(inode)->i_binary>1)?1:0; /* uncompressed forced */
+ #endif
+ #warning Umsdos EMD speedup is not implemented
+
+ offset=*ppos&(clustersize-1);
+ do
+ { cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster>0)break;
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ return -ENOSPC;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ return -ENOSPC;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ return -ENOSPC;
+ }
+ }
+ while(cluster<=0);
+
+ LOG_CLUST("DMSDOS: file_write: beginning with cluster %d\n",
+ cluster);
+
+ b=buf;
+ written=0;
+
+ while(count>0)
+ {
+ if(offset>0||count<clustersize)
+ { /* cluster must be read because it will only partially overwritten */
+ LOG_CLUST("DMSDOS: write_file: reading cluster %d...\n",cluster);
+ ch=ch_read(sb,cluster,C_KEEP_LOCK);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: read_cluster failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ }
+ else
+ { /* cluster will be fully overwritten, don't read it */
+ ch=ch_read(sb,cluster,C_KEEP_LOCK|C_NO_READ);
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_noread failed!\n");
+ ret=-EIO;
+ break;
+ }
+ /*lock_ch(ch); we call with KEEP_LOCK above */
+ ch->c_length= (count+offset<clustersize) ?
+ count+offset : clustersize;
+ }
+ canwrite=MIN(clustersize-offset,count);
+ memcpy_fromfs(&(ch->c_data[offset]),b,canwrite);
+
+ /* did cluster grow ? */
+ if(canwrite+offset>ch->c_length)
+ { /*printk(KERN_ERR "DMSDOS: write_file: write beyond logical cluster end, appending.\n");
+ */
+ ch->c_length=canwrite+offset;
+ }
+ if(ch->c_length>clustersize)
+ { printk(KERN_ERR "DMSDOS: write_file: length>clustersize ??? bug !!!\n");
+ ch->c_length=clustersize;
+ }
+
+ /*unlock_ch(ch); no not here*/
+
+ LOG_CLUST("DMSDOS: write_file: writing cluster %d...\n",cluster);
+
+ /* ch_dirty_locked unlocks the cluster */
+ if(ch_dirty_locked(ch,0,uc)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: ch_dirty failed!\n");
+ ch_free(ch);
+ ch=NULL;
+ ret=-EIO;
+ break;
+ }
+ ch_free(ch);
+ ch=NULL;
+
+ offset=0;
+ b+=canwrite;
+ *ppos+=canwrite;
+ written+=canwrite;
+ count-=canwrite;
+
+ if(count==0)break; /* braucht keinen neuen cluster mehr*/
+
+ /* next cluster ? */
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { LOG_CLUST("DMSDOS: write_file: write_loop: allocating new cluster\n");
+ if(dblsb->s_full==2)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (full flag set)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(dblsb->s_free_sectors<MIN_FREE_SECTORS)
+ { printk(KERN_ERR "DMSDOS: write_file: CVF full (free sector count too low)\n");
+ ret=-ENOSPC;
+ break;
+ }
+ if(fat_add_cluster(inode)<0)
+ { printk(KERN_ERR "DMSDOS: write_file: fat_add_cluster failed\n");
+ ret=-ENOSPC;
+ break;
+ }
+ cluster=get_cluster(inode,*ppos>>clustersize_bits);
+ if(cluster<=0)
+ { printk(KERN_ERR "DMSDOS: write_file: something's wrong, cannot happen\n");
+ ret=-EIO;
+ break;
+ }
+ }
+ }
+
+ if(*ppos>inode->i_size)
+ { inode->i_size=*ppos;
+ /*inode->i_dirt=1; .... HMMM .... */
+ mark_inode_dirty(inode);
+ }
+
+ return (written==0)?ret:written;
+}
+
+
+/* Grmpf.... partially untested code. Don't know an application that does
+ writable mmaps, but it would be needed for testing this piece of code */
+
+/* idea: kernel does buffer reads with bmap calculated buffers - impossible
+ for dmsdos. (see kernel mmap code).
+ kernel emulates writable mmap by calling file_write when no buffers
+ are present - should work for dmsdos.
+ so we do file_read-emulated readable mmaps here though the
+ generic_mmap function is used.
+*/
+
+#ifdef DMSDOS_USE_READPAGE
+int read_the_page(unsigned long address, unsigned long pos,
+ struct inode*inode)
+{
+ unsigned int clear;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: read_the_page\n");
+
+ address &= PAGE_MASK;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: read_the_page: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: read_the_page: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,
+ (char*)address,
+ need_read,
+ &(filp.f_pos));
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ return -1;
+ }
+ }
+ if (clear > 0){
+ memset ((char*)address+PAGE_SIZE-clear,0,clear);
+ }
+ return 0;
+}
+
+#ifndef READPAGE_DENTRY
+int dblspace_readpage(struct file *file, struct page *page)
+#else
+int dblspace_readpage(struct dentry*dentry, struct page *page)
+#endif
+{ unsigned long address;
+ int error = -1;
+ #ifndef READPAGE_DENTRY
+ struct dentry * dentry = file->f_dentry;
+ #endif
+ struct inode * inode = dentry->d_inode;
+
+ LOG_FS("DMSDOS: readpage %08lx\n", page_address(page));
+
+ address = page_address(page);
+ atomic_inc(&page->count);
+ set_bit(PG_locked, &page->flags);
+
+ /* now read the data */
+#ifndef __FOR_KERNEL_2_3_30
+ error=read_the_page(address,page->offset,inode);
+#else /* __FOR_KERNEL_2_3_30 */
+ error=read_the_page(address,page->index<<PAGE_CACHE_SHIFT,inode);
+#endif /* __FOR_KERNEL_2_3_30 */
+
+ LOG_FS("DMSDOS: readpage: read_the_page returned %d\n",error);
+
+ if(error==0)set_bit(PG_uptodate, &page->flags);
+
+ clear_bit(PG_locked, &page->flags);
+ wake_up(&page->wait);
+
+ free_page(address);
+ return error;
+}
+
+#else /* from: ifdef DMSDOS_USE_READPAGE */
+
+/* this is supposed to be obsolete stuff for older kernels... */
+
+#if LINUX_VERSION_CODE >= LVC(2,1,90)
+#error kernel >=2.1.90 requires readpage interface, rerun dmsdos configuration
+#endif
+
+/*
+ * Fill in the supplied page for mmap
+ */
+static unsigned long dblspace_file_mmap_nopage(
+ struct vm_area_struct * area,
+ unsigned long address,
+ int error_code)
+{
+ struct inode * inode = area->vm_dentry->d_inode;
+ unsigned long page;
+ unsigned int clear;
+ int pos;
+ long gap; /* distance from eof to pos */
+
+ LOG_FS("DMSDOS: file_mmap_nopage\n");
+ page = __get_free_page(GFP_KERNEL);
+ if (!page)return page;
+ LOG_FS("DMSDOS: file_mmap_nopage: got page\n");
+
+ address &= PAGE_MASK;
+ pos = address - area->vm_start + area->vm_offset;
+
+ clear = 0;
+ gap = inode->i_size - pos;
+ if (gap <= 0){
+ /* mmaping beyond end of file */
+ clear = PAGE_SIZE;
+ }else{
+ int cur_read;
+ int need_read;
+ struct file filp;
+ if (gap < PAGE_SIZE){
+ clear = PAGE_SIZE - gap;
+ }
+ filp.f_reada = 0;
+ filp.f_pos = pos;
+ need_read = PAGE_SIZE - clear;
+ { mm_segment_t cur_fs = get_fs();
+ filp.f_dentry=kmalloc(sizeof(struct dentry),GFP_KERNEL);
+ if(filp.f_dentry==NULL)
+ { printk(KERN_ERR "DMSDOS: file_mmap_nopage: no memory!\n");
+ return -1;
+ }
+ filp.f_dentry->d_inode=inode;
+
+ set_fs (KERNEL_DS);
+ LOG_FS("DMSDOS: file_mmap_nopage: calling file_read...\n");
+ cur_read = dblspace_file_read (&filp,(char*)page,
+ need_read,&filp.f_pos);
+ LOG_FS("DMSDOS: file_mmap_nopage: file_read returned\n");
+ set_fs (cur_fs);
+ kfree(filp.f_dentry);
+ }
+ if (cur_read != need_read){
+ printk ("DMSDOS: Error while reading an mmap file %d <> %d\n"
+ ,cur_read,need_read);
+ }
+ }
+ if (clear > 0){
+ memset ((char*)page+PAGE_SIZE-clear,0,clear);
+ }
+ LOG_FS("DMSDOS: file_mmap_nopage: end\n");
+ return page;
+}
+
+struct vm_operations_struct dblspace_file_mmap = {
+ NULL, /* open */
+ NULL, /* close */
+ NULL, /* unmap */
+ NULL, /* protect */
+ NULL, /* sync */
+ NULL, /* advise */
+ dblspace_file_mmap_nopage, /* nopage */
+ NULL, /* wppage */
+ NULL, /* swapout */
+ NULL, /* swapin */
+};
+
+/*
+ * This is used for a general mmap of a dmsdos file
+ * Returns 0 if ok, or a negative error code if not.
+ */
+int dblspace_mmap(struct file*file,struct vm_area_struct*vma)
+{ struct inode *inode = file->f_dentry->d_inode;
+ LOG_FS("DMSDOS: mmap ino=%ld\n",inode->i_ino);
+ if (vma->vm_flags & VM_SHARED) /* only PAGE_COW or read-only supported now */
+ return -EINVAL;
+ if (vma->vm_offset & (inode->i_sb->s_blocksize - 1))
+ return -EINVAL;
+ if (!inode->i_sb || !S_ISREG(inode->i_mode))
+ return -EACCES;
+ if (!IS_RDONLY(inode)) {
+ inode->i_atime = CURRENT_TIME;
+ /*inode->i_dirt = 1;*/
+ mark_inode_dirty(inode);
+ }
+
+ vma->vm_dentry = dget(file->f_dentry);
+ vma->vm_ops = &dblspace_file_mmap;
+ return 0;
+}
+
+#endif /* from: else / ifdef DMSDOS_USE_READPAGE */
+
diff --git a/src/dblspace_interface.c b/src/dblspace_interface.c
new file mode 100644
index 0000000..39aade8
--- /dev/null
+++ b/src/dblspace_interface.c
@@ -0,0 +1,1147 @@
+/*
+dblspace_interface.c
+
+DMSDOS CVF-FAT module: high-level interface functions.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+# include <linux/fs.h>
+# include <linux/blkdev.h>
+# include <linux/msdos_fs.h>
+# include <linux/msdos_fs_sb.h>
+# include <linux/fat_cvf.h>
+# include <linux/string.h>
+# include <linux/malloc.h>
+# include <asm/semaphore.h>
+# include <linux/module.h>
+#endif
+
+#include"dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+# include"lib_interface.h"
+# include<string.h>
+# include<malloc.h>
+# define MAJOR(x) 0
+# define MINOR(x) 0
+ extern long int blk_size[1][1];
+#endif
+
+extern Acache mdfat[];
+extern Acache dfat[];
+extern Acache bitfat[];
+
+unsigned long loglevel=DEFAULT_LOGLEVEL;
+unsigned long dmsdos_speedup=DEFAULT_SPEEDUP;
+
+/* evaluate numbers from options */
+char* read_number(char*p, unsigned long* n, int*error)
+{ *n=0;
+ *error=-1;
+ if(*p=='b'||*p=='B'||*p=='%')
+ { /*binary*/
+ ++p;
+ while(*p=='0'||*p=='1')
+ { (*n)*=2;
+ if(*p=='1')++(*n);
+ ++p;
+ *error=0;
+ }
+ }
+ else if(*p=='0'&&(*(p+1)=='x'||*(p+1)=='X'))
+ { /*hexadecimal*/
+ p+=2;
+ while((*p>='0'&&*p<='9')||(*p>='a'&&*p<='f')||(*p>='A'&&*p<='F'))
+ { (*n)*=16;
+ (*n)+=((*p<='9')?(*p):(*p)-'a'+10)&0xf;
+ ++p;
+ *error=0;
+ }
+ }
+ else if(*p=='0'||*p=='O'||*p=='o')
+ { /*octal*/
+ ++p;
+ while(*p>='0'&&*p<='8')
+ { (*n)*=8;
+ (*n)+=(*p)-'0';
+ ++p;
+ *error=0;
+ }
+ }
+ else
+ { /*decimal*/
+ while(*p>='0'&&*p<='9')
+ { (*n)*=10;
+ (*n)+=(*p)-'0';
+ ++p;
+ *error=0;
+ }
+ }
+ LOG_REST("DMSDOS: read_number: n=%lu=0x%lx error=%d\n",*n,*n,*error);
+ return p;
+}
+
+/* evaluates a single option (needn't be '\0' terminated) */
+int evaluate_option(char*option,Dblsb*dblsb,int*repair)
+{ int ret=0;
+
+ LOG_REST("DMSDOS: evaluate option: %s\n",option);
+ if(strncmp(option,"comp=",5)==0||strncmp(option,"comp:",5)==0)
+ { if(strncmp(option+5,"no",2)==0)dblsb->s_comp=UNCOMPRESSED;
+ /*else if(strncmp(option+5,"ro",2)==0)*comp=READ_ONLY;*/
+ else if(strncmp(option+5,"ds00",4)==0)dblsb->s_comp=DS_0_0;
+ else if(strncmp(option+5,"ds01",4)==0)dblsb->s_comp=DS_0_1;
+ else if(strncmp(option+5,"ds02",4)==0)dblsb->s_comp=DS_0_2;
+ else if(strncmp(option+5,"jm00",4)==0)dblsb->s_comp=JM_0_0;
+ else if(strncmp(option+5,"jm01",4)==0)dblsb->s_comp=JM_0_1;
+ else if(strncmp(option+5,"sq00",4)==0)dblsb->s_comp=SQ_0_0;
+ else if(strncmp(option+5,"sd3",3)==0)dblsb->s_comp=SD_3;
+ else if(strncmp(option+5,"sd4",3)==0)dblsb->s_comp=SD_4;
+ else if(strncmp(option+5,"guess",5)==0)dblsb->s_comp=GUESS;
+ else ret=-1;
+ }
+ else if(strncmp(option,"cf=",3)==0||strncmp(option,"cf:",3)==0)
+ { if(option[3]=='1'&&option[4]>='0'&&option[4]<='2')
+ dblsb->s_cfaktor=option[4]-'0'+9;
+ else if(option[3]>='1'&&option[3]<='9')
+ dblsb->s_cfaktor=option[3]-'0'-1;
+ else ret=-1;
+ }
+ else if(strncmp(option,"loglevel=",9)==0||strncmp(option,"loglevel:",9)==0)
+ { /* must be decimal or hexadecimal (0x preceeded) number */
+ read_number(option+9,&loglevel,&ret);
+ if(ret>=0)
+ LOG_REST("DMSDOS: evaluate_option: loglevel set to 0x%lx.\n",loglevel);
+ }
+ else if(strncmp(option,"speedup=",8)==0||strncmp(option,"speedup:",8)==0)
+ { /* must be decimal or hexadecimal (0x preceeded) number */
+ read_number(option+8,&dmsdos_speedup,&ret);
+ if(ret>=0)
+ LOG_REST("DMSDOS: evaluate_option: speedup set to 0x%lx.\n",dmsdos_speedup);
+ }
+ else if(strncmp(option,"bitfaterrs=",11)==0||strncmp(option,"bitfaterrs:",11)==0)
+ { if(strncmp(option+11,"repair",6)==0)*repair=1;
+ else if(strncmp(option+11,"ignore",6)==0)*repair=2;
+ else if(strncmp(option+11,"setro",5)==0)*repair=0;
+ else if(strncmp(option+11,"nocheck",7)==0)*repair=-1;
+ else ret=-1;
+ }
+ else
+ { printk(KERN_ERR "DMSDOS: unknown option %s, rejected\n",option);
+ ret=-1;
+ }
+ return ret;
+}
+
+int parse_dmsdos_options(char*options,Dblsb*dblsb,int*repair)
+{ if(options==NULL)return 0;
+
+ while(*options)
+ { if(evaluate_option(options,dblsb,repair)<0)return -1;
+ while(*options!='\0'&&*options!='.'&&*options!='+')++options;
+ while(*options=='.'||*options=='+')++options;
+ }
+ return 0;
+}
+
+int ilog2(int arg)
+{ /* integer log2 */
+ int i=0;
+
+ if(arg<=0)return -1;
+
+ while(arg>>=1)++i;
+ return i;
+}
+
+#ifndef __DMSDOS_LIB__
+void do_spc_init(void)
+{ /* first call of DMSDOS filesystem, initialising variables */
+ int i;
+
+ printk(KERN_NOTICE "DMSDOS CVF-FAT extension version %d.%d.%d" DMSDOS_VLT
+ " compiled " __DATE__ " " __TIME__ " with options:"
+#ifndef DBL_WRITEACCESS
+ " read-only"
+#else
+ " read-write"
+#endif
+#ifdef USE_XMALLOC
+ ", xmalloc"
+#else
+#ifdef USE_VMALLOC
+ ", vmalloc"
+#else
+ ", kmalloc"
+#endif
+#endif
+#ifdef DMSDOS_USE_READPAGE
+ ", readpage"
+#endif
+#ifdef USE_READA_LIST
+ ", reada list"
+#endif
+#ifdef INTERNAL_DAEMON
+ ", internal daemon"
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ ", doublespace/drivespace(<3)"
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ ", drivespace 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC3
+ ", stacker 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ ", stacker 4"
+#endif
+ "\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL);
+
+ /* init cluster cache */
+ ccache_init();
+
+/* no this is done by mount ... and removed by unmount
+ otherwise the module cannot be unloaded again if the
+ internal daemon is running
+ init_daemon();
+*/
+
+#ifdef USE_READA_LIST
+ init_reada_list();
+#endif
+
+ for(i=0;i<MDFATCACHESIZE;++i)
+ { mdfat[i].a_time=0;
+ mdfat[i].a_acc=0;
+ mdfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<DFATCACHESIZE;++i)
+ { dfat[i].a_time=0;
+ dfat[i].a_acc=0;
+ dfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<BITFATCACHESIZE;++i)
+ { bitfat[i].a_time=0;
+ bitfat[i].a_acc=0;
+ bitfat[i].a_buffer=NULL;
+ }
+}
+
+void do_spc_exit(void)
+{
+ force_exit_daemon();
+}
+#endif
+
+#ifdef DMSDOS_CONFIG_DBL
+int detect_dblspace(struct super_block*sb)
+{ struct buffer_head*bh;
+
+ MOD_INC_USE_COUNT;
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ if(strncmp(bh->b_data+3,"MSDBL6.0",8)==0
+ ||strncmp(bh->b_data+3,"MSDSP6.0",8)==0)
+ { raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 1;
+ }
+ raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* setup fresh dblsb structure */
+Dblsb* malloc_dblsb(void)
+{ Dblsb*dblsb;
+
+ dblsb=kmalloc(sizeof(Dblsb),GFP_KERNEL);
+ if(dblsb==NULL)return NULL;
+ dblsb->mdfat_alloc_semp=NULL;
+
+ return dblsb;
+}
+
+/* ensure all memory is released */
+void free_dblsb(Dblsb*dblsb)
+{ if(dblsb==NULL)return;
+ if(dblsb->mdfat_alloc_semp)
+ { kfree(dblsb->mdfat_alloc_semp);
+ dblsb->mdfat_alloc_semp=NULL;
+ }
+ kfree(dblsb);
+}
+
+int mount_dblspace(struct super_block*sb,char*options)
+{ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int i,mdfatb,fatb;
+ unsigned int version_flag;
+ unsigned char * pp;
+ Dblsb* dblsb;
+ int repair=0;
+ int mdrc,m_sector=0;
+
+ MOD_INC_USE_COUNT;
+ LOG_REST("DMSDOS: dblspace/drvspace module mounting...\n");
+
+ dblsb=malloc_dblsb();
+ if(dblsb==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_dblspace: out of memory\n");
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ MSDOS_SB(sb)->private_data=dblsb;
+
+#ifdef __KERNEL__
+ { struct semaphore* sem;
+
+ sem=kmalloc(sizeof(struct semaphore),GFP_KERNEL);
+ if(sem==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_dblspace: out of memory\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ init_MUTEX(sem);
+ dblsb->mdfat_alloc_semp=sem;
+ }
+#endif
+
+ dblsb->s_comp=GUESS;
+ dblsb->s_cfaktor=DEFAULT_CF;
+
+ if(parse_dmsdos_options(options,dblsb,&repair))
+ {
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ dblsb->s_dataend=blk_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]*2;
+
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ if(strncmp(bh->b_data+3,"MSDBL6.0",8)&&strncmp(bh->b_data+3,"MSDSP6.0",8))
+ { printk(KERN_ERR "DMSDOS: MSDBL/MSDSP signature not found, CVF skipped\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ if(sb->s_flags & MS_RDONLY)dblsb->s_comp=READ_ONLY;
+ printk(KERN_INFO "DMSDOS: mounting CVF on device 0x%x %s...\n",
+ sb->s_dev,
+ dblsb->s_comp==READ_ONLY?"read-only":"read-write");
+
+ /* dblspace correction was relocated. Pavel */
+ dblsb->s_dataend-=1;
+
+ pp=&(bh->b_data[45]);
+ dblsb->s_dcluster=CHS(pp);
+ if(dblsb->s_dcluster&0x8000)dblsb->s_dcluster|=0xffff0000;
+ pp=&(bh->b_data[36]);
+ dblsb->s_mdfatstart=CHS(pp)+1;
+ pp=&(bh->b_data[17]);
+ dblsb->s_rootdirentries=CHS(pp);
+ dblsb->s_sectperclust=((unsigned long)(bh->b_data[13]));
+ dblsb->s_spc_bits=ilog2(dblsb->s_sectperclust);
+ pp=&(bh->b_data[39]);i=CHS(pp);/*i=res0*/
+ dblsb->s_bootblock=i;
+ pp=&(bh->b_data[14]);
+ dblsb->s_fatstart=i+CHS(pp);
+ pp=&(bh->b_data[41]);
+ dblsb->s_rootdir=i+CHS(pp);
+ pp=&(bh->b_data[43]);
+ dblsb->s_datastart=i+CHS(pp)+2;
+ dblsb->s_2nd_fat_offset=0; /* dblsp doesn't have a second fat */
+ dblsb->s_cvf_version=DBLSP;
+ version_flag=bh->b_data[51];
+ if(version_flag==2)dblsb->s_cvf_version=DRVSP;
+ if(version_flag==3||dblsb->s_sectperclust>16)dblsb->s_cvf_version=DRVSP3;
+ if(version_flag>3)printk(KERN_WARNING "DMSDOS: strange version flag %d, assuming 0.\n",
+ version_flag);
+
+#ifndef DMSDOS_CONFIG_DBLSP_DRVSP
+ if(dblsb->s_cvf_version<=DRVSP)
+ { printk(KERN_ERR "DMSDOS: support for doublespace/drivespace(<3) not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+#ifndef DMSDOS_CONFIG_DRVSP3
+ if(dblsb->s_cvf_version==DRVSP3)
+ { printk(KERN_ERR "DMSDOS: support for drivespace 3 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read emulated boot block\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ pp=&(bh2->b_data[57]);
+ if(CHL(pp)==0x20203631)dblsb->s_16bitfat=1;
+ else if(CHL(pp)==0x20203231)dblsb->s_16bitfat=0;
+ else if(CHL(pp)==0x20203233)
+ { printk(KERN_ERR "DMSDOS: CVF has FAT32 signature, not mounted. Please report this.\n");
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ else
+ { pp=&(bh->b_data[62]);
+ dblsb->s_16bitfat=(CHS(pp)>32) ? 1 : 0;
+ printk(KERN_WARNING "DMSDOS: FAT bit size not recognized, guessed %d bit\n",
+ CHS(pp)>32 ? 16 : 12 );
+ }
+ raw_brelse(sb,bh2);
+
+ /* try to verify correct end of CVF */
+ mdrc=0;
+ for(i=-1;i<=1;++i)
+ { bh2=raw_bread(sb,dblsb->s_dataend+i);
+ if(bh2==NULL)
+ { LOG_REST("DMSDOS: MDR test breaks at i=%d\n",i);
+ break;
+ }
+ if(strcmp(bh2->b_data,"MDR")==0)
+ { ++mdrc;
+ m_sector=dblsb->s_dataend+i;
+ LOG_REST("DMSDOS: MDR signature found at sector %d\n",m_sector);
+ }
+ raw_brelse(sb,bh2);
+ }
+ if(mdrc!=1)
+ printk(KERN_WARNING "DMSDOS: could not find MDR signature or found more than one, mdrc=%d (ignored)\n",
+ mdrc);
+ else
+ { if(dblsb->s_dataend!=m_sector-1)
+ { LOG_REST("DMSDOS: dataend corrected due to MDR signature old=%d new=%d\n",
+ dblsb->s_dataend,m_sector-1);
+ dblsb->s_dataend=m_sector-1;
+ }
+ }
+
+ dblsb->s_full=0;
+
+ /* calculate maximum cluster nr (fixes lost cluster messages) */
+ mdfatb=(dblsb->s_bootblock-dblsb->s_mdfatstart);
+ mdfatb*=((dblsb->s_sectperclust>16)?102:128);
+ mdfatb-=dblsb->s_dcluster;
+ fatb=512*(dblsb->s_rootdir-dblsb->s_fatstart);
+ if(dblsb->s_16bitfat)fatb/=2; else fatb=(2*fatb)/3;
+ dblsb->s_max_cluster=((mdfatb<fatb)?mdfatb:fatb)-1;
+ if(dblsb->s_16bitfat)
+ { if(dblsb->s_max_cluster>0xFFF6)dblsb->s_max_cluster=0xFFF6;
+ }
+ else
+ { if(dblsb->s_max_cluster>0xFF6)dblsb->s_max_cluster=0xFF6;
+ }
+
+ /* adapt max_cluster according to dos' limits */
+ dblsb->s_max_cluster2=dblsb->s_max_cluster;
+ pp=&(bh->b_data[32]);
+ i=CHL(pp);
+ pp=&(bh->b_data[22]);
+ i-=CHS(pp);
+ pp=&(bh->b_data[14]);
+ i-=CHS(pp);
+ i-=dblsb->s_rootdirentries>>4;
+ /*i=(i>>4)+1;*/
+ i=(i/dblsb->s_sectperclust)+1;
+ if(i<=dblsb->s_max_cluster)
+ { dblsb->s_max_cluster=i;
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: dos max_cluster=%d too large, cutting to %d.\n",
+ i,dblsb->s_max_cluster);
+ }
+
+ LOG_REST("DMSDOS: dcluster=%d\n",dblsb->s_dcluster);
+ LOG_REST("DMSDOS: mdfatstart=%d\n",dblsb->s_mdfatstart);
+ LOG_REST("DMSDOS: rootdirentries=%d\n",dblsb->s_rootdirentries);
+ LOG_REST("DMSDOS: sectperclust=%d\n",dblsb->s_sectperclust);
+ LOG_REST("DMSDOS: fatstart=%d\n",dblsb->s_fatstart);
+ LOG_REST("DMSDOS: rootdir=%d\n",dblsb->s_rootdir);
+ LOG_REST("DMSDOS: %d bit FAT\n",dblsb->s_16bitfat ? 16 : 12);
+
+ dblsb->s_lastnear=0;
+ dblsb->s_lastbig=0;
+ dblsb->s_free_sectors=-1; /* -1 means unknown */
+
+ /* error test (counts sectors) */
+ if(repair!=-1) /* repair==-1 means do not even check */
+ {
+ i=simple_check(sb,repair&1);
+ if(i==-1||i==-2)
+ { printk(KERN_WARNING "DMSDOS: CVF has serious errors or compatibility problems, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ if(i==-3)
+ { if(repair&2)
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, ignored.\n");
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ }
+
+ /* if still unknown then count now */
+ if(dblsb->s_free_sectors<0)check_free_sectors(sb);
+
+ /* print doublespace version */
+ if(dblsb->s_cvf_version==DBLSP&&dblsb->s_sectperclust==16)
+ { printk(KERN_INFO "DMSDOS: CVF is in doublespace format (version 1).\n");
+ }
+ else if(dblsb->s_cvf_version==DRVSP&&dblsb->s_sectperclust==16)
+ { printk(KERN_INFO "DMSDOS: CVF is in drivespace format (version 2).\n");
+ }
+ else if(dblsb->s_cvf_version==DRVSP3&&dblsb->s_sectperclust==64)
+ { printk(KERN_INFO "DMSDOS: CVF is in drivespace 3 format.\n");
+ }
+ else
+ { printk(KERN_INFO "DMSDOS: CVF is in unknown (new?) format, please report.\n");
+ printk(KERN_INFO "DMSDOS: version_flag=%d sectperclust=%d\n",version_flag,
+ dblsb->s_sectperclust);
+ printk(KERN_NOTICE "DMSDOS: CVF set to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ raw_brelse(sb,bh);
+
+ /* set some msdos fs important stuff */
+ MSDOS_SB(sb)->dir_start=FAKED_ROOT_DIR_OFFSET;
+ MSDOS_SB(sb)->dir_entries=dblsb->s_rootdirentries;
+ MSDOS_SB(sb)->data_start=FAKED_DATA_START_OFFSET; /*begin of virtual cluster 2*/
+ MSDOS_SB(sb)->clusters=dblsb->s_max_cluster;
+ if(MSDOS_SB(sb)->fat_bits!=dblsb->s_16bitfat?16:12)
+ { LOG_REST("DMSDOS: fat bit size mismatch in fat driver, trying to correct\n");
+ MSDOS_SB(sb)->fat_bits=dblsb->s_16bitfat?16:12;
+ }
+ MSDOS_SB(sb)->cluster_size=dblsb->s_sectperclust;
+ #ifdef HAS_SB_CLUSTER_BITS
+ for(MSDOS_SB(sb)->cluster_bits=0;
+ (1<<MSDOS_SB(sb)->cluster_bits)<MSDOS_SB(sb)->cluster_size;)
+ MSDOS_SB(sb)->cluster_bits++;
+ MSDOS_SB(sb)->cluster_bits+=SECTOR_BITS;
+ #endif
+
+ /* these *must* always match */
+ if(dblsb->s_comp==READ_ONLY)sb->s_flags |= MS_RDONLY;
+
+ /* we allow using the daemon - calling this more than once doesn't matter */
+ init_daemon();
+
+ return 0;
+}
+#endif
+
+int unmount_dblspace(struct super_block*sb)
+{ int j;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ LOG_REST("DMSDOS: CVF on device 0x%x unmounted.\n",sb->s_dev);
+
+ /* discard/write cached clusters */
+ free_ccache_dev(sb);
+ /* the same for the daemon if it is running */
+ clear_list_dev(sb);
+
+#ifdef DMSDOS_CONFIG_STAC
+ /* mark stacker bitfat as up to date and unmounted */
+ if(dblsb->s_cvf_version>=STAC3)
+ stac_bitfat_state(sb,1);
+#endif
+
+ /* kill buffers used by unmounted cvf */
+ for(j=0;j<MDFATCACHESIZE;++j)
+ { if(mdfat[j].a_buffer!=NULL)
+ { if(mdfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,mdfat[j].a_buffer);
+ mdfat[j].a_buffer=NULL;
+ }
+ mdfat[j].a_time=0;
+ mdfat[j].a_acc=0;
+ }
+ }
+ for(j=0;j<DFATCACHESIZE;++j)
+ { if(dfat[j].a_buffer!=NULL)
+ { if(dfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,dfat[j].a_buffer);
+ dfat[j].a_buffer=NULL;
+ }
+ dfat[j].a_time=0;
+ dfat[j].a_acc=0;
+ }
+ }
+ for(j=0;j<BITFATCACHESIZE;++j)
+ { if(bitfat[j].a_buffer!=NULL)
+ { if(bitfat[j].a_sb->s_dev==sb->s_dev)
+ { raw_brelse(sb,bitfat[j].a_buffer);
+ bitfat[j].a_buffer=NULL;
+ }
+ bitfat[j].a_time=0;
+ bitfat[j].a_acc=0;
+ }
+ }
+
+#ifdef __KERNEL__
+#ifdef USE_READA_LIST
+ /* throw away all stacked reada entries for this dev */
+ kill_reada_list_dev(sb->s_dev);
+#endif
+ /* this is unused in the library */
+ /* looks like we don't need this here... */
+ /*kfree(dblsb->mdfat_alloc_semp);*/
+ /*dblsb->mdfat_alloc_semp=NULL;*/
+#endif
+ /*kfree(MSDOS_SB(sb)->private_data);*/
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ /*MSDOS_SB(sb)->cvf_format=NULL;*/ /*this causes a segfault in
+ dec_cvf_format_use_count_by_version*/
+ exit_daemon();
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef DMSDOS_CONFIG_STAC
+int detect_stacker(struct super_block*sb)
+{ struct buffer_head*bh;
+
+ MOD_INC_USE_COUNT;
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block\n");
+ MOD_DEC_USE_COUNT;
+ return 0;
+ }
+ if(strncmp(bh->b_data,"STACKER",7)==0)
+ { raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 1;
+ }
+ raw_brelse(sb,bh);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+int mount_stacker(struct super_block*sb,char*options)
+{
+ struct buffer_head*bh;
+ struct buffer_head*bh2;
+ int i;
+ unsigned char * pp, *p;
+ unsigned char buf[512];
+ unsigned char b,c;
+ int SectSize, ClustSects, ClustSize, ReservSects, FATCnt;
+ int RootDirEnt, TotalSects, FATSize, HidenSects, FirstRootSect;
+ int FirstDataSect, FirstDataSect2, FAT12, FirstFATSect;
+ int StacVersion;
+ /* parameters of virtual DOS drive */
+ int BB_FirstDataSect, BB_ClustCnt, BB_SectSize, BB_TotalSects;
+ Dblsb*dblsb;
+ int repair=0;
+
+ MOD_INC_USE_COUNT;
+ LOG_REST("DMSDOS: stacker 3/4 module mounting...\n");
+
+ dblsb=malloc_dblsb();
+ if(dblsb==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_stacker: out of memory\n");
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ MSDOS_SB(sb)->private_data=dblsb;
+
+#ifdef __KERNEL__
+ { struct semaphore* sem;
+
+ sem=kmalloc(sizeof(struct semaphore),GFP_KERNEL);
+ if(sem==NULL)
+ { printk(KERN_ERR "DMSDOS: mount_stacker: out of memory\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ init_MUTEX(sem);
+ dblsb->mdfat_alloc_semp=sem;
+ }
+#endif
+
+ dblsb->s_comp=GUESS;
+ dblsb->s_cfaktor=DEFAULT_CF;
+
+ if(parse_dmsdos_options(options,dblsb,&repair))
+ {
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ dblsb->s_dataend=blk_size[MAJOR(sb->s_dev)][MINOR(sb->s_dev)]*2;
+
+ LOG_REST("DMSDOS: reading super block...\n");
+ bh=raw_bread(sb,0);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read super block of CVF\n");
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ LOG_REST("DMSDOS: super block read finished\n");
+ pp=&(bh->b_data[0]);
+ if(strncmp(pp,"STACKER",7)!=0)
+ { printk(KERN_ERR "DMSDOS: STACKER signature not found\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ /* copy block (must not directly modify kernel buffer!!!) */
+ memcpy(buf,bh->b_data,SECTOR_SIZE);
+ /* decode super block */
+ for(i=0x30,p=buf+0x50,b=buf[0x4c];i--;p++)
+ { b=0xc4-b;
+ b=b<0x80?b*2:b*2+1;
+ b^=c=*p;
+ *p=b;b=c;
+ }
+ if(buf[0x4e]!=0xa||buf[0x4f]!=0x1a)
+ { printk(KERN_ERR "DMSDOS: Stacker 0x1A0A signature not found\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+
+ if(sb->s_flags & MS_RDONLY)dblsb->s_comp=READ_ONLY;
+ printk(KERN_NOTICE "DMSDOS: mounting CVF on device 0x%x %s...\n",
+ sb->s_dev,
+ dblsb->s_comp==READ_ONLY?"read-only":"read-write");
+
+ /* extract important info */
+ pp=&(buf[0x6C]);
+ TotalSects=CHL(pp);
+ pp=&(buf[0x70]);
+ dblsb->s_bootblock=CHS(pp);
+ pp=&(buf[0x74]);
+ dblsb->s_mdfatstart=CHS(pp); /* here it's AMAP start !!! */
+ pp=&(buf[0x76]);
+ FirstFATSect=dblsb->s_fatstart=CHS(pp);
+ pp=&(buf[0x7a]);
+ FirstDataSect2=dblsb->s_datastart=CHS(pp);
+ pp=&(buf[0x60]);
+ StacVersion=CHS(pp);
+ if(StacVersion>=410)dblsb->s_cvf_version=STAC4;
+ else dblsb->s_cvf_version=STAC3;
+ /* if(buf[0x64]==9)dblsb->s_cvf_version=STAC4;
+ else dblsb->s_cvf_version=STAC3; */
+
+#ifndef DMSDOS_CONFIG_STAC3
+ if(dblsb->s_cvf_version==STAC3)
+ { printk(KERN_ERR "DMSDOS: support for stacker 3 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+#ifndef DMSDOS_CONFIG_STAC4
+ if(dblsb->s_cvf_version==STAC4)
+ { printk(KERN_ERR "DMSDOS: support for stacker 4 not compiled in.\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+#endif
+
+ /* now we need the boot block */
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read emulated boot block of CVF\n");
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ /* read values */
+ dblsb->s_sectperclust=bh2->b_data[0xd];
+ dblsb->s_spc_bits=ilog2(dblsb->s_sectperclust);
+ pp=&(bh2->b_data[0x11]);
+ dblsb->s_rootdirentries=CHS(pp);
+
+ pp=&(buf[0x62]); SectSize=CHS(pp);
+ pp=&(bh2->b_data[0xB]); BB_SectSize=CHS(pp);
+ if(SectSize!=SECTOR_SIZE||BB_SectSize!=SECTOR_SIZE)
+ printk(KERN_WARNING "DMSDOS: Stacker sector size not 512 bytes, hmm...\n");
+ ClustSects=bh2->b_data[0xD];
+ ClustSize=ClustSects*SectSize;
+ pp=&(bh2->b_data[0xE]); ReservSects=CHS(pp);
+ FATCnt=bh2->b_data[0x10];
+ pp=&(bh2->b_data[0x11]); RootDirEnt=CHS(pp);
+ pp=&(bh2->b_data[0x13]); BB_TotalSects=CHS(pp);
+ if(!BB_TotalSects)
+ { pp=&(bh2->b_data[0x20]); BB_TotalSects=CHL(pp);};
+ pp=&(bh2->b_data[0x16]); FATSize=CHS(pp);
+ pp=&(bh2->b_data[0x1B]); HidenSects=CHS(pp);
+ if(BB_SectSize!=SectSize)printk(KERN_WARNING "DMSDOS: Inconsistent sector length\n");
+ FirstRootSect=FirstFATSect+3*FATCnt*FATSize;
+
+ dblsb->s_2nd_fat_offset=3*(FATCnt-1)*FATSize;
+
+ /* Number of sectors in root directory */
+ FirstDataSect=((long)RootDirEnt*0x20+SectSize-1)/SectSize;
+ /* Emulated data start sector for DOS */
+ BB_FirstDataSect=FirstDataSect+FATCnt*FATSize+ReservSects;
+ /* ??? +HidenSects; */
+ /* Real data start sector */
+ FirstDataSect+=FirstRootSect;
+ /* Counting BB_ClustCnt from emulated boot block */
+ BB_ClustCnt=(BB_TotalSects-BB_FirstDataSect)/ClustSects;
+ if(BB_ClustCnt>=0xFED)FAT12=0; else FAT12=1;
+ if(BB_ClustCnt<2||BB_ClustCnt>0xfff7)
+ { printk(KERN_ERR "DMSDOS: BB_ClustCnt=0x%x impossible (FAT32?)\n",BB_ClustCnt);
+ raw_brelse(sb,bh2);
+ raw_brelse(sb,bh);
+ free_dblsb(dblsb);
+ MSDOS_SB(sb)->private_data=NULL;
+ MOD_DEC_USE_COUNT;
+ return -1;
+ }
+ if(FirstDataSect2!=FirstDataSect)
+ { printk(KERN_WARNING "DMSDOS: Inconsistent first data sector number. Mounting READ ONLY.\n");
+ printk(KERN_WARNING "In header found %u but computed %u\n",(unsigned)FirstDataSect2,(unsigned)FirstDataSect);
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ LOG_REST("DMSDOS: Stac version %u start of FAT %u, root %u, data %u; FATSize %u; FATCnt %u; clusts %u; sects %u\n",
+ (unsigned)StacVersion,(unsigned)FirstFATSect,(unsigned)FirstRootSect,
+ (unsigned)FirstDataSect,(unsigned)FATSize,(unsigned)FATCnt,
+ (unsigned)BB_ClustCnt,(unsigned)BB_TotalSects);
+
+ /* try dos standard method to detect fat bit size - does not work */
+ /* pp=&(bh2->b_data[57]); */
+ /* if(CHL(pp)==0x20203631)dblsb->s_16bitfat=1; */
+ /* else if(CHL(pp)==0x20203231)dblsb->s_16bitfat=0; else */
+
+ /* used only stacker method for fat entry size now */
+ dblsb->s_16bitfat=FAT12? 0: 1;
+ LOG_REST("DMSDOS: FAT bit size of CVF is %d bit\n",
+ (FAT12) ? 12 : 16 );
+
+ /* check if clusters fits in FAT */
+ if(BB_ClustCnt+2>(FAT12?(SECTOR_SIZE*FATSize*2)/3:(SECTOR_SIZE*FATSize)/2))
+ { printk(KERN_WARNING "DMSDOS: FAT size does not match cluster count. Mounting READ ONLY.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+
+ /* check size of physical media against stacvol parameters */
+ if((TotalSects<=0)||(TotalSects-1)>dblsb->s_dataend)
+ { printk(KERN_WARNING "DMSDOS: CVF is shorter about %d sectors. Mounting READ ONLY.\n",
+ (int)TotalSects-1-dblsb->s_dataend);
+ dblsb->s_comp=READ_ONLY;
+ }
+ else if((TotalSects-1)<dblsb->s_dataend)
+ { printk(KERN_INFO "DMSDOS: CVF end padding %d sectors.\n",
+ (int)dblsb->s_dataend-TotalSects+1);
+ dblsb->s_dataend=TotalSects-1;
+ }
+
+ raw_brelse(sb,bh2);
+ dblsb->s_full=0;
+ raw_brelse(sb,bh);
+
+ dblsb->s_rootdir=FirstRootSect;
+ dblsb->s_max_cluster=dblsb->s_max_cluster2=BB_ClustCnt+1;
+
+ LOG_REST("DMSDOS: mdfatstart=%d\n",dblsb->s_mdfatstart);
+ LOG_REST("DMSDOS: rootdirentries=%d\n",dblsb->s_rootdirentries);
+ LOG_REST("DMSDOS: sectperclust=%d\n",dblsb->s_sectperclust);
+ LOG_REST("DMSDOS: fatstart=%d\n",dblsb->s_fatstart);
+ LOG_REST("DMSDOS: rootdir=%d\n",dblsb->s_rootdir);
+ LOG_REST("DMSDOS: %d bit FAT\n",dblsb->s_16bitfat ? 16 : 12);
+
+ /* allocation informations */
+ dblsb->s_lastnear=0;
+ dblsb->s_lastbig=0;
+ dblsb->s_free_sectors=-1; /* -1 means unknown */
+
+ /* set some msdos fs important stuff */
+ MSDOS_SB(sb)->dir_start=FAKED_ROOT_DIR_OFFSET;
+ MSDOS_SB(sb)->dir_entries=dblsb->s_rootdirentries;
+ MSDOS_SB(sb)->data_start=FAKED_DATA_START_OFFSET; /*begin of virtual cluster 2*/
+ MSDOS_SB(sb)->clusters=BB_ClustCnt;
+ if(MSDOS_SB(sb)->fat_bits!=dblsb->s_16bitfat?16:12)
+ { LOG_REST("DMSDOS: fat bit size mismatch in fat driver, trying to correct\n");
+ MSDOS_SB(sb)->fat_bits=dblsb->s_16bitfat?16:12;
+ }
+ MSDOS_SB(sb)->cluster_size=dblsb->s_sectperclust;
+ #ifdef HAS_SB_CLUSTER_BITS
+ for(MSDOS_SB(sb)->cluster_bits=0;
+ (1<<MSDOS_SB(sb)->cluster_bits)<MSDOS_SB(sb)->cluster_size;)
+ MSDOS_SB(sb)->cluster_bits++;
+ MSDOS_SB(sb)->cluster_bits+=SECTOR_BITS;
+ #endif
+
+ /* error test */
+ if(repair!=-1) /* repair==-1 means do not even check */
+ {
+ i=simple_check(sb,repair&1);
+ if(i==-1||i==-2)
+ { printk(KERN_WARNING "DMSDOS: CVF has serious errors or compatibility problems, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ if(i==-3)
+ { if(repair&2)
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, ignored.\n");
+ }
+ else
+ { printk(KERN_WARNING "DMSDOS: CVF has bitfat mismatches, setting to read-only.\n");
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ }
+
+ /* print stacker version */
+ if(dblsb->s_cvf_version==STAC3)
+ { printk(KERN_NOTICE "DMSDOS: CVF is in stacker 3 format.\n");
+ }
+ else if(dblsb->s_cvf_version==STAC4)
+ { printk(KERN_NOTICE "DMSDOS: CVF is in stacker 4 format.\n");
+ }
+
+ /* if still unknown then count now */
+ if(dblsb->s_free_sectors<0)check_free_sectors(sb);
+
+ /* these *must* always match */
+ if(dblsb->s_comp==READ_ONLY)sb->s_flags |= MS_RDONLY;
+
+ /* mark stacker bitfat as mounted and changing */
+ /* if not regulary unmounted, it must be repaired before */
+ /* next write access */
+ if((sb->s_flags&MS_RDONLY)==0)stac_bitfat_state(sb,2);
+
+ /* we allow using the daemon - calling this more than once doesn't matter */
+ init_daemon();
+
+ return 0;
+}
+#endif
+
+#ifdef DMSDOS_USE_READPAGE
+#define READPAGE dblspace_readpage
+#define MMAP NULL
+#define RMFLAG CVF_USE_READPAGE
+#else
+#define READPAGE NULL
+#define MMAP dblspace_mmap
+#define RMFLAG 0
+#endif
+
+#ifndef __DMSDOS_LIB__
+#ifdef DMSDOS_CONFIG_DBL
+struct cvf_format dblspace_format = {
+ 0x0001, /* version id */
+ "dblspace", /* version text */
+ RMFLAG, /* flags */
+ detect_dblspace, /* detect */
+ mount_dblspace, /* mount */
+ unmount_dblspace, /* unmount */
+ dblspace_bread, /* bread */
+ dblspace_getblk, /* getblk */
+ dblspace_brelse, /* brelse */
+ dblspace_mark_buffer_dirty, /* mark_buffer_dirty */
+ dblspace_set_uptodate, /* set_uptodate */
+ dblspace_is_uptodate, /* is_uptodate */
+ dblspace_ll_rw_block, /* ll_rw_block */
+ dblspace_fat_access, /* fat_access */
+ NULL, /* statfs */
+ dblspace_bmap, /* bmap */
+ #ifndef __FOR_KERNEL_2_3_10
+ dblspace_smap, /* smap */
+ #endif
+ dblspace_file_read, /* file_read */
+ dblspace_file_write, /* file_write */
+ MMAP, /* mmap */
+ READPAGE, /* readpage */
+ NULL, /* writepage */
+ dmsdos_ioctl_dir, /* dir ioctl */
+ dblspace_zero_new_cluster /* zero_new_cluster */
+};
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+struct cvf_format stacker_format = {
+ 0x0002, /* version id */ /**** only ****/
+ "stacker", /* version text */ /**** these ****/
+ RMFLAG, /* flags */
+ detect_stacker, /* detect */ /**** four ****/
+ mount_stacker, /* mount */ /**** differ :) ****/
+ unmount_dblspace, /* unmount */
+ dblspace_bread, /* bread */
+ dblspace_getblk, /* getblk */
+ dblspace_brelse, /* brelse */
+ dblspace_mark_buffer_dirty, /* mark_buffer_dirty */
+ dblspace_set_uptodate, /* set_uptodate */
+ dblspace_is_uptodate, /* is_uptodate */
+ dblspace_ll_rw_block, /* ll_rw_block */
+ dblspace_fat_access, /* fat_access */
+ NULL, /* statfs */
+ dblspace_bmap, /* bmap */
+ #ifndef __FOR_KERNEL_2_3_10
+ dblspace_smap, /* smap */
+ #endif
+ dblspace_file_read, /* file_read */
+ dblspace_file_write, /* file_write */
+ MMAP, /* mmap */
+ READPAGE, /* readpage */
+ NULL, /* writepage */
+ dmsdos_ioctl_dir, /* dir ioctl */
+ dblspace_zero_new_cluster /* zero_new_cluster */
+};
+#endif
+
+int init_dmsdos(void)
+{ int i;
+
+ do_spc_init();
+#ifdef DMSDOS_CONFIG_DBL
+ i=register_cvf_format(&dblspace_format);
+ if(i<0)
+ { printk(KERN_ERR "register_cvf_format failed, dmsdos not loaded successfully\n");
+ do_spc_exit();
+ return i;
+ }
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ i=register_cvf_format(&stacker_format);
+ if(i<0)
+ { printk(KERN_ERR "register_cvf_format failed, dmsdos not loaded successfully\n");
+ do_spc_exit();
+#ifdef DMSDOS_CONFIG_DBL
+ unregister_cvf_format(&dblspace_format);
+#endif
+ return i;
+ }
+#endif
+ LOG_REST("CVF format(s) successfully registered\n");
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{ return init_dmsdos();
+}
+
+void cleanup_module(void)
+{ do_spc_exit();
+#ifdef DMSDOS_CONFIG_DBL
+ unregister_cvf_format(&dblspace_format);
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ unregister_cvf_format(&stacker_format);
+#endif
+}
+#endif /* MODULE */
+#endif /* ifndef __DMSDOS_LIB__ */
+
+char seq[]="000000";
+
+#ifdef __DMSDOS_LIB__
+/* we don't need locking in the library */
+void lock_prseq(void) {}
+void unlock_prseq(void) {}
+#else
+DECLARE_MUTEX(prseq_sem); /* Must be initialized to green light */
+void lock_prseq(void) {down(&prseq_sem);}
+void unlock_prseq(void) {up(&prseq_sem);}
+#endif
+
+int log_prseq(void)
+{ int i;
+
+ lock_prseq();
+
+ i=5;
+ while(i>=0)
+ { ++seq[i];
+ if(seq[i]<='9')break;
+ seq[i]='0';
+ --i;
+ }
+
+ printk(seq);
+
+ unlock_prseq();
+
+ return 1;
+}
diff --git a/src/dblspace_ioctl.c b/src/dblspace_ioctl.c
new file mode 100644
index 0000000..6eecc28
--- /dev/null
+++ b/src/dblspace_ioctl.c
@@ -0,0 +1,971 @@
+/*
+dblspace_ioctl.c
+
+DMSDOS CVF-FAT module: ioctl functions (interface for external utilities).
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifndef __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <asm/segment.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/mm.h>
+#include <asm/semaphore.h>
+#include "dmsdos.h"
+
+#ifdef INTERNAL_DAEMON
+int idmsdosd(void*);
+#define __KERNEL_SYSCALLS__
+#include <linux/unistd.h>
+#endif
+
+extern unsigned long dmsdos_speedup;
+
+int daemon_present=0;
+int must_maintain_list=0;
+int listcount=0;
+Rwlist rwlist[LISTSIZE];
+int rlist=0;
+int wlist=0;
+
+#ifdef INTERNAL_DAEMON
+DECLARE_WAIT_QUEUE_HEAD(daemon_wait);
+DECLARE_WAIT_QUEUE_HEAD(daemon_exit_wait);
+int daemon_go_home=0;
+int internal_daemon_counter=0;
+#else
+int daemon_pid;
+int daemon_task_nr;
+#endif
+
+void dumpcache(void);
+void dump_ccache(void);
+
+DECLARE_MUTEX(listaccess_sem); /* Must be initialized to green light */
+void lock_listaccess(void) {down(&listaccess_sem);}
+void unlock_listaccess(void) {up(&listaccess_sem);}
+
+void log_statistics(void)
+{ log_list_statistics();
+ log_ccache_statistics();
+ log_found_statistics();
+ /*log_other_statistics();*/
+}
+
+#undef DEPRECATED_CODE
+#ifdef DEPRECATED_CODE
+int set_maxcluster(struct super_block*sb,int data)
+{ struct buffer_head*bh,*bh2;
+ int i;
+ unsigned char*pp;
+ unsigned long int sectors;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(dblsb->s_cvf_version>=DRVSP3)
+ { printk(KERN_ERR "DMSDOS: set_maxcluster refused - CVF is not in doublespace or drivespace<=2 format.\n");
+ return -EINVAL;
+ }
+ if(data<2||data>dblsb->s_max_cluster2)return -EINVAL;
+ /* check if higher clusters are unused */
+ for(i=dblsb->s_max_cluster2;i>data;--i)
+ { if(dbl_fat_nextcluster(sb,i,NULL))
+ { printk(KERN_ERR "DMSDOS: set_maxcluster %d refused: cluster %d in use\n",
+ data,i);
+ return -EINVAL;
+ }
+ }
+
+ bh=raw_bread(sb,0);
+ if(bh==NULL)return -EIO;
+ bh2=raw_bread(sb,dblsb->s_bootblock);
+ if(bh2==NULL)
+ { raw_brelse(sb,bh);
+ return -EIO;
+ }
+
+ /* calculate number of sectors */
+ /*sectors=(data-1)<<4;*/
+ sectors=(data-1)*dblsb->s_sectperclust;
+ sectors+=dblsb->s_rootdirentries>>4;
+ pp=&(bh->b_data[14]);
+ sectors+=CHS(pp);
+ pp=&(bh->b_data[22]);
+ sectors+=CHS(pp);
+ bh->b_data[32]=sectors;
+ bh->b_data[33]=sectors>>8;
+ bh->b_data[34]=sectors>>16;
+ bh->b_data[35]=sectors>>24;
+ sectors+=CHS(pp);
+ bh2->b_data[32]=sectors;
+ bh2->b_data[33]=sectors>>8;
+ bh2->b_data[34]=sectors>>16;
+ bh2->b_data[35]=sectors>>24;
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh2,1);
+ raw_brelse(sb,bh);
+ raw_brelse(sb,bh2);
+
+ dblsb->s_max_cluster=data;
+ MSDOS_SB(sb)->clusters=data;
+ MSDOS_SB(sb)->free_clusters=-1;
+
+ return 0;
+}
+#endif
+
+void dmsdos_extra_statfs(struct super_block*sb, Dblstat*dblstat)
+{ int sector,size,cluster;
+ Mdfat_entry mde;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ dblstat->free_sectors=0;
+ dblstat->max_hole=0;
+ for(sector=dblsb->s_datastart;sector<dblsb->s_dataend;
+ ++sector)
+ { if(dbl_bitfat_value(sb,sector,NULL)==0)
+ { ++(dblstat->free_sectors);
+ size=1;
+ while(dbl_bitfat_value(sb,sector+size,NULL)==0)++size;
+ if(size>dblstat->max_hole)dblstat->max_hole=size;
+ sector+=size-1;
+ dblstat->free_sectors+=size-1;
+ }
+ }
+ dblstat->used_sectors=dblsb->s_dataend+1-dblsb->s_datastart
+ -dblstat->free_sectors;
+ dblstat->sectors_lo=0;
+ dblstat->sectors_hi=0;
+ dblstat->compressed_clusters=0;
+ dblstat->uncompressed_clusters=0;
+ dblstat->free_clusters=0;
+ dblstat->used_clusters=0;
+ dblstat->lost_clusters=0;
+ for(cluster=2;cluster<=dblsb->s_max_cluster;++cluster)
+ { if(dbl_fat_nextcluster(sb,cluster,NULL)!=0)
+ { dbl_mdfat_value(sb,cluster,NULL,&mde);
+ if(mde.flags&2)
+ { ++(dblstat->used_clusters);
+ if(mde.flags&1)++(dblstat->uncompressed_clusters);
+ else ++(dblstat->compressed_clusters);
+ dblstat->sectors_hi+=mde.size_hi_minus_1+1;
+ dblstat->sectors_lo+=mde.size_lo_minus_1+1;
+ }
+ else ++(dblstat->lost_clusters);
+ }
+ else ++(dblstat->free_clusters);
+ }
+}
+
+int dmsdos_ioctl_dir(
+ struct inode *dir,
+ struct file *filp,
+ unsigned int cmd,
+ unsigned long data)
+{ unsigned char* idata;
+ struct buffer_head*bh;
+ int newval;
+ int val;
+ int cluster;
+ int sector;
+ Dblstat dblstat;
+ Mdfat_entry mde,dummy;
+ unsigned char *clusterd;
+ int membytes;
+ struct super_block*sb=dir->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ long lval;
+#ifndef INTERNAL_DAEMON
+ int length;
+ int i;
+ int plist;
+ /*int sectors;*/
+ int rawlength;
+#endif
+
+ idata=(unsigned char*)data;
+
+ switch(cmd)
+ { case DMSDOS_GET_DBLSB:
+ /* check dutil version number */
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*if(get_fs_long(idata)<DMSDOS_LOWEST_COMPATIBLE_VERSION)*/
+ memcpy_fromfs(&lval,idata,sizeof(long));
+ if(lval<DMSDOS_LOWEST_COMPATIBLE_VERSION)
+ return DMSDOS_VERSION|0x0f000000;
+ if(verify_area(VERIFY_WRITE, idata, sizeof(Dblsb)))return -EFAULT;
+ memcpy_tofs(idata,(unsigned char*)dblsb,sizeof(Dblsb));
+ return DMSDOS_VERSION;
+ case DMSDOS_EXTRA_STATFS:
+ if(verify_area(VERIFY_WRITE, idata, sizeof(Dblstat)))return -EFAULT;
+ dmsdos_extra_statfs(dir->i_sb,&dblstat);
+ memcpy_tofs(idata,&dblstat,sizeof(Dblstat));
+ return 0;
+ case DMSDOS_READ_BLOCK:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ bh=raw_bread(dir->i_sb,sector);
+ if(bh==NULL)return -EIO;
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),SECTOR_SIZE))return -EFAULT;
+ memcpy_tofs(idata+sizeof(long),bh->b_data,SECTOR_SIZE);
+ raw_brelse(dir->i_sb,bh);
+ return 0;
+ case DMSDOS_WRITE_BLOCK:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)+SECTOR_SIZE))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ bh=raw_bread(dir->i_sb,sector);
+ if(bh==NULL)return -EIO;
+ memcpy_fromfs(bh->b_data,idata+sizeof(long),SECTOR_SIZE);
+ raw_mark_buffer_dirty(dir->i_sb,bh,1);
+ raw_brelse(dir->i_sb,bh);
+ return 0;
+ case DMSDOS_READ_DIRENTRY:
+ case DMSDOS_WRITE_DIRENTRY:
+ printk(KERN_WARNING "DMSDOS: READ/WRITE DIRENTRY ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_READ_BITFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ val=dbl_bitfat_value(sb,sector,NULL);
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&val,sizeof(long));
+ return 0;
+ case DMSDOS_WRITE_BITFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, 2*sizeof(long)))return -EFAULT;
+ /*sector=get_fs_long(idata);*/
+ memcpy_fromfs(&sector,idata,sizeof(long));
+ if(sector<0||sector>dblsb->s_dataend)return -EINVAL;
+ /*newval=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&newval,idata+sizeof(long),sizeof(long));
+ dbl_bitfat_value(sb,sector,&newval);
+ return 0;
+ case DMSDOS_READ_MDFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ dbl_mdfat_value(sb,cluster,NULL,&mde);
+ if(verify_area(VERIFY_WRITE,idata+sizeof(long),sizeof(Mdfat_entry)))return -EFAULT;
+ memcpy_fromfs(&lval,idata+sizeof(long),sizeof(long));
+ memcpy_tofs((Mdfat_entry*)lval,&mde,sizeof(Mdfat_entry));
+ return 0;
+ case DMSDOS_WRITE_MDFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)+sizeof(Mdfat_entry)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ memcpy_fromfs(&lval,idata+sizeof(long),sizeof(long));
+ memcpy_fromfs(&mde,(Mdfat_entry*)lval,
+ sizeof(Mdfat_entry));
+ dbl_mdfat_value(sb,cluster,&mde,&dummy);
+ return 0;
+ case DMSDOS_READ_DFAT:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ val=dbl_fat_nextcluster(sb,cluster,NULL);
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&val,sizeof(long));
+ return 0;
+ case DMSDOS_WRITE_DFAT:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, 2*sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster>dblsb->s_max_cluster)return -EINVAL;
+ /*newval=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&newval,idata+sizeof(long),sizeof(long));
+ dbl_fat_nextcluster(sb,cluster,&newval);
+ return 0;
+ case DMSDOS_READ_CLUSTER:
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ /*cluster=get_fs_long(idata);*/
+ memcpy_fromfs(&cluster,idata,sizeof(long));
+ if(cluster < 0 || cluster>dblsb->s_max_cluster)
+ return -EINVAL;
+ membytes=SECTOR_SIZE*dblsb->s_sectperclust;
+ if(verify_area(VERIFY_WRITE, idata+sizeof(long),membytes))return -EFAULT;
+ if((clusterd=(unsigned char*)MALLOC(membytes))==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: read_cluster: no memory!\n");
+ return -EIO;
+ }
+ val=dmsdos_read_cluster(dir->i_sb,clusterd,cluster);
+ if (val >= 0)
+ memcpy_tofs(idata+sizeof(long), clusterd, membytes);
+ FREE(clusterd);
+ return val;
+ case DMSDOS_SET_COMP:
+ if(current->euid!=0)return -EPERM;
+ if(dblsb->s_comp!=READ_ONLY&&data==READ_ONLY)sync_cluster_cache(0);
+ dblsb->s_comp=data;
+ if(data==READ_ONLY)sb->s_flags |= MS_RDONLY;
+ else sb->s_flags&=~MS_RDONLY;
+ return 0;
+ case DMSDOS_SET_CF:
+ if(current->euid!=0)return -EPERM;
+ if(data>=12u)return -EINVAL;
+ dblsb->s_cfaktor=data;
+ return 0;
+ case DMSDOS_SIMPLE_CHECK:
+ if(verify_area(VERIFY_READ, idata, sizeof(long)))return -EFAULT;
+ memcpy_fromfs(&lval,idata,sizeof(long));
+ val=simple_check(dir->i_sb,lval);
+ if(verify_area(VERIFY_WRITE, idata, sizeof(long)))return -EFAULT;
+ /*put_fs_long(val,idata);*/
+ memcpy_tofs(idata,&val,sizeof(long));
+ return 0;
+ case DMSDOS_DUMPCACHE:
+ dumpcache();
+ dump_ccache();
+ return 0;
+ case DMSDOS_D_ASK:
+ #ifdef INTERNAL_DAEMON
+ return -EINVAL;
+ #else
+ if(current->euid!=0)return -EPERM;
+ /* dmsdosd says it is present and ready */
+ if(current->pid!=data)
+ { printk(KERN_ERR "DMSDOS: daemon is lying about its pid\n");
+ return -EINVAL;
+ }
+ daemon_present=1;
+ daemon_pid=current->pid;
+ LOG_DAEMON("DMSDOS: D_ASK\n");
+ return 0;
+ #endif
+ case DMSDOS_D_READ:
+ #ifdef INTERNAL_DAEMON
+ return -EINVAL;
+ #else
+ if(current->euid!=0)return -EPERM;
+ lock_listaccess();
+ /*search next valid entry*/
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[rlist].flag==D_VALID)
+ { /* check in mdfat that cluster is actually used */
+ dbl_mdfat_value(rwlist[rlist].sb,rwlist[rlist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)==3)goto vr_found; /* used and uncompressed */
+ rwlist[rlist].flag=D_EMPTY; /* remove - it's garbage */
+ --listcount;
+ LOG_DAEMON("DMSDOS: D_READ: removing garbage entry cluster=%d\n",
+ rwlist[rlist].clusternr);
+ }
+ /*rlist=(rlist+1)&(LISTSIZE-1);*/
+ rlist++;if(rlist>=LISTSIZE)rlist=0;
+ }
+ unlock_listaccess();
+ return 0;
+ vr_found:
+ cluster=rwlist[rlist].clusternr;
+ /* yes we change sb here */
+ sb=rwlist[rlist].sb;
+ dblsb=MSDOS_SB(sb)->private_data;
+ length=rwlist[rlist].length;
+ clusterd=MALLOC(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(clusterd==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: no memory!\n");
+ unlock_listaccess();
+ return 0;
+ }
+ if((i=dmsdos_read_cluster(sb,clusterd,cluster))<0)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: read_cluster failed!\n");
+ rwlist[rlist].flag=D_EMPTY;
+ --listcount;
+ unlock_listaccess();
+ FREE(clusterd);
+ return 0;
+ }
+ if(length<0)length=i; /* if invalid length, use the read value */
+ rwlist[rlist].flag=D_IN_D_ACTION;
+ if(verify_area(VERIFY_WRITE, idata, 3*sizeof(long)+length))
+ { unlock_listaccess();
+ FREE(clusterd);
+ return -EFAULT;
+ }
+ /*put_fs_long(rlist,idata);*/
+ memcpy_tofs(idata,&rlist,sizeof(long));
+ /*put_fs_long(length,idata+sizeof(long));*/
+ memcpy_tofs(idata+sizeof(long),&length,sizeof(long));
+ /*put_fs_long(rwlist[rlist].method,idata+2*sizeof(long));*/
+ memcpy_tofs(idata+2*sizeof(long),&(rwlist[rlist].method),sizeof(long));
+ memcpy_tofs(idata+3*sizeof(long),clusterd,length);
+ unlock_listaccess();
+ FREE(clusterd);
+ return 1;
+#endif
+ case DMSDOS_D_WRITE:
+#ifdef INTERNAL_DAEMON
+ return -EINVAL;
+#else
+ if(current->euid!=0)return -EPERM;
+ if(verify_area(VERIFY_READ,idata,3*sizeof(long)))return -EFAULT;
+ lock_listaccess();
+ /*plist=get_fs_long(idata);*/
+ memcpy_fromfs(&plist,idata,sizeof(long));
+ if(rwlist[plist].flag==D_OVERWRITTEN){rwlist[plist].flag=D_EMPTY;--listcount;}
+ if(rwlist[plist].flag!=D_IN_D_ACTION)
+ { LOG_DAEMON("DMSDOS: D_WRITE: Entry not in action, flag=%d cluster=%d\n",
+ rwlist[plist].flag,rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* will be freed in any case later, so: */
+ --listcount;
+
+ /*rawlength=get_fs_long(idata+sizeof(long));*/
+ memcpy_fromfs(&rawlength,idata+sizeof(long),sizeof(long));
+ if(rawlength==0)
+ { /* data were uncompressible */
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ /* check that cluster is used */
+ dbl_mdfat_value(rwlist[plist].sb,rwlist[plist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)!=3)
+ { rwlist[plist].flag=D_EMPTY; /* remove - it's garbage */
+ LOG_DAEMON("DMSDOS: D_WRITE: removing garbage entry cluster=%d\n",
+ rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+ if(verify_area(VERIFY_READ,idata+3*sizeof(long),rawlength))
+ { rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return -EFAULT;
+ }
+ clusterd=MALLOC(rawlength);
+ if(clusterd==NULL)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_WRITE: no memory!\n");
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ length=rwlist[plist].length;
+ cluster=rwlist[plist].clusternr;
+ sb=rwlist[plist].sb;
+ memcpy_fromfs(clusterd,idata+3*sizeof(long),rawlength);
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ /* this may call try_daemon via ccache code (freeing a ccache slot) */
+ daemon_write_cluster(sb,clusterd,length,
+ cluster,
+ rawlength);
+ FREE(clusterd);
+ return 0;
+#endif
+ case DMSDOS_D_EXIT:
+#ifdef INTERNAL_DAEMON
+ return -EINVAL;
+#else
+ if(current->euid!=0)return -EPERM;
+ /* dmsdosd is saying good-bye */
+ daemon_present=0;
+ LOG_DAEMON("DMSDOS: D_EXIT\n");
+ return 0;
+#endif
+ case DMSDOS_MOVEBACK:
+ printk(KERN_WARNING "DMSDOS: MOVEBACK ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_SET_MAXCLUSTER:
+ /*if(current->euid!=0)return -EPERM;
+ return set_maxcluster(dir->i_sb,data);*/
+ printk(KERN_WARNING "DMSDOS: SETMAXCLUSTER ioctl has gone.\n");
+ return -EINVAL;
+ case DMSDOS_FREE_IDLE_CACHE:
+ if(current->euid!=0)return -EPERM;
+ free_idle_cache();
+ return 0;
+ case DMSDOS_SET_LOGLEVEL:
+ if(current->euid!=0)return -EPERM;
+ loglevel=data;
+ printk(KERN_INFO "DMSDOS: ioctl: loglevel set to 0x%lx.\n",loglevel);
+ return 0;
+ case DMSDOS_SET_SPEEDUP:
+ if(current->euid!=0)return -EPERM;
+ dmsdos_speedup=data;
+ printk(KERN_INFO "DMSDOS: ioctl: speedup set to 0x%lx.\n",dmsdos_speedup);
+ return 0;
+ case DMSDOS_SYNC_CCACHE:
+ sync_cluster_cache(data);
+ return 0;
+ case DMSDOS_LOG_STATISTICS:
+ if(current->euid!=0)return -EPERM;
+ log_statistics();
+ return 0;
+ case DMSDOS_RECOMPRESS:
+ printk(KERN_WARNING "DMSDOS: RECOMPRESS ioctl has gone\n");
+ return -EINVAL;
+ case DMSDOS_REPORT_MEMORY:
+ { Memuse memuse;
+ if(verify_area(VERIFY_WRITE,idata,sizeof(Memuse)))return -EFAULT;
+ get_memory_usage_acache(&(memuse.acachebytes),&(memuse.max_acachebytes));
+ get_memory_usage_ccache(&(memuse.ccachebytes),&(memuse.max_ccachebytes));
+ memcpy_tofs(idata,&memuse,sizeof(Memuse));
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+
+}
+
+void remove_from_daemon_list(struct super_block*sb,int clusternr)
+{ int i;
+
+ if(must_maintain_list==0)return;
+
+ lock_listaccess();
+
+ /* check list for existing entry and mark it as overwritten */
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].flag!=D_EMPTY)
+ if(rwlist[i].clusternr==clusternr&&rwlist[i].sb->s_dev==sb->s_dev)
+ {
+ if(rwlist[i].flag==D_IN_D_ACTION){rwlist[i].flag=D_OVERWRITTEN;break;}
+ if(rwlist[i].flag==D_VALID){rwlist[i].flag=D_EMPTY;--listcount;break;}
+ }
+ }
+
+ if(daemon_present==0)
+ { if(listcount==0)must_maintain_list=0;
+ }
+
+ unlock_listaccess();
+}
+
+int lastawake=0;
+int try_daemon(struct super_block*sb,int clusternr, int length, int method)
+{ int i;
+
+ if(daemon_present==0&&must_maintain_list==0)return 0;
+
+ lock_listaccess();
+
+ must_maintain_list=1;
+
+ /* check list for existing entry and mark it as overwritten */
+/* no longer necessary here......
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].clusternr==clusternr&&rwlist[i].sb->s_dev==sb->s_dev)
+ {
+ if(rwlist[i].flag==D_IN_D_ACTION){rwlist[i].flag=D_OVERWRITTEN;break;}
+ if(rwlist[i].flag==D_VALID){rwlist[i].flag=D_EMPTY;--listcount;break;}
+ }
+ }
+*/
+
+ if(daemon_present==0||listcount==LISTSIZE)
+ {
+ if(listcount==0)must_maintain_list=0;
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* find empty slot in list */
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[wlist].flag==D_EMPTY) goto w_found;
+ /*wlist=(wlist+1)&(LISTSIZE-1);*/
+ wlist++;if(wlist>=LISTSIZE)wlist=0;
+ }
+ /*this shouldn't happen - otherwise there's a count error */
+ printk(KERN_WARNING "DMSDOS: try_daemon: no empty slot found, listcount corrected.\n");
+ listcount=LISTSIZE;
+ unlock_listaccess();
+ return 0;
+
+ w_found:
+ ++listcount;
+ rwlist[wlist].clusternr=clusternr;
+ rwlist[wlist].sb=sb;
+ rwlist[wlist].length=length;
+ rwlist[wlist].method=method;
+ rwlist[wlist].flag=D_VALID;
+ unlock_listaccess();
+
+ /* check whether or not to awake the daemon -
+ strategy is this:
+ * don't awake it in periods below 5 secs
+ * don't awake it for just a little data
+ */
+ if(lastawake+5>CURRENT_TIME||listcount<LISTSIZE/2)return 1;
+ lastawake=CURRENT_TIME;
+#ifdef INTERNAL_DAEMON
+ wake_up(&daemon_wait);
+#else
+ i=kill_proc(daemon_pid,SIGUSR1,1);
+ if(i<0)
+ { printk(KERN_WARNING "DMSDOS: try_daemon: kill_proc daemon_pid=%d failed with error code %d, assuming daemon has died\n",
+ daemon_pid,-i);
+ daemon_present=0;
+ }
+#endif
+ return 1;
+}
+
+void clear_list_dev(struct super_block*sb)
+{ int i;
+
+ lock_listaccess();
+ for(i=0;i<LISTSIZE;++i)
+ { if(rwlist[i].flag!=D_EMPTY)
+ { if(rwlist[i].sb)printk(KERN_ERR "DMSDOS: clear_list_dev: Uhh, sb==NULL ...\n");
+ else
+ if(rwlist[i].sb->s_dev==sb->s_dev)
+ { rwlist[i].flag=D_EMPTY;
+ --listcount;
+ }
+ }
+ }
+ unlock_listaccess();
+}
+
+void init_daemon(void)
+{ int i;
+
+ if(daemon_present)
+ { printk(KERN_INFO "DMSDOS: init_daemon: daemon already present\n");
+#ifdef INTERNAL_DAEMON
+ ++internal_daemon_counter;
+#endif
+ return;
+ }
+ lock_listaccess();
+ LOG_REST("DMSDOS: clearing rwlist...\n");
+ for(i=0;i<LISTSIZE;++i)rwlist[i].flag=D_EMPTY;
+ listcount=0;
+ unlock_listaccess();
+
+#ifdef INTERNAL_DAEMON
+ /*daemon_present=1...this is maintained by idmsdosd itself*/;
+ internal_daemon_counter=1;
+ /* fire up internal daemon */
+ printk(KERN_NOTICE "DMSDOS: starting internal daemon...\n");
+ daemon_go_home=0;
+ kernel_thread(idmsdosd,NULL,0);
+#endif
+}
+
+void log_list_statistics()
+{ int j;
+ int empty;
+ int valid;
+ int in_action;
+ int overwritten;
+
+ lock_listaccess();
+
+ printk(KERN_INFO "DMSDOS: list statistics:\n");
+ printk(KERN_INFO "daemon_present=%d must_maintain_list=%d listcount=%d\n",
+ daemon_present,must_maintain_list,listcount);
+ empty=0;
+ valid=0;
+ in_action=0;
+ overwritten=0;
+
+ for(j=0;j<LISTSIZE;++j)
+ { switch(rwlist[j].flag)
+ { case D_EMPTY: ++empty; break;
+ case D_VALID: ++valid; break;
+ case D_IN_D_ACTION: ++in_action; break;
+ case D_OVERWRITTEN: ++overwritten; break;
+ default: printk(KERN_ERR "DMSDOS: log_list_statistics: cannot happen.\n");
+ }
+ }
+ printk(KERN_INFO "sum: empty=%d valid=%d in_action=%d overwritten=%d\n",
+ empty,valid,in_action,overwritten);
+
+ unlock_listaccess();
+}
+
+#ifdef INTERNAL_DAEMON
+
+int internal_d_read(int*val1, int*val2, int*val3,
+ unsigned char*clusterd)
+{ int i;
+ int cluster;
+ Mdfat_entry mde;
+ int length;
+ int method;
+ struct super_block*sb=NULL;
+
+ lock_listaccess();
+ /*search next valid entry*/
+ for(i=LISTSIZE;i>0;--i)
+ { if(rwlist[rlist].flag==D_VALID)
+ { /* check in mdfat that cluster is actually used */
+ sb=rwlist[rlist].sb;
+ if(sb)
+ { dbl_mdfat_value(sb,rwlist[rlist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)==3)goto vr_found; /* used and uncompressed */
+ }
+ rwlist[rlist].flag=D_EMPTY; /* remove - it's garbage */
+ --listcount;
+ LOG_DAEMON("DMSDOS: D_READ: removing garbage entry cluster=%d\n",
+ rwlist[rlist].clusternr);
+ }
+ /*rlist=(rlist+1)&(LISTSIZE-1);*/
+ rlist++;if(rlist>=LISTSIZE)rlist=0;
+ }
+ unlock_listaccess();
+ return 0;
+ vr_found:
+ cluster=rwlist[rlist].clusternr;
+ sb=rwlist[rlist].sb;
+ length=rwlist[rlist].length;
+ method=rwlist[rlist].method;
+ if((i=dmsdos_read_cluster(sb,clusterd,cluster))<0)
+ { printk(KERN_ERR "DMSDOS: ioctl: D_READ: read_cluster failed!\n");
+ rwlist[rlist].flag=D_EMPTY;
+ --listcount;
+ unlock_listaccess();
+ return 0;
+ }
+ if(length<0)length=i; /* if invalid length, use read value */
+ rwlist[rlist].flag=D_IN_D_ACTION;
+ *val1=rlist; /*put_fs_long(rlist,idata);*/
+ *val2=length; /*put_fs_long(length,idata+sizeof(long));*/
+ *val3=method; /*put_fs_long(rwlist[rlist].method,idata+2*sizeof(long));*/
+ unlock_listaccess();
+ return 1;
+}
+
+int internal_d_write(int plist, int rawlength, unsigned char*clusterd)
+{ Mdfat_entry mde;
+ int length;
+ int cluster;
+ struct super_block*sb=NULL;
+
+ lock_listaccess();
+ if(rwlist[plist].flag==D_OVERWRITTEN){rwlist[plist].flag=D_EMPTY;--listcount;}
+ if(rwlist[plist].flag!=D_IN_D_ACTION)
+ { LOG_DAEMON("DMSDOS: D_WRITE: Entry not in action, flag=%d cluster=%d\n",
+ rwlist[plist].flag,rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+
+ /* will be freed in any case later, so: */
+ --listcount;
+
+ if(rawlength==0)
+ { /* data were uncompressible */
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ return 0;
+ }
+ /* check that cluster is used */
+ sb=rwlist[plist].sb;
+ if(sb==NULL)goto shitt;
+ dbl_mdfat_value(sb,rwlist[plist].clusternr,
+ NULL,&mde);
+ if((mde.flags&3)!=3)
+ { shitt:
+ rwlist[plist].flag=D_EMPTY; /* remove - it's garbage */
+ LOG_DAEMON("DMSDOS: D_WRITE: removing garbage entry cluster=%d\n",
+ rwlist[plist].clusternr);
+ unlock_listaccess();
+ return 0;
+ }
+ length=rwlist[plist].length;
+ cluster=rwlist[plist].clusternr;
+ rwlist[plist].flag=D_EMPTY;
+ unlock_listaccess();
+ /* this may call try_daemon via ccache code (freeing a ccache slot) */
+ daemon_write_cluster(sb,clusterd,length,cluster,
+ rawlength);
+ return 0;
+}
+
+typedef struct
+{ long val1;
+ long val2;
+ long val3;
+ unsigned char data[32*1024];
+} Cdata;
+
+/* we need the memory always - there's only one process using it - idmsdosd */
+Cdata cdata;
+Cdata ckdata;
+
+int get_and_compress_one(void)
+{ int ret;
+ int handle;
+ int length;
+ int size;
+ int method;
+
+ /* get cluster to compress */
+ LOG_DAEMON("idmsdosd: Trying to read...\n");
+ ret=internal_d_read(&handle,&length,&method,cdata.data);
+ if(ret!=1)
+ { LOG_DAEMON("idmsdosd: nothing there - D_READ ioctl returned %d\n",ret);
+ return ret;
+ }
+
+ size=(length-1)/512+1;
+ LOG_DAEMON("idmsdosd: compressing...\n");
+ ret=
+#ifdef DMSDOS_CONFIG_STAC
+ (method==SD_3||method==SD_4) ?
+ stac_compress(cdata.data,length,ckdata.data,
+ sizeof(ckdata.data),method,11) :
+#endif
+ dbl_compress(ckdata.data,cdata.data,size,method,11)*512;
+ LOG_DAEMON("idmsdosd: compress %X from %d returned %d\n",
+ method,length,ret);
+ if(ret<0)ret=0; /* compression failed */
+ LOG_DAEMON("idmsdosd: writing...\n");
+ internal_d_write(handle,ret,ckdata.data);
+
+ return 1; /* one cluster compressed */
+}
+
+struct timer_list idmsdosd_timer;
+
+static void idmsdosd_timer_function(unsigned long data)
+{
+ /* do something */
+ /*printk(KERN_DEBUG "DMSDOS: idmsdosd_timer_function: doing something :)\n");*/
+
+ /* wake up daemon */
+ wake_up(&daemon_wait);
+
+ del_timer(&idmsdosd_timer);
+ idmsdosd_timer.expires=jiffies + (IDMSDOSD_TIME * HZ);
+ add_timer(&idmsdosd_timer);
+}
+
+int idmsdosd(void*dummy)
+{
+ /* throw away some things from the calling process */
+ /* otherwise the root fs cannot be unmounted on reboot ... urgh*/
+ exit_files(current);
+ exit_fs(current);
+ exit_sighand(current);
+ exit_mm(current);
+
+ /*
+ * We have a bare-bones task_struct, and really should fill
+ * in a few more things so "top" and /proc/2/{exe,root,cwd}
+ * display semi-sane things. Not real crucial though...
+ */
+ current->session = 1;
+ current->pgrp = 1;
+ sprintf(current->comm, "dmsdosd");
+
+#undef NEED_LOCK_KERNEL
+ /* do we really need this ??? The code was copied from kflushd. */
+#ifdef NEED_LOCK_KERNEL
+ /*
+ * As a kernel thread we want to tamper with system buffers
+ * and other internals and thus be subject to the SMP locking
+ * rules. (On a uniprocessor box this does nothing).
+ */
+#ifdef __SMP__
+ lock_kernel();
+ syscall_count++;
+#endif
+#endif
+
+ daemon_present=1;
+
+ init_timer(&idmsdosd_timer);
+ idmsdosd_timer.function=idmsdosd_timer_function;
+ idmsdosd_timer.expires=jiffies + (IDMSDOSD_TIME * HZ);
+ add_timer(&idmsdosd_timer);
+
+ for(;;)
+ { while(get_and_compress_one()==1);
+ /* don't kill the system performance when nothing to compress */
+ { LOG_DAEMON("idmsdosd: sleeping...\n");
+ interruptible_sleep_on(&daemon_wait);
+ if(daemon_go_home)break;
+ /* throw away long idle mdfat/dfat/bitfat sectors and clusters */
+ free_idle_cache();
+ }
+ }
+
+ del_timer(&idmsdosd_timer);
+ daemon_present=0;
+
+ LOG_DAEMON("idmsdosd: exiting...\n");
+ wake_up(&daemon_exit_wait);
+ return 0;
+}
+
+void remove_internal_daemon(void)
+{ if(daemon_present)
+ { printk(KERN_NOTICE "DMSDOS: killing internal daemon...\n");
+ daemon_go_home=1;
+ wake_up(&daemon_wait);
+ interruptible_sleep_on(&daemon_exit_wait);
+ /* this seems to work - don't ask me why :) */
+ }
+}
+
+#endif
+
+void exit_daemon(void)
+{
+#ifdef INTERNAL_DAEMON
+ --internal_daemon_counter;
+ if(internal_daemon_counter<0)
+ { printk(KERN_WARNING "DMSDOS: exit_daemon: counter<0 ???\n");
+ internal_daemon_counter=0;
+ }
+ if(internal_daemon_counter==0)remove_internal_daemon();
+#endif
+}
+
+void force_exit_daemon(void)
+{
+#ifdef INTERNAL_DAEMON
+ internal_daemon_counter=0;
+ remove_internal_daemon();
+#endif
+}
diff --git a/src/dblspace_methsq.c b/src/dblspace_methsq.c
new file mode 100644
index 0000000..43422b1
--- /dev/null
+++ b/src/dblspace_methsq.c
@@ -0,0 +1,1256 @@
+/*
+dblspace_methsq.c
+
+DMSDOS CVF-FAT module: drivespace SQ compression/decompression routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#include <linux/malloc.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_DAEMON__
+#include<malloc.h>
+#include<string.h>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
+extern int debug;
+#undef LOG_DECOMP
+#define LOG_DECOMP if(debug)printk
+#endif
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#endif
+
+#ifdef DMSDOS_CONFIG_DRVSP3
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+/* store and load __u16 in any byteorder on any */
+/* address (odd or even). */
+/* this is problematic on architectures, */
+/* which cannot do __u16 access to odd address. */
+/* used for temporary storage of LZ intercode. */
+#define C_ST_u16(p,v) {put_unaligned(v,((__u16*)p)++);}
+#define C_LD_u16(p,v) {v=get_unaligned(((__u16*)p)++);}
+
+/* high speed compare and move routines */
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+#ifdef GAS_XLAT_BUG
+#define XLAT "xlatl\n\t"
+#else
+#define XLAT "xlat\n\t"
+#endif
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+/* compare blocks, overlaping repeat test */
+/* pointers and counter are modified to point after block */
+/* D and S points to first diff adr */
+#define M_FIRSTDIFF(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "cmpsb\n\t" \
+ "je 1f\n\t" \
+ "dec %0\n\t" \
+ "dec %1\n\t" \
+ "1:\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ )
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+#define M_FIRSTDIFF(D,S,C) for(;(*(__u8*)(D)==*(__u8*)(S))&&(C);\
+ (__u8*)(D)++,(__u8*)(S)++,(C)--)
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define cpu_to_le16(v) (v)
+#endif
+
+/*==============================================================*/
+/* bitstream reading */
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* already readed bits from buf */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+const unsigned sq_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+/* read next 16 bits from input */
+#define RDN_G16(bits) \
+ { \
+ (bits).buf>>=16; \
+ (bits).pb-=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=((__u32)(le16_to_cpu(*((bits).pd++))))<<16; \
+ }; \
+ }
+
+/* prepares at least 16 bits for reading */
+#define RDN_PR(bits,u) \
+ { \
+ if((bits).pb>=16) RDN_G16(bits); \
+ u=(bits).buf>>(bits).pb; \
+ }
+
+/* initializes reading from bitstream */
+INLINE void sq_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+/* reads n<=16 bits from bitstream *pbits */
+INLINE unsigned sq_rdn(bits_t *pbits,int n)
+{
+ unsigned u;
+ RDN_PR(*pbits,u);
+ pbits->pb+=n;
+ u&=sq_bmsk[n];
+ return u;
+};
+
+/*==============================================================*/
+/* huffman decoding */
+
+#define MAX_SPDA_BITS 10
+#define MAX_SPDA_LEN (1<<MAX_SPDA_BITS)
+#define MAX_BITS 16
+#define MAX_CODES 0x140
+#define OUT_OVER 0x100
+
+typedef
+ struct {
+ __s8 ln; /* character lens .. for tokens -0x40 */
+ __u8 ch; /* character/token code */
+ }huf_chln_t;
+
+typedef
+ struct {
+ unsigned cd_ln[MAX_BITS+1]; /* distribution of bits */
+ unsigned cd_ch[MAX_BITS+1]; /* distribution of codes codes */
+ int bn; /* chln array convert max bn bits codes */
+ huf_chln_t chln1[MAX_CODES]; /* for codes with more than bn bits */
+ huf_chln_t chln[0]; /* character codes decode array length SPDA_LEN */
+ }huf_rd_t;
+
+#define HUF_RD_SIZE(SPDA_LEN) \
+ ((sizeof(huf_rd_t)+SPDA_LEN*sizeof(huf_chln_t)+3)&~3)
+
+const __u8 swap_bits_xlat[]=
+ {0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0,0x10,0x90,
+ 0x50,0xd0,0x30,0xb0,0x70,0xf0,0x08,0x88,0x48,0xc8,
+ 0x28,0xa8,0x68,0xe8,0x18,0x98,0x58,0xd8,0x38,0xb8,
+ 0x78,0xf8,0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4,
+ 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4,0x0c,0x8c,
+ 0x4c,0xcc,0x2c,0xac,0x6c,0xec,0x1c,0x9c,0x5c,0xdc,
+ 0x3c,0xbc,0x7c,0xfc,0x02,0x82,0x42,0xc2,0x22,0xa2,
+ 0x62,0xe2,0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2,
+ 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea,0x1a,0x9a,
+ 0x5a,0xda,0x3a,0xba,0x7a,0xfa,0x06,0x86,0x46,0xc6,
+ 0x26,0xa6,0x66,0xe6,0x16,0x96,0x56,0xd6,0x36,0xb6,
+ 0x76,0xf6,0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee,
+ 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe,0x01,0x81,
+ 0x41,0xc1,0x21,0xa1,0x61,0xe1,0x11,0x91,0x51,0xd1,
+ 0x31,0xb1,0x71,0xf1,0x09,0x89,0x49,0xc9,0x29,0xa9,
+ 0x69,0xe9,0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9,
+ 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5,0x15,0x95,
+ 0x55,0xd5,0x35,0xb5,0x75,0xf5,0x0d,0x8d,0x4d,0xcd,
+ 0x2d,0xad,0x6d,0xed,0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,
+ 0x7d,0xfd,0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3,
+ 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3,0x0b,0x8b,
+ 0x4b,0xcb,0x2b,0xab,0x6b,0xeb,0x1b,0x9b,0x5b,0xdb,
+ 0x3b,0xbb,0x7b,0xfb,0x07,0x87,0x47,0xc7,0x27,0xa7,
+ 0x67,0xe7,0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7,
+ 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef,0x1f,0x9f,
+ 0x5f,0xdf,0x3f,0xbf,0x7f,0xff};
+
+/* swap 16 bits order */
+INLINE unsigned swap_bits_order_16(unsigned d)
+{ unsigned r;
+ #ifdef USE_GNU_ASM_i386
+ __asm__ (
+ XLAT
+ "xchgb %%al,%%ah\n\t"
+ XLAT
+ :"=a"(r):"0"(d),"b"(swap_bits_xlat));
+ #else
+ r=((unsigned)swap_bits_xlat[(__u8)d])<<8;
+ r|=swap_bits_xlat[(__u8)(d>>8)];
+ #endif
+ return r;
+};
+
+/* swap bit order */
+INLINE unsigned swap_bits_order(unsigned d,int n)
+{ unsigned r=0;
+ while(n--) { r<<=1;r|=d&1;d>>=1;};
+ return r;
+};
+
+/* initializes huffman conversion structure *phuf for m codes,
+ *ca code and token bit lengths, ends with 0xFF,
+ bn predicated maximal bit length */
+int sq_rdhufi(huf_rd_t *phuf,int m,int bn,__u8 *ca)
+{
+ if(bn>MAX_SPDA_BITS) bn=MAX_SPDA_BITS;
+ phuf->bn=bn;
+ {
+ int i;
+ unsigned u,us,ut;
+ memset(phuf->cd_ln,0,sizeof(phuf->cd_ln));i=0;
+ while((u=ca[i++])<=MAX_BITS) phuf->cd_ln[u]++;
+ memset(phuf->cd_ch,0,sizeof(phuf->cd_ch));
+ phuf->cd_ln[0]=0;us=0;ut=0;
+ for(i=1;i<=MAX_BITS;i++)
+ {
+ u=phuf->cd_ln[i];phuf->cd_ln[i]=ut;
+ phuf->cd_ch[i]=us;ut+=u;us+=u;us<<=1;
+ };
+ /* if suceed, codespace should be full else report error */
+ if (us&((1<<MAX_BITS)-1))
+ if(us!=1)return(0); /* exeption, zip 2.0 allows one code one bit long */
+ };
+ {
+ int i,ln,l,ch,sh,cod;
+ for(i=0;(l=ln=ca[i])<=MAX_BITS;i++) if(ln)
+ {
+ sh=(bn-ln);
+ cod=(phuf->cd_ch[ln])++;
+ cod=swap_bits_order_16(cod)>>(16-ln);
+ if(i<m) ch=i; else {ch=i-m+1;ln-=0x40;};
+ if (sh>0)
+ {
+ sh=1<<sh;
+ l=1<<l;
+ while(sh--)
+ {
+ phuf->chln[cod].ch=ch;
+ phuf->chln[cod].ln=ln;
+ cod+=l;
+ };
+ } else if (sh==0) {
+ phuf->chln[cod].ch=ch;
+ phuf->chln[cod].ln=ln;
+ } else {
+ cod&=sq_bmsk[bn];
+ phuf->chln[cod].ch=0x00;
+ phuf->chln[cod].ln=-0x40;
+ cod=(phuf->cd_ln[l])++;
+ phuf->chln1[cod].ch=ch;
+ phuf->chln1[cod].ln=ln;
+ };
+ };
+ /* if suceed ln should be 0xFF */
+ };
+ return(1);
+};
+
+/* read and huffman decode of characters, stops on tokens or buffer ends */
+INLINE unsigned sq_rdh(bits_t *pbits,const huf_rd_t *phuf,__u8 **pout,__u8 *pend)
+{
+ unsigned ch;
+ unsigned bmsk=sq_bmsk[phuf->bn];
+
+ while(1)
+ {while(1)
+ {if(pbits->pb>=16)
+ RDN_G16(*pbits);
+ if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+
+ if(pbits->pb<16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+
+ if(pbits->pb<16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>pbits->pb)&bmsk;
+ if((pbits->pb+=phuf->chln[ch].ln)<0) break;
+ *((*pout)++)=phuf->chln[ch].ch;
+ };
+ };
+ };
+
+ ch=phuf->chln[ch].ch;
+ pbits->pb+=0x40; if(ch--) return ch;
+ /* code longer than phuf->bn */
+ if(pbits->pb>=16) RDN_G16(*pbits);
+ ch=swap_bits_order_16((__u16)(pbits->buf>>pbits->pb));
+ {
+ int i;
+ i=phuf->bn;
+ do
+ i++;
+ while((phuf->cd_ch[i]<=(ch>>(16-i)))&&(i<MAX_BITS));
+ ch=((ch>>(16-i)))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb+=phuf->chln1[ch].ln)<0)
+ {pbits->pb+=0x40;
+ return phuf->chln1[ch].ch-1;
+ };
+ *((*pout)++)=phuf->chln1[ch].ch;
+ };
+};
+
+/* read one huffman encoded value */
+INLINE unsigned sq_rdh1(bits_t *pbits,const huf_rd_t *phuf)
+{unsigned ch;
+ if(pbits->pb>=16) RDN_G16(*pbits);
+ ch=(pbits->buf>>pbits->pb)&sq_bmsk[phuf->bn];
+ if((pbits->pb+=phuf->chln[ch].ln)>=0) return phuf->chln[ch].ch;
+ ch=phuf->chln[ch].ch;
+ pbits->pb+=0x40; if(ch) return ch+0x100-1;
+ ch=swap_bits_order_16((__u16)(pbits->buf>>pbits->pb));
+ {int i;
+ i=phuf->bn;
+ do
+ i++;
+ while((phuf->cd_ch[i]<=(ch>>(16-i)))&&(i<MAX_BITS));
+ ch=((ch>>(16-i)))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb+=phuf->chln1[ch].ln)>=0) return phuf->chln1[ch].ch;
+ pbits->pb+=0x40;
+ return phuf->chln1[ch].ch+0x100-1;
+};
+
+/*==============================================================*/
+/* SQ decompression */
+
+/* index conversion table for first bitlen table */
+const int code_index_1[]={0x10,0x11,0x12,0x00,0x08,0x07,0x09,0x06,0x0A,0x05,
+ 0x0B,0x04,0x0C,0x03,0x0D,0x02,0x0E,0x01,0x0F};
+
+const unsigned sqt_repbas[]={
+ 0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0D,
+ 0x0F,0x11,0x13,0x17,0x1B,0x1F,0x23,0x2B,0x33,0x3B,
+ 0x43,0x53,0x63,0x73,0x83,0xA3,0xC3,0xE3,0x102,0x00,0x00};
+
+const unsigned char sqt_repbln[]={
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,
+ 0x01,0x01,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x03,
+ 0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x00,0x63,0x63};
+
+const unsigned sqt_offbas[]={
+ 0x0001,0x0002,0x0003,0x0004,0x0005,0x0007,0x0009,0x000D,
+ 0x0011,0x0019,0x0021,0x0031,0x0041,0x0061,0x0081,0x00C1,
+ 0x0101,0x0181,0x0201,0x0301,0x0401,0x0601,0x0801,0x0C01,
+ 0x1001,0x1801,0x2001,0x3001,0x4001,0x6001,0x0000,0x0000};
+
+const unsigned char sqt_offbln[]={
+ 0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x02,
+ 0x03,0x03,0x04,0x04,0x05,0x05,0x06,0x06,
+ 0x07,0x07,0x08,0x08,0x09,0x09,0x0A,0x0A,
+ 0x0B,0x0B,0x0C,0x0C,0x0D,0x0D,0x00,0x00};
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+
+int sq_dec(void* pin,int lin, void* pout, int lout, int flg)
+{
+ __u8 *p, *pend, *r;
+ unsigned u, u1, repoffs, replen;
+ int i,bn_max;
+ bits_t bits;
+ int final_flag;
+ int count_1;
+ int count_2;
+ int count_3;
+ int method;
+ unsigned mask;
+ __u8 *code_bln; /* bitlengths of char, tokens and rep codes [0x150] */
+ huf_rd_t *huf1,*huf2; /* tables for huffman decoding */
+ char *work_mem;
+
+ sq_rdi(&bits,pin,lin);
+ p=(__u8*)pout;pend=p+lout;
+ if((sq_rdn(&bits,8)!='S')||(sq_rdn(&bits,8)!='Q'))
+ { printk(KERN_ERR "DMSDOS: Data are not SQ compressed\n");
+ return(0);
+ };
+ u=sq_rdn(&bits,16);
+ LOG_DECOMP("DMSDOS: sq_dec: version %X\n",u);
+ /* allocating work memory */
+ work_mem=(char*)MALLOC(0x150+HUF_RD_SIZE(MAX_SPDA_LEN)+HUF_RD_SIZE(256));
+ if(!work_mem)
+ {printk(KERN_ERR "DMSDOS: sq_dec: out of memory!\n");return 0;};
+ code_bln=work_mem;
+ huf1=(huf_rd_t*)(work_mem+0x150);
+ huf2=(huf_rd_t*)(work_mem+0x150+HUF_RD_SIZE(MAX_SPDA_LEN));
+ do
+ { final_flag=sq_rdn(&bits,1); LOG_DECOMP("DMSDOS: final_flag %d\n",final_flag);
+ method=sq_rdn(&bits,2); LOG_DECOMP("DMSDOS: method %d\n",method);
+ switch(method)
+ {
+ case 0:
+ printk(KERN_NOTICE "DMSDOS: dec_sq: submethod not tested - raw read\n");
+ /* go to byte boundary */
+ /* read 16 bits - count of raw bytes */
+ sq_rdn(&bits,(8-bits.pb)&7);
+ replen=sq_rdn(&bits,16);
+ if (replen+sq_rdn(&bits,16)!=0xFFFF) {FREE(work_mem);return 0;};
+ r=(__u8*)bits.pd-(32-bits.pb)/8;
+ if(r+replen>(__u8*)bits.pe) {FREE(work_mem);return 0;};
+ if(p+replen>pend) {FREE(work_mem);return 0;};
+ M_MOVSB(p,r,replen); /* copy/repeat function */
+ if((unsigned)r&1) bits.pb=32;
+ else {bits.pb=32+8;r--;};
+#if 0
+ /* some compilers seem to be confused by this (???) */
+ bits.pd=(typeof(bits.pd))r;
+#else
+ bits.pd=(__u16*)r;
+#endif
+ break;
+
+ case 1:
+ printk(KERN_NOTICE "DMSDOS: sq_dec: submethod not tested - fixed huffman\n");
+ /* 0x90*8 0x70*9 0x18*7 8*8 sqt_repbln sqt_repbas 0x101 0x120h */
+ /* 0x1E*5 offset sqt_offbln sqt_offbas 0 0x1Eh */
+ bn_max=9;
+ count_1=0x120;
+ count_2=0x20;
+ i=0;
+ while(i<0x90) code_bln[i++]=8;
+ while(i<0x100) code_bln[i++]=9;
+ while(i<0x118) code_bln[i++]=7;
+ while(i<0x120) code_bln[i++]=8;
+ while(i<0x140) code_bln[i++]=5;
+ goto case_1_cont;
+
+ case 2:
+ LOG_DECOMP("DMSDOS: sq_dec: submethod huffman\n");
+ count_1=sq_rdn(&bits,5)+0x101;
+ LOG_DECOMP("DMSDOS: count_1 %d\n",count_1);
+ if(count_1>0x11E)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff count_1 too big\n");
+ FREE(work_mem);return(0);
+ };
+ count_2=sq_rdn(&bits,5)+1;
+ LOG_DECOMP("DMSDOS: count_2 %d\n",count_2);
+ if(count_2>0x1E)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff count_2 too big\n");
+ FREE(work_mem);return(0);
+ };
+ count_3=sq_rdn(&bits,4)+4;
+ LOG_DECOMP("DMSDOS: count_3 %d\n",count_3);
+ bn_max=0;
+ for(i=0;i<count_3;i++)
+ { u=sq_rdn(&bits,3);
+ code_bln[code_index_1[i]]=u;
+ if(u>bn_max)bn_max=u;
+ };
+ while(i<19) code_bln[code_index_1[i++]]=0;code_bln[19]=0xFF;
+ i=sq_rdhufi(huf1,19,bn_max,code_bln);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in helper table\n");
+ FREE(work_mem);return 0;
+ };
+ mask=sq_bmsk[huf1->bn]; u1=0; bn_max=0;
+ for(i=0;i<count_1+count_2;)
+ { RDN_PR(bits,u);
+ bits.pb+=huf1->chln[u&mask].ln;
+ u=huf1->chln[u&mask].ch;
+ switch(u)
+ { case 16: /* 3 to 6 repeats of last */
+ u=sq_rdn(&bits,2)+3;
+ while(u--) code_bln[i++]=u1;
+ break;
+ case 17: /* 3 to 10 repeats of 0 */
+ u=sq_rdn(&bits,3)+3; u1=0;
+ while(u--) code_bln[i++]=u1;
+ break;
+ case 18: /* 11 to 139 repeats of 0 */
+ u=sq_rdn(&bits,7)+11; u1=0;
+ while(u--) code_bln[i++]=u1;
+ break;
+ default:
+ code_bln[i++]=u;
+ u1=u;
+ if(u>bn_max) bn_max=u;
+ };
+ };
+
+ case_1_cont:
+ /* code_bln+count_1 0x96 count_2 sqt_offbln sqt_offbas */
+ code_bln[count_1+count_2]=0xFF;
+ i=sq_rdhufi(huf2,0x100,8,code_bln+count_1);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in offset table\n");
+ FREE(work_mem);return 0;
+ };
+
+ /* code_bln 0x100 count_1 sqt_repbln sqt_repbas */
+ code_bln[count_1]=0xFF;
+ i=sq_rdhufi(huf1,0x100,bn_max,code_bln);
+ if(!i)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff error in char and len table\n");
+ FREE(work_mem);return 0;
+ };
+
+ while((u=sq_rdh(&bits,huf1,&p,pend))!=0)
+ { if(u==OUT_OVER){u=sq_rdh1(&bits,huf1)-0x100;break;};
+ u--;
+ replen=sqt_repbas[u]+sq_rdn(&bits,sqt_repbln[u]);
+ u=sq_rdh1(&bits,huf2);
+ repoffs=sqt_offbas[u]+sq_rdn(&bits,sqt_offbln[u]);
+ if(!repoffs)
+ { printk("DMSDOS: sq_dec: bad repoffs !!!\n\n");
+ FREE(work_mem);return(0);
+ };
+ if ((__u8*)pout+repoffs>p)
+ { repoffs=p-(__u8*)pout;
+ printk(KERN_INFO "DMSDOS: sq_dec: huff offset UNDER\n");
+ };
+ if (p+replen>pend)
+ { replen=pend-p;
+ printk(KERN_INFO "DMSDOS: sq_dec: huff offset OVER\n");
+ };
+ r=p-repoffs; M_MOVSB(p,r,replen); /* copy/repeat function */
+ };
+
+ if(u)
+ { printk(KERN_INFO "DMSDOS: sq_dec: huff BAD last token %x\n",u);
+ FREE(work_mem);return 0;
+ };
+ break;
+
+ case 3:
+ printk(KERN_INFO "DMSDOS: sq_dec: unknown submethod - 3\n");
+ FREE(work_mem);
+ return(0);
+ };
+ } while((!final_flag)&&(p<pend));
+ FREE(work_mem);
+ return(p-(__u8*)pout);
+};
+
+#endif /* __KERNEL__||__DMSDOS_LIB__ */
+
+/*==============================================================*/
+/* bitstream writting */
+
+/* initializes writting to bitstream */
+INLINE void sq_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+/* writes n<=16 bits to bitstream *pbits */
+INLINE void sq_wrn(bits_t *pbits,unsigned u, int n)
+{
+ pbits->buf|=(__u32)(u&sq_bmsk[n])<<pbits->pb;
+ if((pbits->pb+=n)>=16)
+ {
+ if(pbits->pd<pbits->pe)
+ *(pbits->pd++)=cpu_to_le16((__u16)pbits->buf);
+ else if(pbits->pd==pbits->pe) pbits->pd++; /* output overflow */
+ pbits->buf>>=16;
+ pbits->pb-=16;
+ }
+}
+
+/*==============================================================*/
+/* huffman encoding */
+
+typedef long int count_t;
+
+typedef
+ struct {
+ count_t cn;
+ unsigned ch;
+ } ch_tab_t;
+
+typedef
+ struct {
+ __u16 cod; /* character code */
+ __u16 ln; /* character len */
+ } huf_wr_t;
+
+/*** Generation of character codes ***/
+
+void sq_hsort1(ch_tab_t* ch_tab,int ch_num,int cl, ch_tab_t a)
+{
+ /* a eax */
+ /* cl di */
+ int ch; /* bp */
+ ch_tab_t b; /* ecx */
+ ch_tab_t c; /* esi */
+ ch=cl*2;
+ while(ch<ch_num)
+ {
+ b=ch_tab[ch-1];c=ch_tab[ch];
+ if((c.cn<b.cn)||((c.cn==b.cn)&&(c.ch<=b.ch))) {b=c;ch++;};
+ if((b.cn>a.cn)||((b.cn==a.cn)&&(b.ch>=a.ch))) {ch_tab[cl-1]=a;return;}
+ ch_tab[cl-1]=b;cl=ch;ch*=2;
+ };
+ if(ch==ch_num)
+ {
+ b=ch_tab[ch-1];
+ if((b.cn<a.cn)||((b.cn==a.cn)&&(b.ch<a.ch)))
+ {ch_tab[cl-1]=b;cl=ch;ch*=2;};
+ };
+ ch_tab[cl-1]=a;
+};
+
+int sq_huffman(count_t* ch_cn,__u8* ch_blen,unsigned* ch_blcn,int cod_num,ch_tab_t *ch_tab)
+{
+ int i,ch_num,cl;
+ ch_tab_t a;
+ ch_tab_t b;
+
+ redo_reduced:
+ ch_num=0;
+ for(i=0;i<cod_num;i++) if(ch_cn[i])
+ {ch_tab[ch_num].cn=ch_cn[i];ch_tab[ch_num].ch=i|0x800;ch_num++;};
+ ch_tab[ch_num].ch=0;
+ if(ch_num==0)
+ {
+ ch_tab[0].ch=0x800;
+ ch_tab[0].cn=1;
+ ch_num++;
+ }
+ if(ch_num==1)
+ {
+ ch_tab[ch_num]=ch_tab[ch_num-1];
+ ch_tab[ch_num].ch&=0x801;
+ ch_tab[ch_num].ch^=1;ch_num++;
+ };
+ cl=ch_num/2;
+ while(cl>1)
+ {
+ sq_hsort1(ch_tab,ch_num,cl,ch_tab[cl-1]);
+ cl--;
+ };
+
+ cl=ch_num; a=ch_tab[0];
+ while(cl>2)
+ {
+ sq_hsort1(ch_tab,cl,1,a);
+ b=ch_tab[0];
+ a=ch_tab[--cl];
+ ch_tab[cl].ch=b.ch;
+ sq_hsort1(ch_tab,cl,1,a);
+ a=ch_tab[0];
+ ch_tab[cl].cn=a.ch;
+ if(a.ch<=b.ch) {a.ch=b.ch;};
+ a.ch=(a.ch&0x7800)+cl+0x800;
+ if(a.ch>=0x8000u)
+ {
+ printk("DMSDOS: sq_huffman: Problems with number of bits\n");
+ for(i=0;i<cod_num;i++) ch_cn[i]=(ch_cn[i]+1)>>1;
+ goto redo_reduced;
+ };
+ a.ch+=0x8000u;
+ a.cn+=b.cn;
+ };
+ ch_tab[1].cn=a.ch;
+
+ {
+ int st[MAX_BITS+2];
+ int k=0,l=1,blen=0;
+
+ memset(ch_blcn,0,sizeof(ch_blcn[0])*(MAX_BITS+1));
+ memset(ch_blen,0,sizeof(ch_blen[0])*cod_num);
+ while(1)
+ {
+ do
+ {
+ k|=0x4000;
+ do
+ {
+ st[blen]=k;
+ blen++;
+ k=l&0x7FF;
+ l=ch_tab[k].ch&0x87FF;
+ }while(l&0x8000);
+ ch_blen[l]=blen;
+ ch_blcn[blen]++;
+ l=ch_tab[k].cn&0x87FF;
+ }while(l&0x8000);
+ do
+ {
+ ch_blen[l]=blen;
+ ch_blcn[blen]++;
+ do
+ {
+ if(!--blen) goto code_done;
+ k=st[blen];
+ }while(k&0x4000);
+ l=ch_tab[k].cn&0x87FF;
+ }while(!(l&0x8000));
+ };
+ code_done:;
+ };
+ return(0);
+};
+
+INLINE int sq_wrhufi(huf_wr_t *phuf, __u8* ch_blen,
+ unsigned* ch_blencn,int cod_num)
+{
+ unsigned i,u,t,blen;
+ u=0;
+ for(i=0;i<=MAX_BITS;i++) {u<<=1;t=u;u+=ch_blencn[i];ch_blencn[i]=t;};
+ if(u!=1u<<MAX_BITS) return(1);
+ for(i=0;i<cod_num;i++)
+ {
+ if((blen=ch_blen[i])!=0)
+ {
+ phuf[i].cod=swap_bits_order_16(ch_blencn[blen]++)>>(16-blen);
+ phuf[i].ln=blen;
+ };
+ };
+ return(0);
+};
+
+INLINE void sq_wrh(bits_t *pbits,const huf_wr_t *phuf,const unsigned ch)
+{
+ sq_wrn(pbits,phuf[ch].cod,phuf[ch].ln);
+};
+
+
+/*==============================================================*/
+/* SQ compression */
+
+typedef __u8* hash_t;
+
+#define TK_END 0x00
+#define TK_CHRS 0xF0
+#define TKWR_CHRS(p,v) {if(v<15) *(p++)=TK_CHRS+(__u8)v;\
+ else {*(p++)=TK_CHRS+15;C_ST_u16(p,v);};}
+
+#define HASH_TAB_ENT (1<<10)
+#define HASH_HIST_ENT (1<<12)
+#define MAX_OFFS 32768
+#define MAX_OFFS_BLN8 1024
+#define MIN_REP 3
+#define MAX_REP 258
+
+/* definition of data hash function, it can use max 3 chars */
+INLINE unsigned sq_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<2)^((__u16)p[1]<<4)^(__u16)p[2])&(HASH_TAB_ENT-1);
+};
+
+/* store hash of chars at *p in hash_tab, previous occurence is stored */
+/* in hash_hist, which is indexed by next hash positions */
+/* returns previous occurence of same hash as is hash of chars at *p */
+INLINE hash_t sq_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hist_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sq_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hist_mask))=hash_cur;
+ return(hash_cur);
+};
+
+/* binary seeking of nearest less or equal base value */
+INLINE unsigned find_token(int token,const unsigned *tab_val,int tab_len)
+{
+ int half;
+ int beg=0;
+ do
+ {
+ half=tab_len>>1;
+ if(tab_val[beg+half]>token) tab_len=half;
+ else {beg+=half;tab_len-=half;};
+ }
+ while(tab_len>1);
+ return beg;
+};
+
+/* finds repetitions in *pin and writes intermediate code to *pout */
+unsigned sq_complz(void* pin,int lin,void* pout,int lout,int flg,
+ count_t* ch_cn, count_t* offs_cn, void *work_mem)
+{
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab; /* [HASH_TAB_ENT] */
+ /* pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist; /* [HASH_HIST_ENT] */
+ /* previous occurences of hash, index actual pointer&hist_mask */
+ unsigned hist_mask=(HASH_HIST_ENT-1); /* mask for index into hash_hist */
+ __u8 *pi, *po, *pc, *pd, *pend, *poend;
+ hash_t hash_cur;
+ unsigned cn;
+ unsigned max_match, match, token;
+ int try_cn;
+ hash_t max_hash=NULL;
+
+ int delay_count=0; /* test next # characters for better match */
+ int delay_cn;
+ int delay_best;
+
+ hash_tab/*[HASH_TAB_ENT]*/=(hash_t*)work_mem;
+ hash_hist/*[HASH_HIST_ENT]*/=(hash_t*)work_mem+HASH_TAB_ENT;
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ if(flg&0x4000)
+ {
+ /* maximal compression */
+ delay_count=2;
+ try_count*=4;
+ hash_skiped*=4;
+ };
+
+ pi=(__u8*)pin;
+ po=(__u8*)pout;
+ if(!lin) return(0);
+ pend=pi+(lin-1);
+ if(lout<0x20) return(0); /* some minimal space for lz interform buffer */
+ poend=po+(lout-0x20);
+ for(cn=0;cn<HASH_TAB_ENT;cn++) hash_tab[cn]=pend; /* none ocurence of hash */
+ for(cn=0;cn<=hist_mask;cn++) hash_hist[cn]=pend; /* should not be needed */
+ pend--; /* last two bytes cannot be hashed */
+ cn=0;
+ while(pi<pend)
+ {
+ hash_cur=sq_newhash(pi,hash_tab,hash_hist,hist_mask);
+ /* goto single_char; */ /* to by pass LZ for tests */
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=MIN_REP-1;
+ do{
+ if(pi-hash_cur>MAX_OFFS) break; /* longer offsets are not allowed */
+ if((hash_cur[max_match]==pi[max_match])&&
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1]))
+ /* pi[2]=hash_cur[2] from hash function */
+ {
+ match=pend-pi; /* length of rest of data */
+ if(match>MAX_REP-2) match=MAX_REP-2;
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match); /* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match)&&((match>3)||(pi-hash_cur<=MAX_OFFS_BLN8)))
+ {
+ max_hash=hash_cur; /* found maximal hash */
+ max_match=match;
+ if(match==MAX_REP)break; /* longer match cannot be encoded */
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hist_mask])<pc));
+ if(max_match<MIN_REP) goto single_char;
+
+ /* tests if better matchs on next characters */
+ delay_cn=0;
+ if(delay_count)
+ {
+ delay_best=0;
+ while((delay_cn<delay_count)&&(pi+max_match<pend)&&
+ (max_match<0x100))
+ {
+ pi++;delay_cn++;
+ hash_cur=sq_newhash(pi,hash_tab,hash_hist,hist_mask);
+ try_cn=try_count;
+ if (hash_cur<pi) do
+ {
+ if(pi-hash_cur>MAX_OFFS) break; /* longer offsets are not allowed */
+ if((hash_cur[max_match]==pi[max_match])&&
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur!=max_hash+delay_cn-delay_best))
+ { /* do not test actual max match */
+ match=pend-pi; /* length of rest of data */
+ if(match>MAX_REP-2) match=MAX_REP-2;
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match+delay_cn)&&((match>3)||(pi-hash_cur<=MAX_OFFS_BLN8)))
+ {
+ max_hash=hash_cur;max_match=match; /* find maximal hash */
+ delay_best=delay_cn;
+ if(match==MAX_REP)break;/* longer match cannot be encoded */
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hist_mask])<pc));
+ };
+ if(delay_best)
+ LOG_DECOMP("DMSDOS: sq_complz: Delayed match %i is better\n",delay_best);
+ pi-=delay_cn;
+ delay_cn-=delay_best;
+ while(delay_best)
+ {
+ delay_best--;
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) return(0);};
+ }
+ };
+
+ if(cn) TKWR_CHRS(po,cn);
+ cn=pi-max_hash; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+
+ /* store information about match len into *po */
+ token=find_token(max_match,sqt_repbas,29); /* for max match */
+ ch_cn[token+0x101]++;
+ *po++=token+1;
+ if(sqt_repbln[token]) *po++=max_match-sqt_repbas[token];
+ /* store information about match offset into *po */
+ token=find_token(cn,sqt_offbas,30); /* for history offset */
+ offs_cn[token]++;
+ *po++=token;
+ if(sqt_offbln[token])
+ {
+ if(sqt_offbln[token]<=8)
+ *po++=cn-sqt_offbas[token];
+ else
+ C_ST_u16(po,cn-sqt_offbas[token]);
+ };
+ if(hash_skiped&&(pi<pend))
+ {
+ max_match-=delay_cn;
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sq_newhash(pi++,hash_tab,hash_hist,hist_mask);
+ };
+ if(poend<po) return(0);
+ cn=0;
+ continue;
+ single_char:
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) return(0);};
+ };
+
+ pend+=2;
+ while(pi!=pend) {ch_cn[*(pi++)]++;cn++;};
+ if(cn)
+ {
+ if(cn>=0x8000u) {TKWR_CHRS(po,0x8000u);cn-=0x8000u;};
+ TKWR_CHRS(po,cn);
+ };
+
+ ch_cn[TK_END+0x100]++;
+ *po++=TK_END;
+ return(po-(__u8*)pout);
+};
+
+/*** Main compression routine ***/
+
+__u16 sq_comp_rat_tab[]=
+ {0x7F9,0x7F9,0x621,0x625,
+ 0x665,0x669,0x6E9,0x6ED,
+ 0x7D1,0x7D9,0x6E9,0x47D9,
+ 0x46E9}; /* compression ratio to seek lengths */
+
+typedef
+ struct{
+ count_t ch_cn[0x120]; /* counts of characters and rep length codes */
+ count_t offs_cn[0x20]; /* counts of offset codes */
+ union {
+ struct {
+ __u8 ch_blen[0x120+0x20]; /* bitlengths of character codes and tokens */
+ __u8 code_buf[0x120+0x20]; /* precompressed decompression table */
+ ch_tab_t ch_tab[0x120+0x20];/* temporrary table for huffman */
+ huf_wr_t ch_huf[0x120]; /* character and token encoding table */
+ huf_wr_t offs_huf[0x20]; /* repeat encoding table */
+ } a;
+ hash_t lz_tabs[HASH_TAB_ENT+HASH_HIST_ENT];
+ } a;
+ }sq_comp_work_t;
+
+int sq_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ count_t *ch_cn; /* [0x120] counts of characters and rep length codes */
+ count_t *offs_cn; /* [0x20] counts of offset codes */
+ unsigned lz_length; /* length of intermediate reprezentation */
+ __u8* lz_pos; /* possition of intermediate data in pout */
+
+ __u8 *ch_blen; /* [0x120] bitlengths of character codes and tokens */
+ __u8 *offs_blen; /* [0x20] bitlengths of ofset codes are stored in */
+ /* end of ch_blen */
+ unsigned ch_blcn[MAX_BITS+1]; /* counts of bitlengths of chars */
+ unsigned offs_blcn[MAX_BITS+1]; /* counts of bitlengths of offs */
+ huf_wr_t *ch_huf; /* [0x120] character and token encoding table */
+ huf_wr_t *offs_huf; /* [0x20] repeat encoding table */
+
+ ch_tab_t *ch_tab; /* [0x120+0x20] temporrary table for huffman */
+ __u8 *code_buf; /* [0x120+0x20] precompressed decompression table */
+ count_t code_cn[0x20];
+ __u8 code_blen[0x20];
+ unsigned code_blcn[MAX_BITS+1];
+ unsigned code_buf_len;
+ sq_comp_work_t *work_mem=NULL;
+
+ int count_1, count_2, count_3;
+ bits_t bits;
+ int i;
+
+
+ /* allocating work memory */
+ work_mem=(sq_comp_work_t*)MALLOC(sizeof(sq_comp_work_t));
+ if(!work_mem)
+ { printk("DMSDOS: sq_comp: Not enough memory\n");
+ return 0;
+ };
+
+ ch_cn=work_mem->ch_cn;
+ offs_cn=work_mem->offs_cn;
+ ch_blen=work_mem->a.a.ch_blen;
+ code_buf=work_mem->a.a.code_buf;
+ ch_tab=work_mem->a.a.ch_tab;
+ ch_huf=work_mem->a.a.ch_huf;
+ offs_huf=work_mem->a.a.offs_huf;
+ memset(ch_cn,0,sizeof(work_mem->ch_cn));
+ memset(offs_cn,0,sizeof(work_mem->offs_cn));
+
+ /* find repetitions in input data block */
+ lz_length=sq_complz(pin,lin,pout,lout,sq_comp_rat_tab[flg&0xf],
+ ch_cn,offs_cn,work_mem->a.lz_tabs);
+ LOG_DECOMP("DMSDOS: sq_comp: lz_length %d\n",lz_length);
+ if(lz_length==0) {FREE(work_mem);return(0);};
+
+ /* move intermediate data to end of output buffer */
+ lz_pos=(__u8*)pout+lout-lz_length;
+ memmove(lz_pos,pout,lz_length);
+
+ {
+ count_1=0x11E;
+ while(!ch_cn[count_1-1])count_1--;
+ count_2=0x1E;
+ while(!offs_cn[count_2-1]&&(count_2>2))count_2--;
+ /* offset bitlengths are stored exactly after count_1 character bitlengths */
+ offs_blen=ch_blen+count_1;
+ sq_huffman(ch_cn,ch_blen,ch_blcn,count_1,ch_tab);
+ sq_huffman(offs_cn,offs_blen,offs_blcn,count_2,ch_tab);
+
+ LOG_DECOMP("DMSDOS: sq_comp: count_1 %d\n",count_1);
+ LOG_DECOMP("DMSDOS: sq_comp: count_2 %d\n",count_2);
+ }
+
+ {
+ __u8 *pi=ch_blen;
+ __u8 *pe=ch_blen+count_1+count_2;
+ __u8 *po=code_buf;
+ int code, rep;
+
+ for(code=19;code--;) code_cn[code]=0;
+ code=0;
+ while(pi<pe)
+ {
+ if(*pi==0)
+ {
+ code=0;
+ rep=1; pi++;
+ while((pi<pe)&&(rep<138)&&(*pi==0)) {pi++;rep++;}
+ if (rep<=2) {code_cn[0]+=rep; while(rep--) *po++=0;}
+ /* code 17 - 3 to 10 repeats of 0 - (3)+3 */
+ else if(rep<=10) {code_cn[17]++;*po++=17;*po++=rep-3;}
+ /* code 18 - 11 to 139 repeats of 0 - (7)+11 */
+ else {code_cn[18]++;*po++=18;*po++=rep-11;}
+ continue;
+ }
+ if(*pi==code)
+ {
+ rep=1; pi++;
+ while((pi<pe)&&(rep<6)&&(*pi==code)) {pi++;rep++;}
+ if (rep<=2) {code_cn[code]+=rep; while(rep--) *po++=code;}
+ /* code 16 - 3 to 6 repeats of last - (2)+3 */
+ else {code_cn[16]++;*po++=16;*po++=rep-3;}
+ continue;
+ }
+ code=*pi++;
+ *po++=code;
+ code_cn[code]++;
+ };
+ code_buf_len=po-code_buf;
+
+ do{
+ sq_huffman(code_cn,code_blen,code_blcn,19,ch_tab);
+ code=1;
+ /* not elegant way to limit helper table blen by 7 */
+ for(i=0;i<19;i++) if(code_blen[i]>7) code=0;
+ if(code) break;
+ for(i=0;i<19;i++) code_cn[i]=(code_cn[i]+1)>>1;
+ }while(1);
+
+ count_3=19;
+ while(!code_blen[code_index_1[count_3-1]]) count_3--;
+
+ LOG_DECOMP("DMSDOS: sq_comp: count_3 %d\n",count_3);
+ }
+
+ /* prepare output bitstream for writting */
+ sq_wri(&bits,pout,lout);
+
+ sq_wrn(&bits,'S',8);
+ sq_wrn(&bits,'Q',8);
+ sq_wrn(&bits,0,16);
+ sq_wrn(&bits,1,1); /* final flag */
+ sq_wrn(&bits,2,2); /* huffman */
+ sq_wrn(&bits,count_1-0x101,5);
+ sq_wrn(&bits,count_2-1,5);
+ sq_wrn(&bits,count_3-4,4);
+ for(i=0;i<count_3;i++) sq_wrn(&bits,code_blen[code_index_1[i]],3);
+
+ { /* compressed code table write */
+ __u8 *pi=code_buf;
+ __u8 *pe=code_buf+code_buf_len;
+ int code;
+
+ if(sq_wrhufi(ch_huf,code_blen,code_blcn,19))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 1\n");
+ FREE(work_mem);return(0);
+ };
+ while(pi<pe)
+ {
+ sq_wrh(&bits,ch_huf,code=*pi++);
+ switch(code)
+ {
+ case 16: sq_wrn(&bits,*pi++,2); break;
+ case 17: sq_wrn(&bits,*pi++,3); break;
+ case 18: sq_wrn(&bits,*pi++,7); break;
+ default: ;
+ }
+ }
+ }
+
+ { /* real data write */
+ int cod;
+ int len;
+ __u8 *pi=(__u8*)pin;
+
+ if(sq_wrhufi(ch_huf,ch_blen,ch_blcn,count_1))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 2\n");
+ FREE(work_mem);return(0);
+ };
+ if(sq_wrhufi(offs_huf,offs_blen,offs_blcn,count_2))
+ { printk("DMSDOS: sq_comp: Huffman code leakage in table 3\n");
+ FREE(work_mem);return(0);
+ };
+
+ while((cod=*(lz_pos++))!=TK_END)
+ {
+ if((__u8*)bits.pd+0x20>=lz_pos)
+ {
+ LOG_DECOMP("DMSDOS: sq_comp: Data overwrites intermediate code\n");
+ FREE(work_mem);return 0;
+ };
+ if(cod>=TK_CHRS)
+ { /* characters */
+ len=cod-TK_CHRS;
+ if(len==15) C_LD_u16(lz_pos,len);
+ while(len--) sq_wrh(&bits,ch_huf,*(pi++));
+ }else{ /* tokens */
+ sq_wrh(&bits,ch_huf,cod+0x100);
+ cod--;
+ len=sqt_repbas[cod];
+ if(sqt_repbln[cod])
+ {
+ sq_wrn(&bits,*lz_pos,sqt_repbln[cod]);
+ len+=*(lz_pos++);
+ };
+ pi+=len;
+ cod=*(lz_pos++);
+ sq_wrh(&bits,offs_huf,cod);
+ if(sqt_offbln[cod])
+ {
+ if(sqt_offbln[cod]<=8) sq_wrn(&bits,*(lz_pos++),sqt_offbln[cod]);
+ else { C_LD_u16(lz_pos,len);sq_wrn(&bits,len,sqt_offbln[cod]);};
+ };
+ };
+ }
+ sq_wrh(&bits,ch_huf,TK_END+0x100);
+ sq_wrn(&bits,0,16);
+ if(pi-(__u8*)pin!=lin)
+ {
+ printk("DMSDOS: sq_comp: ERROR: Processed only %d bytes !!!!!!\n",pi-(__u8*)pin);
+ FREE(work_mem);return 0;
+ };
+ FREE(work_mem);
+ return((__u8*)bits.pd-(__u8*)pout);
+ };
+ FREE(work_mem);return 0;
+};
+
+#endif /* DMSDOS_CONFIG_DRVSP3 */
diff --git a/src/dblspace_tables.c b/src/dblspace_tables.c
new file mode 100644
index 0000000..492e3ff
--- /dev/null
+++ b/src/dblspace_tables.c
@@ -0,0 +1,760 @@
+/*
+dblspace_tables.c
+
+DMSDOS CVF-FAT module: [d|md|bit]fat access functions.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include <asm/semaphore.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<time.h>
+#include<errno.h>
+#endif
+
+
+Acache mdfat[MDFATCACHESIZE];
+Acache dfat[DFATCACHESIZE];
+Acache bitfat[BITFATCACHESIZE];
+
+extern unsigned long int dmsdos_speedup;
+
+#ifdef __DMSDOS_LIB__
+
+/* we don't need locking in the library */
+void lock_mdfat(void) {}
+void unlock_mdfat(void) {}
+void lock_dfat(void) {}
+void unlock_dfat(void) {}
+void lock_bitfat(void) {}
+void unlock_bitfat(void) {}
+
+#else
+
+DECLARE_MUTEX(mdfat_sem); /* Must be initialized to green light */
+void lock_mdfat(void) {down(&mdfat_sem);}
+void unlock_mdfat(void) {up(&mdfat_sem);}
+
+DECLARE_MUTEX(dfat_sem); /* Must be initialized to green light */
+void lock_dfat(void) {down(&dfat_sem);}
+void unlock_dfat(void) {up(&dfat_sem);}
+
+DECLARE_MUTEX(bitfat_sem); /* Must be initialized to green light */
+void lock_bitfat(void) {down(&bitfat_sem);}
+void unlock_bitfat(void) {up(&bitfat_sem);}
+
+#endif /* else / __DMSDOS_LIB__ */
+
+int acache_get(struct super_block*sb, Acache*acache, int area, int never,
+ int cachesize)
+{ unsigned long min_time;
+ unsigned int min_acc;
+ int index;
+ int i;
+
+ LOG_ACACHE("DMSDOS: acache_get area=%d never=%d\n",area,never);
+
+ min_time=acache[0].a_time;
+ min_acc=acache[0].a_acc;
+ index=0;
+ if(never==0)
+ { min_time=acache[1].a_time;
+ min_acc=acache[1].a_acc;
+ index=1;
+ }
+ /* find area and dev in cache */
+ for(i=0;i<cachesize;++i)
+ { if( ( acache[i].a_time<min_time||
+ (acache[i].a_time==min_time&&acache[i].a_acc<min_acc)
+ ) &&never!=i)
+ { min_time=acache[i].a_time;
+ min_acc=acache[i].a_acc;
+ index=i;
+ }
+ if(acache[i].a_buffer!=NULL&&area==acache[i].a_area&&sb->s_dev==acache[i].a_sb->s_dev)
+ { /* found */
+ if(acache[i].a_time==CURRENT_TIME)++acache[i].a_acc;
+ else
+ { acache[i].a_time=CURRENT_TIME;
+ acache[i].a_acc=0;
+ }
+ index=i;
+ return index;
+ }
+ }
+ /* index = least recently used entry number */
+ if(acache[index].a_buffer!=NULL)
+ raw_brelse(acache[index].a_sb,acache[index].a_buffer);
+ LOG_ACACHE("DMSDOS: acache_get: reading area %d\n",area);
+ if((acache[index].a_buffer=raw_bread(sb,area))==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read acache area=%d\n",area);
+ return -EIO;
+ }
+ acache[index].a_area=area;
+ acache[index].a_time=CURRENT_TIME;
+ acache[index].a_acc=0;
+ acache[index].a_sb=sb;
+ return index;
+}
+
+void u_dumpcache(Acache*c)
+{ printk(KERN_INFO "area=%d time=%ld acc=%d buffer=%p dev=0x%x\n",c->a_area,c->a_time,
+ c->a_acc,c->a_buffer,
+ /* check validity of sb before dereferencing to s_dev :) */
+ (c->a_buffer!=NULL&&c->a_sb!=NULL)?c->a_sb->s_dev:0);
+}
+
+void dumpcache(void)
+{ int i;
+
+ printk(KERN_INFO "DMSDOS: mdfat cache:\n");
+ for(i=0;i<MDFATCACHESIZE;++i)u_dumpcache(&(mdfat[i]));
+ printk(KERN_INFO "DMSDOS: dfat cache:\n");
+ for(i=0;i<DFATCACHESIZE;++i)u_dumpcache(&(dfat[i]));
+ printk(KERN_INFO "DMSDOS: bitfat cache:\n");
+ for(i=0;i<BITFATCACHESIZE;++i)u_dumpcache(&(bitfat[i]));
+}
+
+#ifndef __DMSDOS_LIB__
+void get_memory_usage_acache(int*size,int*max)
+{ int i;
+ int used=0;
+
+ for(i=0;i<MDFATCACHESIZE;++i)if(mdfat[i].a_buffer)++used;
+ for(i=0;i<DFATCACHESIZE;++i)if(dfat[i].a_buffer)++used;
+ for(i=0;i<BITFATCACHESIZE;++i)if(bitfat[i].a_buffer)++used;
+
+ if(size)*size=used*SECTOR_SIZE;
+ if(max)*max=(MDFATCACHESIZE+DFATCACHESIZE+BITFATCACHESIZE)*SECTOR_SIZE;
+}
+
+void u_free_idle_cache(Acache*c)
+{ if(c->a_buffer!=NULL&&c->a_time-CURRENT_TIME>MAX_CACHE_TIME)
+ { raw_brelse(c->a_sb,c->a_buffer);
+ c->a_buffer=NULL;
+ c->a_time=0;
+ c->a_acc=0;
+ }
+}
+
+void free_idle_cache(void)
+{ int i;
+
+ lock_mdfat();
+ for(i=0;i<MDFATCACHESIZE;++i)u_free_idle_cache(&(mdfat[i]));
+ unlock_mdfat();
+ lock_dfat();
+ for(i=0;i<DFATCACHESIZE;++i)u_free_idle_cache(&(dfat[i]));
+ unlock_dfat();
+ lock_bitfat();
+ for(i=0;i<BITFATCACHESIZE;++i)u_free_idle_cache(&(bitfat[i]));
+ unlock_bitfat();
+
+ /* handle cluster cache */
+ free_idle_ccache();
+}
+#endif
+
+int dbl_mdfat_value(struct super_block* sb,int clusternr,
+ Mdfat_entry*new,
+ Mdfat_entry*mde)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_STAC
+ int i;
+ int merk_i2;
+ int nr_of_bytes;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ int res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ unsigned char mdfat_raw_field[5];
+#endif
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal mdfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ goto fake_mde;
+ }
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC
+ case STAC3:
+ case STAC4:
+ if(dblsb->s_16bitfat)pos=clusternr*4;
+ else pos=clusternr*3;
+ area=pos/SECTOR_SIZE;
+ offset=pos%SECTOR_SIZE;
+ area=(area/6)*9+(area%6)+3+dblsb->s_fatstart; /* yes!!! */
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ nr_of_bytes=3;
+ if(dblsb->s_16bitfat)nr_of_bytes=4;
+
+ /* read 2nd sector if necessary */
+ if(offset+nr_of_bytes-1>511)
+ { merk_i2=acache_get(sb,mdfat,area+1,merk_i,MDFATCACHESIZE);
+ if(merk_i2<0){++area;goto mdfat_error;}
+ }
+ else merk_i2=merk_i;
+
+ /* copy data in mdfat_raw_field (nr_of_bytes byte) */
+ mdfat_raw_field[3]=0; /* in case only 3 bytes to read */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat_raw_field[i]=mdfat[merk_i].a_buffer->b_data[i+offset];
+ else
+ mdfat_raw_field[i]=mdfat[merk_i2].a_buffer->b_data[i+offset-512];
+ }
+
+ /* setup mde */
+ mde->sector_minus_1=CHS(mdfat_raw_field)+((mdfat_raw_field[3]&0x3f)<<16)-1;
+ mde->unknown=(mdfat_raw_field[2])&0xf0; /* same as flags here */
+ mde->size_lo_minus_1=(mdfat_raw_field[2]&0xf)+((mdfat_raw_field[3]>>2)&0x30);
+ mde->size_hi_minus_1=mde->size_lo_minus_1; /* for compatibility */
+ mde->flags=(mdfat_raw_field[2])&0xf0; /* unshifted like in sd4_c.cc */
+ /* set used and compressed bits in flags (for compatibility) */
+ /* Hi Pavel: Is there a bug here? sector seems to be sometimes 1 for
+ empty stacker mdfat slots. I think it should be 0 ? This showed up in
+ the new fs checking code as dead sectors. Hmm.... Should we tolerate
+ 0 and 1 here ? But then the stacker fs check complains later... */
+ /* Hi Frank, they are normal deleted clusters, stored for DOS undel.
+ They should be deleted automaticaly, when free space becomes low.
+ At this moment is best to ignore them in your fat simple check,
+ they are counted by stac simple check */
+ if(mde->sector_minus_1+1)mde->flags|=2; /*set 'used' flag*/
+ switch(mde->flags&0xa0)
+ { case 0x80:
+ case 0x00:
+ if(mde->size_lo_minus_1+1==dblsb->s_sectperclust)
+ mde->flags|=1;
+ break;
+ case 0x20:
+ mde->flags|=1;
+ break;
+ default:
+ /* correct length for compatibility */
+ mde->size_hi_minus_1=dblsb->s_sectperclust-1;
+ }
+
+ LOG_MDFAT("DMSDOS: dbl_mdfat_value: cluster %u\n",
+ (unsigned)clusternr);
+ LOG_MDFAT(" sector %u len %u flags 0x%X raw 0x%02X 0x%02X 0x%02X 0x%02X\n",
+ (unsigned)(mde->sector_minus_1+1),(unsigned)(mde->size_lo_minus_1+1),
+ (unsigned)mde->flags,
+ (unsigned)(mdfat_raw_field[0]),(unsigned)(mdfat_raw_field[1]),
+ (unsigned)(mdfat_raw_field[2]),(unsigned)(mdfat_raw_field[3]));
+ LOG_MDFAT(" pos %u area %u offset %u\n",
+ (unsigned)pos,(unsigned)area,(unsigned)offset);
+
+ /* if new!=NULL, setup nr_of_bytes byte from new in mdfat_raw_field */
+ if(new)
+ { mdfat_raw_field[0]=(new->sector_minus_1+1);
+ /* unknown bits ignored */
+ mdfat_raw_field[1]=(new->sector_minus_1+1)>>8;
+ mdfat_raw_field[3]=((new->sector_minus_1+1)>>16)&0x3f;
+ mdfat_raw_field[3]|=(new->size_lo_minus_1<<2)&0xc0;
+ mdfat_raw_field[2]=new->size_lo_minus_1&0x0f;
+ mdfat_raw_field[2]|=new->flags&0xf0; /* unshifted like in sd4_c.cc */
+
+ /* write back mdfat_raw_entry */
+ for(i=0;i<nr_of_bytes;++i)
+ { if(i+offset<512)
+ mdfat[merk_i].a_buffer->b_data[i+offset]=mdfat_raw_field[i];
+ else
+ mdfat[merk_i2].a_buffer->b_data[i+offset-512]=mdfat_raw_field[i];
+ }
+
+ /* mark buffer dirty (both if necessary) */
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,mdfat[merk_i2].a_buffer,1);
+ /* write second mdfat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ mdfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ mdfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second mdfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,mdfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ case DBLSP:
+ case DRVSP:
+ pos=(dblsb->s_dcluster+clusternr)*4+512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+ res=CHL(pp);
+ mde->sector_minus_1=res&0x1fffff;
+ mde->unknown=(res&0x00200000)>>21;
+ mde->size_lo_minus_1=(res&0x03c00000)>>22;
+ mde->size_hi_minus_1=(res&0x3c000000)>>26;
+ mde->flags=((res&0xC0000000)>>30)&3;
+ if(new)
+ { res=new->sector_minus_1;res&=0x1fffff;
+ /* unknown bit ??? don't know... set to zero */
+ res|=new->size_lo_minus_1<<22;res&=0x03ffffff;
+ res|=new->size_hi_minus_1<<26;res&=0x3fffffff;
+ res|=new->flags<<30;
+ pp[0]=res;pp[1]=res>>8;pp[2]=res>>16;pp[3]=res>>24;
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+ unlock_mdfat();
+ return 0;
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ case DRVSP3:
+ pos=(dblsb->s_dcluster+clusternr)*5
+ +((dblsb->s_dcluster+clusternr)/102)*2
+ +512*dblsb->s_mdfatstart;
+ area=pos/SECTOR_SIZE;
+ offset=(pos%SECTOR_SIZE);
+ lock_mdfat();
+ merk_i=acache_get(sb,mdfat,area,-1,MDFATCACHESIZE);
+ if(merk_i<0)goto mdfat_error;
+ pp=&(mdfat[merk_i].a_buffer->b_data[offset]);
+
+ /* setup mde */
+ mde->sector_minus_1=CHL(pp)&0xffffff;
+ mde->unknown=(CHL(pp)&0x3000000)>>24;
+ mde->size_lo_minus_1=(pp[3]>>2)&0x3f;
+ mde->size_hi_minus_1=pp[4]&0x3f;
+ mde->flags=(pp[4]>>6)&3;
+
+ /* if new!=NULL, setup 5 byte from new in pp */
+ if(new)
+ { pp[0]=new->sector_minus_1/*&0xffffff ??? */;
+ pp[1]=new->sector_minus_1>>8;
+ pp[2]=new->sector_minus_1>>16;
+ /*pp[3]=(new->sector_minus_1>>24)&3; ??? */
+ pp[3]=new->unknown&3; /* we need the fragmented bit here :) */
+ pp[3]|=new->size_lo_minus_1<<2;
+ pp[4]=new->size_hi_minus_1&0x3f;
+ pp[4]|=new->flags<<6;
+
+ raw_mark_buffer_dirty(sb,mdfat[merk_i].a_buffer,1);
+ }
+
+ unlock_mdfat();
+ return 0;
+#endif
+ } /* end switch(dblsb->s_cvf_version) */
+
+ printk(KERN_ERR "DMSDOS: dbl_mdfat_value: unknown version?? This is a bug.\n");
+ goto fake_mde;
+
+ mdfat_error:
+ unlock_mdfat();
+ printk(KERN_ERR "DMSDOS: unable to read mdfat area %d for cluster %d\n",area,
+ clusternr);
+
+ fake_mde:
+ mde->sector_minus_1=0;
+ mde->unknown=0;
+ mde->size_lo_minus_1=0;
+ mde->size_hi_minus_1=0;
+ mde->flags=0;
+
+ return -1;
+}
+
+int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr)
+{ Mdfat_entry mde;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr==0)return dblsb->s_rootdir;
+ if(dbl_mdfat_value(sb,clusternr,NULL,&mde)==0)
+ return mde.sector_minus_1+1;
+ return -1;
+}
+
+int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+ int res;
+ int newval;
+ int merk_i2;
+ int offset2;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(clusternr<2||clusternr>dblsb->s_max_cluster2)
+ { printk(KERN_ERR "DMSDOS: illegal dfat access (cluster=%d max_cluster2=%d)\n",
+ clusternr,dblsb->s_max_cluster2);
+ return -1;
+ }
+
+ pos= dblsb->s_16bitfat ? clusternr<<1/**2*/ : (clusternr*3)>>1/*/2*/;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+
+ /* adapt area for stacker */
+ if(dblsb->s_cvf_version>=STAC3)area=(area/3)*9+(area%3);
+
+ area+=dblsb->s_fatstart;
+
+ lock_dfat();
+
+ merk_i=acache_get(sb,dfat,area,-1,DFATCACHESIZE);
+ if(merk_i<0)
+ {
+ dfat_error:
+ unlock_dfat();
+ printk(KERN_ERR "DMSDOS: unable to read dfat area %d for cluster %d\n",area,
+ clusternr);
+ return -1;
+ }
+ if(offset==511)
+ { /* overlap, must read following sector also */
+ merk_i2=acache_get(sb,dfat,area+1,merk_i,DFATCACHESIZE);
+ if(merk_i2<0){++area;goto dfat_error;}
+ offset2=0;
+ }
+ else
+ { /* normal */
+ merk_i2=merk_i;
+ offset2=offset+1;
+ }
+
+LOG_DFAT("DMSDOS: FAT lookup: area=%d merk_i=%d merk_i2=%d offset=%d offset2=%d\n",
+ area,merk_i,merk_i2,offset,offset2);
+LOG_DFAT("DMSDOS: FAT lookup: cluster=%d value(low=%d high=%d)\n",
+ clusternr,
+ dfat[merk_i].a_buffer->b_data[offset],
+ dfat[merk_i2].a_buffer->b_data[offset2]);
+
+ res=dfat[merk_i].a_buffer->b_data[offset];
+ res&=0xff; /* grmpf... sign problems !!!! this is brutal but it works */
+ res|=dfat[merk_i2].a_buffer->b_data[offset2]<<8;
+ res&=0xffff;
+
+ if(new)
+ { if(dblsb->s_16bitfat) newval=*new&0xFFFF;
+ else
+ { if(clusternr&1) newval=(res&0xF) | ((*new&0xFFF)<<4);
+ else newval=(res&0xF000) | (*new&0xFFF);
+ }
+ dfat[merk_i].a_buffer->b_data[offset]=newval;
+ dfat[merk_i2].a_buffer->b_data[offset2]=newval>>8;
+ raw_mark_buffer_dirty(sb,dfat[merk_i].a_buffer,1);
+ if(merk_i!=merk_i2)
+ raw_mark_buffer_dirty(sb,dfat[merk_i2].a_buffer,1);
+ /* write second fat if it exists */
+ if(dblsb->s_2nd_fat_offset)
+ { struct buffer_head* bh;
+
+ bh=raw_getblk(sb,
+ dfat[merk_i].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ if(merk_i!=merk_i2)
+ { bh=raw_getblk(sb,
+ dfat[merk_i2].a_area+dblsb->s_2nd_fat_offset);
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: unable to read second dfat\n");
+ goto give_up;
+ }
+ memcpy(bh->b_data,dfat[merk_i2].a_buffer->b_data,SECTOR_SIZE);
+ raw_set_uptodate(sb,bh,1);
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ give_up: ;
+ }
+ unlock_dfat();
+ if(dblsb->s_16bitfat)return res>=0xFFF7 ? -1 : res;
+ if(clusternr&1)res>>=4; else res&=0xfff;
+ return res>=0xff7 ? -1 : res;
+}
+
+int dbl_bitfat_value(struct super_block*sb,int sectornr,int*new)
+{ int area;
+ int pos;
+ int offset;
+ int merk_i;
+#ifdef DMSDOS_CONFIG_DBL
+ unsigned char * pp;
+ int newval;
+#endif
+ int bitmask;
+#ifdef DMSDOS_CONFIG_STAC
+ int shiftval;
+#endif
+ int res;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(sectornr<dblsb->s_datastart)return -1;
+ if(sectornr>dblsb->s_dataend)return -1;
+
+ switch(dblsb->s_cvf_version)
+ {
+#ifdef DMSDOS_CONFIG_STAC3
+ case STAC3:
+ pos=((sectornr-dblsb->s_datastart)>>3);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&7);
+ bitmask=0x1;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ case STAC4:
+ pos=((sectornr-dblsb->s_datastart)>>2);
+ offset=pos&(SECTOR_SIZE-1)/*pos%SECTOR_SIZE*/;
+ area=(pos>>SECTOR_BITS)/*pos/SECTOR_SIZE*/+dblsb->s_mdfatstart; /* misused */
+ shiftval=((sectornr-dblsb->s_datastart)&3)<<1/**2*/;
+ bitmask=0x3;
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ res=(bitfat[merk_i].a_buffer->b_data[offset]>>shiftval)&bitmask;
+ if(new)
+ { if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ bitfat[merk_i].a_buffer->b_data[offset]&=~(bitmask<<shiftval);
+ bitfat[merk_i].a_buffer->b_data[offset]|=(*new&bitmask)<<shiftval;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ unlock_bitfat();
+ return res;
+#endif
+#ifdef DMSDOS_CONFIG_DBL
+ case DBLSP:
+ case DRVSP:
+ case DRVSP3:
+ pos=SECTOR_SIZE+((sectornr-dblsb->s_datastart)>>4)*2;
+ area=pos>>SECTOR_BITS/*pos/SECTOR_SIZE*/;
+ offset=pos&(SECTOR_SIZE-1)/*(pos%SECTOR_SIZE)*/;
+ bitmask=0x8000>>((sectornr-dblsb->s_datastart)&15);
+ lock_bitfat();
+ merk_i=acache_get(sb,bitfat,area,-1,BITFATCACHESIZE);
+ if(merk_i<0)goto bitfat_error;
+ pp=&(bitfat[merk_i].a_buffer->b_data[offset]);
+ res=CHS(pp);
+ if(new)
+ { newval= (*new) ? (res|bitmask) : (res&~bitmask);
+ pp[0]=newval;pp[1]=newval>>8;
+ raw_mark_buffer_dirty(sb,bitfat[merk_i].a_buffer,1);
+ }
+ res=(res&bitmask) ? 1 : 0;
+ if(new)
+ { /* WARNING: variable res is different from above */
+ if(dblsb->s_free_sectors>=0&&res!=0&&*new==0)++dblsb->s_free_sectors;
+ if(dblsb->s_free_sectors>=0&&res==0&&*new!=0)--dblsb->s_free_sectors;
+ }
+ unlock_bitfat();
+ return res;
+#endif
+ }
+
+ printk(KERN_ERR "DMSDOS: dbl_bitfat_value: version not found?? cannot happen\n");
+ return -1;
+
+ bitfat_error:
+ unlock_bitfat();
+ printk(KERN_ERR "DMSDOS: unable to read bitfat area %d for sector %d\n",area,
+ sectornr);
+ return -1;
+}
+
+#ifndef __DMSDOS_LIB__
+int dblspace_fat_access(struct super_block*sb, int clusternr, int newval)
+{ int cl;
+
+ cl=dbl_fat_nextcluster(sb,clusternr,NULL);
+
+ if(newval==-1) return cl;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: dblspace_fat_access: READ-ONLY filesystem\n");
+ /* This is a bad hack in order to work around a problem with the
+ FAT driver: The FAT driver assumes fat_access never fails. Thus
+ returning -EROFS results in an endless loop (i.e. system hang)
+ at least in fat_free. We return -1 here in order to simulate EOF
+ which should break any loop in the FAT driver. */
+ return /* -EROFS */ -1;
+ }
+
+ if(newval==0)delete_cache_cluster(sb,clusternr);
+ dbl_fat_nextcluster(sb,clusternr,&newval);
+ if(cl<0)return -1; /* see comment above -- just to be sure :) */
+ /* if cl _is_ -1 (EOF) this is ok. */
+ /* if it is a negative error it is replaced by EOF. */
+ return cl;
+}
+
+int dblspace_bmap(struct inode*inode, int block)
+{
+ #ifdef __FOR_KERNEL_2_3_10
+ return dblspace_smap(inode,block);
+ #else
+ printk(KERN_WARNING "DMSDOS: bmap called, unsupported!\n");
+ return -EIO;
+ #endif
+}
+
+int dblspace_smap(struct inode*inode, int block)
+{ int cluster;
+ int sect_offs;
+ Dblsb*dblsb=MSDOS_SB(inode->i_sb)->private_data;
+
+ cluster=block/dblsb->s_sectperclust;
+ sect_offs=block%dblsb->s_sectperclust;
+
+ LOG_FS("DMSDOS: smap called, block=%d cluster_offs=%d sector_offs=%d\n",
+ block,cluster,sect_offs);
+
+ if (inode->i_ino == MSDOS_ROOT_INO || (S_ISDIR(inode->i_mode) &&
+ !MSDOS_I(inode)->i_start))
+ {
+ if (block >= MSDOS_SB(inode->i_sb)->dir_entries >> MSDOS_DPS_BITS)
+ { LOG_FS("DMSDOS: smap: root dir beyond end, returning 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: root dir, returning %d\n",block+FAKED_ROOT_DIR_OFFSET);
+ return block+FAKED_ROOT_DIR_OFFSET;
+ }
+
+ if (!(cluster = get_cluster(inode,cluster)))
+ { LOG_FS("DMSDOS: smap: get_cluster returned 0\n");
+ return 0;
+ }
+ LOG_FS("DMSDOS: smap: returning vsector(cluster=%d sector=%d)\n",
+ cluster,sect_offs);
+ return ((cluster-2)*dblsb->s_sectperclust)+sect_offs+FAKED_DATA_START_OFFSET;
+}
+
+/* clusternr is absolute, not relative to inode */
+void dblspace_zero_new_cluster(struct inode*inode, int clusternr)
+{ /* we decide upon the inode whether this is a dir cluster */
+ struct super_block*sb=inode->i_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ Cluster_head*ch;
+ int i;
+
+ if(!S_ISDIR(inode->i_mode))
+ { /* we just throw the cluster away if it has an mdfat entry */
+ delete_cache_cluster(sb,clusternr);
+ }
+ else
+ { /* it may be in cache, so ... */
+ ch=ch_read(sb,clusternr,C_NO_READ|C_KEEP_LOCK); /* I hope that noread is OK there, Pavel */
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: zero_new_cluster: ch_noread failed???\n");
+ return;
+ }
+ memset(ch->c_data,0,dblsb->s_sectperclust*SECTOR_SIZE);
+ if(DIR_MAY_BE_SHORT(dblsb))ch->c_length=SECTOR_SIZE;
+ else ch->c_length=dblsb->s_sectperclust*SECTOR_SIZE;
+ /* ch_dirty_locked unlocks the cluster *after* write */
+ i=ch_dirty_locked(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ if(i<0&&i!=-EROFS) /* don't try until death on a read-only filesystem */
+ ch_dirty_retry_until_success(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ ch_free(ch);
+ }
+}
+#endif
diff --git a/src/dblspace_virtual.c b/src/dblspace_virtual.c
new file mode 100644
index 0000000..7c386bd
--- /dev/null
+++ b/src/dblspace_virtual.c
@@ -0,0 +1,989 @@
+/*
+dblspace_virtual.c
+
+DMSDOS CVF-FAT module: virtual buffer routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifndef __KERNEL__
+#error This file needs __KERNEL__
+#endif
+
+#include <linux/fs.h>
+#include <linux/msdos_fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <asm/semaphore.h>
+#include "dmsdos.h"
+
+/* Here we do a virtual sector translation */
+
+#define FAKED_BH_MAGIC 0xfff0
+/* i hope this never occurs in kernel buffers... */
+
+extern unsigned long int dmsdos_speedup;
+
+/* cluster caching ... */
+Cluster_head ccache[CCACHESIZE];
+
+DECLARE_MUTEX(ccache_sem); /* Must be initialized to green light */
+void lock_ccache(void) {down(&ccache_sem);}
+void unlock_ccache(void) {up(&ccache_sem);}
+
+DECLARE_WAIT_QUEUE_HEAD(fullwait);
+
+int help_ccache=0;
+
+/* must be called ccache locked */
+/* function does not deal with locking */
+Cluster_head* find_in_ccache(struct super_block*sb,
+ int clusternr,Cluster_head**lastfree,
+ Cluster_head**oldest)
+{ int i;
+ int lastfree_n=-1;
+ int oldest_n=-1;
+ unsigned int oldest_time=CURRENT_TIME;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_FREE)lastfree_n=i;
+ else
+ { if(ccache[i].c_time<oldest_time&&ccache[i].c_count==0)
+ { oldest_n=i;
+ oldest_time=ccache[i].c_time;
+ }
+ if(ccache[i].c_clusternr==clusternr&&ccache[i].c_sb->s_dev==sb->s_dev)
+ { /* found */
+ return &(ccache[i]);
+ }
+ }
+ }
+ if(lastfree_n>=0&&lastfree!=NULL)*lastfree=&(ccache[lastfree_n]);
+ /* uhhh, deadlock possible here... we should always have an oldest when
+ there's nothing free any more... */
+ /* use help_ccache to avoid always choosing the same cluster */
+ if(lastfree_n<0&&oldest_n<0&&oldest!=NULL)
+ { /* search more carefully */
+ /* candidates are all those with count==0 */
+ for(i=0;i<CCACHESIZE;++i)
+ { ++help_ccache; if(help_ccache>=CCACHESIZE)help_ccache=0;
+ if(ccache[help_ccache].c_count==0)
+ { oldest_n=help_ccache;
+ break;
+ }
+ }
+ }
+ if(oldest_n>=0&&oldest!=NULL)*oldest=&(ccache[oldest_n]);
+ return NULL;
+}
+
+int count_error_clusters(void)
+{ int i;
+ int errs=0;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ if(ccache[i].c_errors)++errs;
+ }
+ if(errs)printk("DMSDOS: %d error clusters waiting in cache\n",errs);
+ return errs;
+}
+
+/* this is called if a cluster write fails too often */
+/* the cluster is expected to be locked */
+#define MAX_ERRORS 5
+#define MAX_ERROR_CLUSTERS ((CCACHESIZE/10)+1)
+void handle_error_cluster(Cluster_head*ch)
+{ Dblsb*dblsb;
+
+ if(ch->c_errors>MAX_ERRORS||count_error_clusters()>MAX_ERROR_CLUSTERS)
+ { /* get rid of the cluster to prevent dmsdos crash due to endless loops */
+ /* this shouldn't hurt much since the filesystem is already damaged */
+ printk(KERN_CRIT "DMSDOS: dirty cluster %d on dev 0x%x removed, data are lost\n",
+ ch->c_clusternr,ch->c_sb->s_dev);
+ /*dmsdos_write_cluster(sb,NULL,0,clusternr,0,0); better not do this*/
+ ch->c_flags=C_DELETED;
+ ch->c_clusternr=-1;
+ if((ch->c_sb->s_flags&MS_RDONLY)==0)
+ { printk(KERN_CRIT "DMSDOS: filesystem on dev 0x%x probably damaged, set to READ-ONLY mode\n",
+ ch->c_sb->s_dev);
+ ch->c_sb->s_flags|=MS_RDONLY;
+ dblsb=MSDOS_SB(ch->c_sb)->private_data;
+ dblsb->s_comp=READ_ONLY;
+ }
+ }
+ else
+ { printk(KERN_CRIT "DMSDOS: cannot write dirty cluster %d on dev 0x%x c_error=%d, trying again later\n",
+ ch->c_clusternr,ch->c_sb->s_dev,ch->c_errors);
+ }
+}
+
+void lock_ch(Cluster_head*ch)
+{ if(ch->c_count==0)panic("DMSDOS: lock_ch: count=0! This is a bug.\n");
+ down(&(ch->c_sem));
+}
+
+void unlock_ch(Cluster_head*ch){up(&(ch->c_sem));}
+
+void dump_ccache(void)
+{ int i;
+
+ printk(KERN_INFO "DMSDOS: ccache contents:\n");
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { printk(KERN_INFO "slot %d: time=0x%x flags=",i,ccache[i].c_time);
+ switch(ccache[i].c_flags)
+ { case C_FREE: printk("FREE"); break;
+ case C_VALID: printk("VALID"); break;
+ case C_INVALID: printk("INVALID"); break;
+ case C_DIRTY_NORMAL: printk("DIRTY_NORMAL"); break;
+ case C_DIRTY_UNCOMPR: printk("DIRTY_UNCOMPR"); break;
+ case C_DELETED: printk("DELETED"); break;
+ case C_NOT_MALLOCD: printk("NOT_MALLOCD"); break;
+ default: printk("unknown(?)");
+ }
+ printk(" count=%d length=%d clusternr=%d dev=0x%x errors=%d\n",
+ ccache[i].c_count,ccache[i].c_length,ccache[i].c_clusternr,
+ /* note that c_sb of a free slot may point to anywhere, so... */
+ ccache[i].c_flags!=C_FREE?ccache[i].c_sb->s_dev:0,
+ ccache[i].c_errors);
+ }
+ unlock_ccache();
+}
+
+/* get a (probably empty) ccache slot for cluster */
+/* this function does not malloc memory or read the data */
+/* the returned cluster is locked */
+#define MAX_RETRIES 100 /* should just break the loop in case of ... */
+Cluster_head* get_ch(struct super_block*sb,int clusternr)
+{ Cluster_head*free=NULL;
+ Cluster_head*oldest=NULL;
+ Cluster_head*actual;
+ int count_retries=0;
+ struct super_block*sb2;
+
+ retry:
+ if(count_retries++>MAX_RETRIES)
+ { printk(KERN_WARNING "DMSDOS: get_ch: max_retries reached, breaking loop. This may be a bug.\n");
+ return NULL;
+ }
+
+ lock_ccache();
+
+ actual=find_in_ccache(sb,clusternr,&free,&oldest);
+
+ /* the simplest case: we have found it in cache */
+ if(actual)
+ { ++(actual->c_count);
+ actual->c_time=CURRENT_TIME;
+ unlock_ccache();
+ lock_ch(actual);
+ if(actual->c_sb!=sb||actual->c_clusternr!=clusternr)
+ { /* looks like some other process was faster and discarded it :( */
+ printk(KERN_INFO "DMSDOS: get_ch: actual looks modified ARGHHH, retrying\n");
+ unlock_ch(actual);
+ lock_ccache();
+ --(actual->c_count);
+ unlock_ccache();
+ goto retry;
+ }
+ return actual;
+ }
+
+ /* we have not found it, instead we found a free slot */
+ if(free)
+ { if(free->c_count!=0)
+ { printk(KERN_WARNING "DMSDOS: get_ch: free->c_count!=0\n");
+ }
+ free->c_count=1;
+ free->c_flags=C_NOT_MALLOCD;
+ free->c_sb=sb;
+ free->c_clusternr=clusternr;
+ free->c_time=CURRENT_TIME;
+ free->c_errors=0;
+ unlock_ccache();
+ lock_ch(free);
+ if(free->c_sb!=sb||free->c_clusternr!=clusternr)
+ { /* looks like some other process was faster and discarded it :( */
+ printk(KERN_INFO "DMSDOS: get_ch: free looks modified ARGHHH, retrying\n");
+ unlock_ch(free);
+ lock_ccache();
+ --(free->c_count);
+ unlock_ccache();
+ goto retry;
+ }
+ return free;
+ }
+
+ /* now the most complex case: we must discard one cluster */
+ if(oldest)
+ { if(oldest->c_count!=0)
+ { printk(KERN_WARNING "DMSDOS: get_ch: oldest->c_count!=0\n");
+ }
+ oldest->c_count=1;
+ /* is this the time touch really necessary here ? */
+ /*oldest->c_time=CURRENT_TIME;*/
+ unlock_ccache();
+ lock_ch(oldest);
+ switch(oldest->c_flags)
+ { case C_DIRTY_NORMAL:
+ case C_DIRTY_UNCOMPR:
+ sb2=oldest->c_sb;
+ if(dmsdos_write_cluster(sb2,oldest->c_data,oldest->c_length,
+ oldest->c_clusternr,0,
+ oldest->c_flags==C_DIRTY_NORMAL?UC_NORMAL:UC_UNCOMPR)<0)
+ { /* write failed */
+ oldest->c_time=CURRENT_TIME;
+ ++(oldest->c_errors);
+ handle_error_cluster(oldest);
+ break;
+ }
+ /* successfully written */
+ oldest->c_flags=C_VALID;
+ oldest->c_errors=0;
+ /* fall through */
+ case C_VALID:
+ case C_INVALID:
+ if(oldest->c_count>1)break; /* we cannot do anything here */
+ /* we are the only process using it */
+ FREE(oldest->c_data);
+ oldest->c_flags=C_NOT_MALLOCD;
+ /* fall through */
+ case C_NOT_MALLOCD:
+ if(oldest->c_count>1)break;
+ /* now we have a free slot */
+ oldest->c_sb=sb;
+ oldest->c_clusternr=clusternr;
+ oldest->c_time=CURRENT_TIME;
+ oldest->c_errors=0;
+ return oldest;
+ }
+ unlock_ch(oldest);
+ lock_ccache();
+ --(oldest->c_count);
+ unlock_ccache();
+ /* anything may have happened meanwhile */
+ goto retry;
+ }
+
+ /* the last case: not found, no one free and no one found to discard */
+ /* all are in use - we must wait */
+ unlock_ccache();
+ LOG_CCACHE("DMSDOS: cluster cache full, waiting...\n");
+ /* or may it be better to abort here ? */
+ sleep_on(&fullwait);
+ goto retry;
+
+ return NULL;
+}
+
+/* this routine *must* return a cluster with its slack zerod out if the
+ cluster doesn't have maximum length and flag C_NO_READ is unset */
+Cluster_head* ch_read(struct super_block*sb,int clusternr,int flag)
+{ Cluster_head*ch;
+ Dblsb*dblsb;
+ int i;
+
+ LOG_CCACHE("DMSDOS: ch_read cluster %d\n",clusternr);
+ ch=get_ch(sb,clusternr);
+ if(ch==NULL)return NULL;
+ /* get_ch returns a locked cluster slot */
+ LOG_CCACHE("DMSDOS: ch_read: got count=%d\n",ch->c_count);
+ dblsb=MSDOS_SB(ch->c_sb)->private_data;
+
+ /* check whether we need to get memory */
+ if(ch->c_flags==C_NOT_MALLOCD)
+ { ch->c_data=MALLOC(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(ch->c_data==NULL)
+ { printk(KERN_ERR "DMSDOS: ch_read: no memory!\n");
+ unlock_ch(ch);
+ ch_free(ch);
+ return NULL;
+ }
+ else ch->c_flags=C_INVALID;
+ }
+
+ /* check whether we need to read the data from disk */
+ if(ch->c_flags==C_INVALID)
+ { ch->c_length=0;
+ if((flag&C_NO_READ)==0)
+ { i=dmsdos_read_cluster(sb,ch->c_data,clusternr);
+ if(i<0)
+ { printk(KERN_ERR "DMSDOS: ch_read: read_cluster failed\n");
+ unlock_ch(ch);
+ ch_free(ch);
+ return NULL;
+ }
+ ch->c_length=i;
+ }
+ ch->c_flags=C_VALID;
+ }
+
+ /* check whether the lock is to be kept */
+ if((flag&C_KEEP_LOCK)==0)unlock_ch(ch);
+
+ LOG_CCACHE("DMSDOS: ch_read finished.\n");
+ return ch;
+}
+
+int ch_dirty_locked(Cluster_head*ch, int near, int ucflag)
+{ /* Hmm...
+ We must ensure that we can safely delay writing of the cluster
+ before. This is done by calling the write_cluster function with
+ the SIMULATE flag. If there's a problem then it is expected to
+ return an error code (return value<0). In that case we fall back to
+ the old write-through caching mechanism.
+ */
+ /* IMPORTANT: This function expects that the cluster has already
+ been locked. If not, use ch_dirty intead.
+ */
+
+ int i;
+ struct super_block*sb=ch->c_sb;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: ch_dirty(_locked): READ-ONLY filesystem\n");
+ unlock_ch(ch);
+ return -EROFS; /*this might be bad, but...*/
+ }
+
+ LOG_CCACHE("DMSDOS: ch_dirty_locked cluster %d flags %d ucflag %d\n",
+ ch->c_clusternr,ch->c_flags,ucflag);
+
+ /* we expect the cluster to be already locked */
+ /* this is not checked here */
+
+ /* do not write free or deleted clusters or other junk */
+ if(ch->c_flags==C_FREE||ch->c_flags==C_DELETED||ch->c_flags==C_INVALID
+ ||ch->c_flags==C_NOT_MALLOCD)
+ { unlock_ch(ch);
+ return 0;
+ }
+
+ if(dmsdos_speedup&SP_USE_WRITE_BACK)
+ {
+ /* call write_cluster with SIMULATE flag (ucflag=UC_TEST) first */
+ i=dmsdos_write_cluster(sb,ch->c_data,ch->c_length,ch->c_clusternr,
+ near,UC_TEST);
+ if(i>=0)
+ { /* success - we can safely delay writing this cluster */
+ ch->c_flags=ucflag!=UC_NORMAL?C_DIRTY_UNCOMPR:C_DIRTY_NORMAL;
+ unlock_ch(ch);
+ return i;
+ }
+ /* otherwise fall back to the default write-through code */
+ }
+
+ i=dmsdos_write_cluster(sb,ch->c_data,ch->c_length,ch->c_clusternr,
+ near,ucflag);
+ /* the cluster is *not* marked dirty since failure is *reported* to the
+ calling process, which can call ch_dirty_retry_until_success if the
+ data really *must* be written */
+ /* so we also don't increment the error counter */
+ unlock_ch(ch);
+ return i;
+}
+
+int ch_dirty(Cluster_head*ch, int near, int ucflag)
+{ /* same as ch_dirty_locked, but cluster is expected not to be locked */
+ /* IMPORTANT: The cluster must not be locked by calling process as this
+ will cause a deadlock. If you have just locked the cluster, use
+ ch_dirty_locked instead.
+ */
+
+ LOG_CCACHE("DMSDOS: ch_dirty cluster %d flags %d ucflag %d\n",
+ ch->c_clusternr,ch->c_flags,ucflag);
+
+ lock_ch(ch);
+ /* ch_dirty_locked unlocks the cluster */
+ return ch_dirty_locked(ch,near,ucflag);
+}
+
+/* This routine is called if extremely important data (directory structure)
+ really must be written when normal ch_dirty already fails. However, there's
+ no guarantee that the data can really be written some time later. This
+ function is meant as a last resort. Code will discard the data later if too
+ many errors occur and set the filesystem to read-only mode. */
+void ch_dirty_retry_until_success(Cluster_head*ch, int near, int ucflag)
+{ lock_ch(ch);
+ if(ch->c_flags!=C_FREE&&ch->c_flags!=C_DELETED&&ch->c_flags!=C_INVALID
+ &&ch->c_flags!=C_NOT_MALLOCD)
+ ch->c_flags=ucflag!=UC_NORMAL?C_DIRTY_UNCOMPR:C_DIRTY_NORMAL;
+ unlock_ch(ch);
+}
+
+void ch_free(Cluster_head*ch)
+{ lock_ccache();
+ --(ch->c_count);
+ LOG_CCACHE("DMSDOS: ch_free cluster %d count_after_free %d\n",
+ ch->c_clusternr,ch->c_count);
+ if(ch->c_count==0)
+ { /* throw away unused deleted clusters immediately */
+ if(ch->c_flags==C_DELETED)
+ { FREE(ch->c_data);
+ ch->c_flags=C_FREE;
+ }
+ /* throw away unused and not malloc'd clusters also */
+ if(ch->c_flags==C_NOT_MALLOCD)ch->c_flags=C_FREE;
+
+ unlock_ccache();
+ wake_up(&fullwait);
+ return;
+ }
+ unlock_ccache();
+}
+
+void ccache_init()
+{ int i;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { ccache[i].c_flags=C_FREE;
+ ccache[i].c_time=0;
+ ccache[i].c_count=0;
+ init_MUTEX(&ccache[i].c_sem);
+ ccache[i].c_errors=0;
+ }
+}
+
+/* for unmount */
+/* we assume that the clusters of the device to be unmounted are
+ - not in use (count==0) and
+ - not locked
+ either of them would mean that an inode of the filesystem is currently
+ in use and thus the filesystem driver shouldn't allow unmount...
+ ...violation will cause segfaults in a lot of other places...
+*/
+void free_ccache_dev(struct super_block*sb)
+{ int i;
+
+ retry:
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags!=C_FREE&&ccache[i].c_sb->s_dev==sb->s_dev)
+ { /* write it before if it is dirty... */
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||
+ ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { /* we do not check for failure since we cannot do anything for it
+ at unmount time (except panic, but this would be worse) */
+ dmsdos_write_cluster(ccache[i].c_sb,ccache[i].c_data,
+ ccache[i].c_length,ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_NORMAL?UMOUNT_UCFLAG:UC_UNCOMPR);
+ ccache[i].c_flags=C_VALID;
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+ unlock_ccache();
+
+ /* I'm not sure whether we can be sure here, so just the safe way */
+ goto retry;
+ }
+
+ if(ccache[i].c_count)printk(KERN_ERR "DMSDOS: free_ccache_dev: oh oh, freeing busy cluster...\n");
+ if(ccache[i].c_flags!=C_NOT_MALLOCD)FREE(ccache[i].c_data);
+ ccache[i].c_flags=C_FREE;
+ wake_up(&fullwait);
+ }
+ }
+ unlock_ccache();
+}
+
+/* for memory management */
+/* We simply ignore clusters that are currently in use
+ (count!=0). This should be safe now. */
+void free_idle_ccache(void)
+{ int i;
+
+ retry:
+ lock_ccache();
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags!=C_FREE&&ccache[i].c_count==0) /*those with count=0
+ are never locked */
+ { /* hmm... would be a good idea to make it depend on free system
+ memory whether to discard which cached clusters... and how many...
+ any ideas? */
+ if(CURRENT_TIME-ccache[i].c_time>MAX_CCACHE_TIME)
+ { /* throw away */
+ /* but write it before if it is dirty... */
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL||
+ ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { if(dmsdos_write_cluster(ccache[i].c_sb,ccache[i].c_data,
+ ccache[i].c_length,ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_NORMAL?UC_NORMAL:UC_UNCOMPR)>=0
+ )
+ { ccache[i].c_flags=C_VALID;
+ ccache[i].c_errors=0;
+ }
+ else /* failed */
+ { ccache[i].c_time=CURRENT_TIME; /* so we don't catch it again
+ in retry */
+ ++(ccache[i].c_errors);
+ handle_error_cluster(&(ccache[i]));
+ }
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+ unlock_ccache();
+
+ /* I'm not sure whether we must restart here */
+ goto retry;
+ }
+ if(ccache[i].c_flags!=C_NOT_MALLOCD)FREE(ccache[i].c_data);
+ ccache[i].c_flags=C_FREE;
+ wake_up(&fullwait);
+ }
+ }
+ }
+ unlock_ccache();
+
+ /* wake up forgotten fullwaits - for safety */
+ wake_up(&fullwait);
+}
+
+/* for cluster cache syncing - writes all currently unused dirty clusters */
+void sync_cluster_cache(int allow_daemon)
+{ int i;
+ struct super_block*sb;
+
+ lock_ccache();
+
+ for(i=0;i<CCACHESIZE;++i)
+ { if(ccache[i].c_flags==C_DIRTY_NORMAL||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { sb=ccache[i].c_sb;
+ ++(ccache[i].c_count);
+ unlock_ccache();
+ lock_ch(&(ccache[i]));
+ if(ccache[i].c_flags==C_DIRTY_NORMAL
+ ||ccache[i].c_flags==C_DIRTY_UNCOMPR)
+ { if(dmsdos_write_cluster(sb,ccache[i].c_data,ccache[i].c_length,
+ ccache[i].c_clusternr,0,
+ ccache[i].c_flags==C_DIRTY_UNCOMPR?UC_UNCOMPR:
+ (allow_daemon?UC_NORMAL:UC_DIRECT)
+ ) >=0
+ )
+ { ccache[i].c_flags=C_VALID;
+ ccache[i].c_errors=0;
+ }
+ else /* we cannot do much :( */
+ { ++(ccache[i].c_errors);
+ handle_error_cluster(&(ccache[i]));
+ }
+ }
+ unlock_ch(&(ccache[i]));
+ lock_ccache();
+ --(ccache[i].c_count);
+
+ /* we must not retry here since this causes an endless loop
+ in case of a write error...
+ unlock_ccache();
+ goto retry;
+ */
+ }
+ }
+
+ unlock_ccache();
+}
+
+/* cluster delete - deletes this cluster from the cache and cancels ch_dirty
+ -- called when cluster is deleted in FAT */
+void delete_cache_cluster(struct super_block*sb, int clusternr)
+{ Cluster_head*ch;
+
+ /* gets always a locked cluster and doesn't force read from disk */
+ ch=get_ch(sb,clusternr);
+
+ /* it is really necessary that get_ch never fails, but .... */
+ if(ch==NULL)
+ { printk(KERN_WARNING "DMSDOS: delete_cache_cluster: get_ch returned NULL\n");
+ /* we can't do anything here, so just assume Murphy is not there :) */
+ /* well of course, failed get_ch means the cluster is surely not in the
+ cache */
+ dmsdos_write_cluster(sb,NULL,0,clusternr,0,0);
+ return;
+ }
+
+ /* inform the cluster handling routines so they can delete pointers in fs */
+ dmsdos_write_cluster(sb,NULL,0,clusternr,0,0);
+
+ /* if someone is using the cluster except us he has bad luck */
+ if(ch->c_flags!=C_NOT_MALLOCD)ch->c_flags=C_DELETED;
+ ch->c_clusternr=-1; /* :) */
+
+ unlock_ch(ch);
+ ch_free(ch);
+}
+
+/* for daemon write */
+int daemon_write_cluster(struct super_block*sb,unsigned char*data,
+ int len, int clusternr, int rawlen)
+{ Cluster_head*ch;
+ int i;
+
+ /* gets always a locked cluster slot and doesn't force read from disk */
+ ch=get_ch(sb,clusternr);
+ if(ch==NULL)
+ { /* sorry */
+ printk(KERN_ERR "DMSDOS: daemon_write_cluster: ch==NULL\n");
+ return -EIO;
+ }
+
+ i=dmsdos_write_cluster(sb,data,len,clusternr,0,-rawlen);
+ unlock_ch(ch);
+ ch_free(ch);
+
+ return i;
+}
+
+void log_ccache_statistics()
+{ int j;
+ int free;
+ int valid;
+ int invalid;
+ int dirty_n;
+ int dirty_u;
+ int del;
+ int n_mallocd;
+
+ lock_ccache();
+
+ printk(KERN_INFO "DMSDOS: ccache statistics:\n");
+
+ free=0;
+ valid=0;
+ invalid=0;
+ dirty_n=0;
+ dirty_u=0;
+ del=0;
+ n_mallocd=0;
+
+ for(j=0;j<CCACHESIZE;++j)
+ { switch(ccache[j].c_flags)
+ { case C_FREE: ++free; break;
+ case C_VALID: ++valid; break;
+ case C_INVALID: ++invalid; break;
+ case C_DIRTY_NORMAL: ++dirty_n; break;
+ case C_DIRTY_UNCOMPR: ++dirty_u; break;
+ case C_DELETED: ++del; break;
+ case C_NOT_MALLOCD: ++n_mallocd; break;
+ default: printk(KERN_ERR "DMSDOS: log_ccache_statistics: cannot happen.\n");
+ }
+ }
+
+ printk(KERN_INFO "sum: free=%d valid=%d invalid=%d dirty_n=%d dirty_u=%d del=%d n_mallocd=%d\n",
+ free,valid,invalid,dirty_n,dirty_u,del,n_mallocd);
+
+ unlock_ccache();
+}
+
+void get_memory_usage_ccache(int*used, int*max)
+{ int i;
+ int size=0;
+ int usedcount=0;
+ Dblsb*dblsb;
+
+ for(i=0;i<CCACHESIZE;++i)
+ { switch(ccache[i].c_flags)
+ { case C_VALID:
+ case C_DIRTY_NORMAL:
+ case C_DIRTY_UNCOMPR:
+ dblsb=MSDOS_SB(ccache[i].c_sb)->private_data;
+ size+=dblsb->s_sectperclust*SECTOR_SIZE;
+ usedcount++;
+ }
+ }
+ if(used)*used=size;
+ if(max)
+ { /* unknown due to possibly different cluster sizes */
+ /* we estimate :) */
+ if(usedcount==0)*max=0; /* nothing in use, we can't estimate :( */
+ else *max=(CCACHESIZE*size)/usedcount;
+ }
+}
+
+/**********************************************************************/
+
+
+struct buffer_head* dblspace_bread(struct super_block*sb,int vsector)
+{ int dbl_clust;
+ int sect_offs;
+ struct buffer_head*bh;
+ Cluster_head*ch;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ if(vsector>=FAKED_ROOT_DIR_OFFSET&&
+ vsector<FAKED_ROOT_DIR_OFFSET+dblsb->s_rootdirentries/16)
+ { LOG_LLRW("DMSDOS: read_virtual_sector: rootdir sector_offset %d\n",
+ vsector-FAKED_ROOT_DIR_OFFSET);
+ return raw_bread(sb,dblsb->s_rootdir+vsector-FAKED_ROOT_DIR_OFFSET);
+ }
+
+ if(vsector<FAKED_DATA_START_OFFSET)
+ { err:
+ printk(KERN_ERR "DMSDOS: illegal virtual sector %d, can't map to real sector\n",
+ vsector);
+ *(int*)0=0;
+ return NULL;
+ }
+ dbl_clust=((vsector-FAKED_DATA_START_OFFSET)/dblsb->s_sectperclust)+2;
+ if(dbl_clust>dblsb->s_max_cluster)goto err;
+ sect_offs=(vsector-FAKED_DATA_START_OFFSET)%dblsb->s_sectperclust;
+
+ ch=ch_read(sb,dbl_clust,0); /*we need to read and shouldn't need a lock*/
+ if(ch==NULL)
+ { printk(KERN_ERR "DMSDOS: read_virtual_sector: read_cluster failed!\n");
+ return NULL;
+ }
+ /* now setup a fake buffer_head */
+ bh=MALLOC(sizeof(struct buffer_head));
+ if(bh==NULL)
+ { printk(KERN_ERR "DMSDOS: read_virtual_sector: no memory!\n");
+ /*FREE(clusterd);*/
+ ch_free(ch);
+ return NULL;
+ }
+ bh->b_state=FAKED_BH_MAGIC;
+
+ /* we must share the data */
+ bh->b_data=&(ch->c_data[sect_offs*SECTOR_SIZE]);
+ /* the cluster is *not* free yet, so DON'T call ch_free here */
+ /* instead copy a pointer to the cluster_head somewhere so we can free it
+ later */
+ bh->b_next=(struct buffer_head*)ch; /*the cast avoids compiler warning */
+ return bh;
+}
+
+struct buffer_head *dblspace_getblk (
+ struct super_block *sb,
+ int block)
+{
+ struct buffer_head*ret;
+ ret=dblspace_bread(sb,block);
+ if(ret==NULL)return ret;
+ return ret;
+}
+
+void dblspace_brelse(struct super_block* sb,struct buffer_head*bh)
+{ if(bh)
+ { if(bh->b_state==FAKED_BH_MAGIC)
+ { /* we free the cluster instead */
+ /* a pointer to ch was saved in b_next */
+ /* uh, we must check the size first.... uh no this is for bh_dirty.. */
+ Cluster_head*ch=(Cluster_head*)bh->b_next;
+ /*if((unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE>ch->c_length)
+ ch->c_length=(unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE;*/
+ ch_free(ch);
+ FREE(bh);
+ return;
+ }
+ }
+ raw_brelse(sb,bh);
+}
+
+
+void dblspace_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val)
+{ int i;
+
+ if(dirty_val==0)return;
+
+ if(sb->s_flags&MS_RDONLY)
+ { printk(KERN_ERR "DMSDOS: dblspace_mark_buffer_dirty: READ-ONLY filesystem\n");
+ return;
+ }
+
+ if(bh)
+ { if(bh->b_state==FAKED_BH_MAGIC)
+ {
+ /* a copy of ch was saved in b_next */
+ Cluster_head*ch=(Cluster_head*)bh->b_next;
+ struct super_block*sb=ch->c_sb;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* ensure cluster is marked large enough to hold the virtual sector */
+ if((unsigned long)bh->b_data-(unsigned long)ch->c_data+SECTOR_SIZE>
+ ch->c_length)
+ ch->c_length=(unsigned long)bh->b_data-(unsigned long)ch->c_data+
+ SECTOR_SIZE;
+
+ /* the virtual sector code is usually called by directory handling
+ routines, so we check whether we may compress a directory
+ -- file_write calles ch_dirty directly */
+ i=ch_dirty(ch,0,DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ if(i<0&&i!=-EROFS) /*don't try until death on a read-only filesystem*/
+ {
+ /* now we have a serious problem here...
+ ch_dirty failed... we are in danger of losing the data....
+ */
+ printk(KERN_CRIT "DMSDOS: Dirty virtual sector cannot be written - FILESYSTEM DAMAGE POSSIBLE! Trying to delay write.\n");
+ ch_dirty_retry_until_success(ch,0,
+ DIR_MAY_BE_COMPRESSED(dblsb)?UC_NORMAL:UC_UNCOMPR);
+ }
+ return;
+ }
+ }
+
+ raw_mark_buffer_dirty(sb,bh,1);
+}
+
+void dblspace_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val)
+{
+ if(bh->b_state==FAKED_BH_MAGIC)return;
+ raw_set_uptodate(sb,bh,val);
+}
+
+int dblspace_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh)
+{
+ if(bh->b_state==FAKED_BH_MAGIC)return 1;
+ return raw_is_uptodate(sb,bh);
+}
+
+void dblspace_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32])
+{
+ /* we just cannot do low-level access here */
+ /* this might be wrong... */
+ /* as far as I know, only the read_ahead code in fat/file.c uses
+ this... well we do our own read-ahead in dmsdos, so what... */
+ return;
+}
+
+#ifdef USE_READA_LIST
+
+DECLARE_MUTEX(reada_sem); /* Must be initialized to green light */
+void lock_reada(void) {down(&reada_sem);}
+void unlock_reada(void) {up(&reada_sem);}
+struct
+{ struct buffer_head*bh;
+ struct super_block*sb;
+} reada_list[READA_LIST_SIZE];
+int ra_pos=0;
+
+void init_reada_list(void)
+{ int i;
+
+ for(i=0;i<READA_LIST_SIZE;++i)
+ { reada_list[i].bh=NULL;
+ reada_list[i].sb=NULL;
+ }
+}
+
+void kill_reada_list_dev(int dev)
+{ int i;
+
+ lock_reada();
+
+ for(i=0;i<READA_LIST_SIZE;++i)
+ { if(reada_list[i].bh)
+ { if(reada_list[i].sb->s_dev==dev)
+ { raw_brelse(reada_list[i].sb,reada_list[i].bh);
+ reada_list[i].bh=NULL;
+ reada_list[i].sb=NULL;
+ }
+ }
+ }
+
+ unlock_reada();
+}
+
+void stack_reada(struct super_block*sb,struct buffer_head*bh)
+{
+ lock_reada();
+
+ if(reada_list[ra_pos].bh)
+ raw_brelse(reada_list[ra_pos].sb,reada_list[ra_pos].bh);
+
+ reada_list[ra_pos].bh=bh;
+ reada_list[ra_pos].sb=sb;
+
+ ++ra_pos;
+ if(ra_pos>=READA_LIST_SIZE)ra_pos=0;
+
+ unlock_reada();
+}
+#endif /*USE_READA_LIST*/
+
+void dblspace_reada(struct super_block*sb, int sector,int count)
+{
+ struct buffer_head*bhlist[MAX_READA];
+ int i;
+ struct buffer_head**bhlist2=bhlist;
+
+ if(count>MAX_READA)count=MAX_READA;
+
+ i=0;
+ while(i<count)
+ {
+ bhlist[i]=raw_getblk(sb,sector+i); /* get without read */
+ if(bhlist[i]==NULL)break; /* failed, give up */
+ if(raw_is_uptodate(sb,bhlist[i]))
+ { raw_brelse(sb,bhlist[i]); /* according to breada the buffer must be
+ freed here */
+ break; /* has already been read, abort */
+ }
+ ++i;
+ }
+
+ count=i;
+
+ if(count==0)return;
+
+ /* uhh.. there's a hard limit of 32 in the fat filesystem... */
+ if(count/32)
+ { for(i=0;i<count/32;++i)
+ { raw_ll_rw_block(sb,READA,32,bhlist2);
+ bhlist2+=32;
+ }
+ }
+ if(count%32)raw_ll_rw_block(sb,READA,count%32,bhlist2);
+ /* place read command in list, but don't wait for it to finish */
+
+ for(i=0;i<count;++i)
+#ifdef USE_READA_LIST
+ stack_reada(sb,bhlist[i]);
+#else
+ /* not a good idea - brelse calls wait_on_buffer....... */
+ raw_brelse(sb,bhlist[i]);
+#endif
+}
diff --git a/src/dcread.c b/src/dcread.c
new file mode 100644
index 0000000..48b62e0
--- /dev/null
+++ b/src/dcread.c
@@ -0,0 +1,344 @@
+/*
+
+dcread.c
+
+DMSDOS: example program illustrating how to use the dmsdos library.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+
+This is an example how to use the dmsdos library. This program displays
+a cluster on the screen in one of several formats (hexdump, text, etc.).
+It can also search a file through the directories.
+
+For documentation about the dmsdos library see file libdmsdos.doc.
+
+Warning: This utility is not perfect. It does not check file end properly.
+It does not even distinguish between files and directories. It does not
+support long file names. And the file name conversion to 8.3 name space is
+far away from good. But example code never has to be perfect :)
+
+There's also no documentation how to use this program except the usage
+line. Example code never has documentation. Well, yes, you are expected
+to read through the source code. :)
+
+*/
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<ctype.h>
+
+#include"dmsdos.h"
+#include"lib_interface.h"
+
+#define M_RAW 1
+#define M_HEX 0
+#define M_DIR 2
+#define M_TXT 3
+#define M_DISPLAYMASK 3
+#define M_VERBOSE 16
+
+/*this is not good - but currently we have only one CVF open at a time*/
+struct super_block*sb;
+Dblsb*dblsb;
+
+int scan(char*text)
+{ int v=0;
+ if(strncmp(text,"0x",2)==0||strncmp(text,"0X",2)==0)
+ sscanf(text+2,"%x",&v);
+ else
+ sscanf(text,"%d",&v);
+ return v;
+}
+
+unsigned char* get_root_dir(void)
+{ unsigned char* data;
+ struct buffer_head*bh;
+ int i;
+
+ data=malloc(dblsb->s_rootdirentries*32);
+ if(data==NULL)return NULL;
+
+ for(i=0;i<dblsb->s_rootdirentries*32/512;++i)
+ { bh=raw_bread(sb,dblsb->s_rootdir+i);
+ if(bh==NULL){free(data);return NULL;}
+ memcpy(data+i*512,bh->b_data,512);
+ raw_brelse(sb,bh);
+ }
+ return data;
+}
+
+int display_cluster(int nr, int mode)
+{ unsigned char*data;
+ int i,j;
+
+ if(nr==0)
+ { data=get_root_dir();
+ if(data==NULL)return -1;
+ i=dblsb->s_rootdirentries*32;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data==NULL)return -1;
+ i=dmsdos_read_cluster(sb,data,nr);
+ if(i<0){free(data);return -1;}
+ }
+
+ if(mode&M_VERBOSE)fprintf(stderr,"cluster %d has length %d\n",nr,i);
+
+ switch(mode&M_DISPLAYMASK)
+ { case M_RAW:
+ for(j=0;j<dblsb->s_sectperclust*512;++j)printf("%c",data[j]);
+ break;
+ case M_HEX:
+ for(j=0;j<dblsb->s_sectperclust*512;j+=16)
+ { char buf[100];
+ char str[100];
+
+ sprintf(str,"%04X:",j);
+ for(i=0;i<16;++i)
+ { sprintf(buf," %02X",data[j+i]);
+ strcat(str,buf);
+ }
+ strcat(str," ");
+ for(i=0;i<16;++i)
+ { if(data[i+j]>=32&&data[i+j]<=126)sprintf(buf,"%c",data[i+j]);
+ else strcpy(buf,".");
+ strcat(str,buf);
+ }
+
+ printf("%s\n",str);
+ }
+ break;
+ case M_DIR:
+ for(j=0;j<dblsb->s_sectperclust*512;j+=32)
+ { unsigned char*pp;
+ unsigned int x;
+
+ if(data[j]==0)break;
+ if(data[j]==0xe5){printf("--DELETED--\n");continue;}
+ for(i=0;i<11;++i)
+ { if(i==8)printf(" ");
+ printf("%c",data[j+i]);
+ }
+ printf(" ");
+ if(data[j+11]&1)printf("R");else printf(" ");
+ if(data[j+11]&2)printf("H");else printf(" ");
+ if(data[j+11]&4)printf("S");else printf(" ");
+ if(data[j+11]&8)printf("V");else printf(" ");
+ if(data[j+11]&16)printf("D");else printf(" ");
+ if(data[j+11]&32)printf("A");else printf(" ");
+ if(data[j+11]&64)printf("?");else printf(" ");
+ if(data[j+11]&128)printf("?");else printf(" ");
+
+ pp=&(data[j+22]);
+ x=CHS(pp);
+ printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);
+
+ pp=&(data[j+24]);
+ x=CHS(pp);
+ printf(" %02d.%02d.%04d",x&31,(x>>5)&15,(x>>9)+1980); /* y2k compliant :) */
+
+ pp=&(data[j+26]);
+ printf(" %5d",CHS(pp));
+
+ pp=&(data[j+28]);
+ printf(" %7lu\n",CHL(pp));
+ }
+ break;
+ case M_TXT:
+ i=0;
+ for(j=0;j<dblsb->s_sectperclust*512;j++)
+ { if(data[j]==10)
+ { printf("\n");
+ i=0;
+ continue;
+ }
+ if(data[j]>=32&&data[j]<=126)printf("%c",data[j]);
+ else printf(".");
+ ++i;
+ if(i==80&&j<511&&data[j+1]!=10)
+ { printf("\n");
+ i=0;
+ }
+ }
+ if(i)printf("\n");
+ break;
+ default:
+ fprintf(stderr,"display mode not implemented\n");
+ free(data);
+ return -1;
+ }
+
+ free(data);
+ return 0;
+}
+
+int display_chain(int start, int mode)
+{ int i,next;
+
+ if(start==0)return display_cluster(0,mode);
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ if(next==0&&(mode&M_VERBOSE)!=0)
+ fprintf(stderr,"warning: cluster %d is marked as unused in FAT\n",start);
+ i=display_cluster(start,mode);
+ if(i<0)return i;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { fprintf(stderr,"chain has no valid end in FAT\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int scan_dir(char*entry,int start)
+{ char buf[]=" ";
+ /*12345678EXT*/
+ int i;
+ int size;
+ unsigned char*data;
+ int next;
+
+ if(strcmp(entry,".")==0)return start;
+ else if(strcmp(entry,"..")==0)strncpy(buf,"..",2);
+ else if(*entry=='.') return -1;
+ else
+ for(i=0;i<11;++i)
+ { if(*entry=='.'&&i<=7){i=7;++entry;continue;}
+ if(*entry=='.'&&i==8){i=7;++entry;continue;}
+ if(*entry=='.')break;
+ if(*entry=='\0')break;
+ buf[i]=toupper(*entry);
+ ++entry;
+ }
+
+ do
+ {
+ printf("scan_dir: searching for %s in %d\n",buf,start);
+
+ if(start==0)
+ { data=get_root_dir();
+ size=dblsb->s_rootdirentries;
+ next=-1;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data!=NULL)
+ { i=dmsdos_read_cluster(sb,data,start);
+ if(i<0){free(data);data=NULL;}
+ size=i/32;
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ if(next==0)
+ fprintf(stderr,"warning: cluster %d is marked as unused in FAT\n",
+ next);
+ }
+ }
+ if(data==NULL)return -1;
+
+ for(i=0;i<size;++i)
+ { if(strncmp(&(data[i*32]),buf,11)==0)
+ { unsigned char*pp;
+ int cluster;
+
+ pp=&(data[i*32+26]);
+ cluster=CHS(pp);
+ free(data);
+ return cluster;
+ }
+ }
+
+ free(data);
+ start=next;
+ }
+ while(next>0&&next<=dblsb->s_max_cluster);
+ return -1;
+}
+
+int scan_path(char*path,int start)
+{ int i;
+ char*p;
+
+ for(p=strtok(path,"/");p;p=strtok(NULL,"/"))
+ { i=scan_dir(p,start);
+ if(i<0)
+ { fprintf(stderr,"path component %s not found\n",p);
+ return -1;
+ }
+ start=i;
+ }
+
+ return start;
+}
+
+int main(int argc, char*argv[])
+{ int mode=0;
+ int cluster;
+ int i;
+
+ if(argc<3)
+ { fprintf(stderr,"usage: dcread CVF cluster|/path/to/file [raw|dir|hex|txt]\n");
+ return 1;
+ }
+
+ sb=open_cvf(argv[1],0/*read-only*/);
+ if(sb==NULL)
+ { printf("open CVF %s failed\n",argv[1]);
+ return 2;
+ }
+ dblsb=MSDOS_SB(sb)->private_data;
+
+ if(*(argv[2])=='/')
+ { cluster=scan_path(argv[2]+1,0);
+ if(cluster<0)
+ { fprintf(stderr,"%s not found\n",argv[2]);
+ return 1;
+ }
+ }
+ else cluster=scan(argv[2]);
+
+ if(argc==4)
+ { if(strcmp(argv[3],"raw")==0)mode=M_RAW;
+ else if(strcmp(argv[3],"dir")==0)mode=M_DIR;
+ else if(strcmp(argv[3],"hex")==0)mode=M_HEX;
+ else if(strcmp(argv[3],"txt")==0)mode=M_TXT;
+ else
+ { fprintf(stderr,"invalid argument %s\n",argv[3]);
+ close_cvf(sb);
+ return 1;
+ }
+ }
+
+ i=display_chain(cluster,mode|M_VERBOSE);
+
+ close_cvf(sb);
+
+ return i;
+}
diff --git a/src/dmsdos-config.default b/src/dmsdos-config.default
new file mode 100644
index 0000000..c402af5
--- /dev/null
+++ b/src/dmsdos-config.default
@@ -0,0 +1,65 @@
+#
+# Defaults for a safe dmsdos configuration. Do not edit this file.
+#
+
+#
+# CVF formats to be supported
+#
+DMSDOS_CONFIG_DBLSP_DRVSP=y
+DMSDOS_CONFIG_DRVSP3=y
+DMSDOS_CONFIG_STAC3=y
+DMSDOS_CONFIG_STAC4=y
+
+#
+# Memory Management
+#
+# USE_XMALLOC is not set
+USE_VMALLOC=y
+
+#
+# Cache setup
+#
+LISTSIZE=1024
+MDFATCACHESIZE=40
+DFATCACHESIZE=20
+BITFATCACHESIZE=10
+MAX_CACHE_TIME=60
+CCACHESIZE=64
+MAX_CCACHE_TIME=240
+
+#
+# Read-ahead Options
+#
+MAX_READA=64
+USE_READA_LIST=y
+READA_LIST_SIZE=256
+READA_THRESHOLD=4095
+
+#
+# Misc Options
+#
+DBL_WRITEACCESS=y
+DEFAULT_LOGLEVEL=0
+# NOLOG is not set
+DEFAULT_CF=11
+# SEQLOG is not set
+DMSDOS_USE_READPAGE=y
+
+#
+# Internal compression daemon
+#
+# INTERNAL_DAEMON is not set
+IDMSDOSD_TIME=30
+
+#
+# Speedup Tricks & Hacks
+#
+SP_BIT0=y
+SP_BIT1=y
+# SP_BIT2 is not set
+# SP_BIT3 is not set
+SP_BIT4=y
+SP_BIT5=y
+# SP_BIT6 is not set
+SP_BIT7=y
+# SP_BIT8 is not set
diff --git a/src/dmsdos-config.h.default b/src/dmsdos-config.h.default
new file mode 100644
index 0000000..08ff27e
--- /dev/null
+++ b/src/dmsdos-config.h.default
@@ -0,0 +1,24 @@
+/*
+ * Automatically generated C config: don't edit
+ * Run Configure instead
+ */
+#undef DMSDOS_EXPERT
+
+/*
+ * CVF formats to be supported
+ */
+#define DMSDOS_CONFIG_DBLSP_DRVSP 1
+#define DMSDOS_CONFIG_DRVSP3 1
+#define DMSDOS_CONFIG_STAC3 1
+#define DMSDOS_CONFIG_STAC4 1
+
+/*
+ * Misc Options
+ */
+#define DBL_WRITEACCESS 1
+#undef NOLOG
+
+/*
+ * Internal Compression Daemon
+ */
+#undef INTERNAL_DAEMON
diff --git a/src/dmsdos.h b/src/dmsdos.h
new file mode 100644
index 0000000..5606f52
--- /dev/null
+++ b/src/dmsdos.h
@@ -0,0 +1,716 @@
+/*
+dmsdos.h
+
+DMSDOS CVF-FAT module: declaration of dmsdos functions and structures.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifndef _DMSDOS_H
+#define _DMSDOS_H
+
+/* version number hacks */
+#define LVC(x,y,z) ((x)*65536+(y)*256+(z))
+
+#ifdef __KERNEL__
+
+#ifndef LINUX_VERSION_CODE
+ #include<linux/version.h>
+#endif
+#if LINUX_VERSION_CODE == LVC(2,2,1)
+ /* this works around a bug in Linux 2.2.1 */
+ #include<asm/page.h>
+#endif
+#include<asm/semaphore.h>
+#include<linux/fs.h>
+#if LINUX_VERSION_CODE < LVC(2,3,3)
+ #define init_MUTEX(sem) (*sem=MUTEX)
+ #define DECLARE_MUTEX(name) struct semaphore name=MUTEX
+ #define DECLARE_WAIT_QUEUE_HEAD(name) \
+ struct wait_queue * name=NULL
+#endif
+
+#if LINUX_VERSION_CODE >= LVC(2,1,78)
+ #define __FOR_KERNEL_2_1_80
+ #if LINUX_VERSION_CODE < LVC(2,1,80)
+ #define READPAGE_DENTRY
+ #else
+ #define FAT_GET_CLUSTER
+ #if LINUX_VERSION_CODE < LVC(2,3,0)
+ #define READPAGE_INODE
+ #else
+ #if LINUX_VERSION_CODE < LVC(2,3,10)
+ #define READPAGE_FILE
+ #else
+ #define __FOR_KERNEL_2_3_10
+ #if LINUX_VERSION_CODE >= LVC(2,3,30)
+ #define __FOR_KERNEL_2_3_30
+ #define READPAGE_DENTRY
+ #define HAS_SB_CLUSTER_BITS
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+#if (LINUX_VERSION_CODE >= LVC(2,1,0)) && (LINUX_VERSION_CODE < LVC(2,1,78))
+ #error dmsdos 0.9.x needs kernel >= 2.1.80 or use 2.0.33
+#endif
+
+#ifdef FAT_GET_CLUSTER
+#define get_cluster fat_get_cluster
+#endif
+
+#endif /* __KERNEL__*/
+
+#include "dmsdos-config.h"
+/* hacks for new Configure */
+#ifndef DMSDOS_EXPERT
+#define USE_VMALLOC
+#define LISTSIZE 1024
+#define MDFATCACHESIZE 40
+#define DFATCACHESIZE 20
+#define BITFATCACHESIZE 10
+#define MAX_CACHE_TIME 60
+#define CCACHESIZE 64
+#define MAX_CCACHE_TIME 240
+#define MAX_READA 64
+#define USE_READA_LIST
+#define READA_LIST_SIZE 256
+#define READA_THRESHOLD 4095
+#define DEFAULT_LOGLEVEL 0
+#define DEFAULT_CF 11
+#define DMSDOS_USE_READPAGE
+#define IDMSDOSD_TIME 30
+#define SP_BIT0 /* never compress dir */
+#define SP_BIT1 /* never compress EMD */
+#define SP_BIT4 /* write-back caching */
+#define SP_BIT5 /* read-ahead */
+#define SP_BIT7 /* daemon compresses */
+#endif /* DMSDOS_EXPERT */
+
+#ifndef SP_BIT0
+#define SP_BIT0 0
+#else
+#undef SP_BIT0
+#define SP_BIT0 1
+#endif
+
+#ifndef SP_BIT1
+#define SP_BIT1 0
+#else
+#undef SP_BIT1
+#define SP_BIT1 1
+#endif
+
+#ifndef SP_BIT2
+#define SP_BIT2 0
+#else
+#undef SP_BIT2
+#define SP_BIT2 1
+#endif
+
+#ifndef SP_BIT3
+#define SP_BIT3 0
+#else
+#undef SP_BIT3
+#define SP_BIT3 1
+#endif
+
+#ifndef SP_BIT4
+#define SP_BIT4 0
+#else
+#undef SP_BIT4
+#define SP_BIT4 1
+#endif
+
+#ifndef SP_BIT5
+#define SP_BIT5 0
+#else
+#undef SP_BIT5
+#define SP_BIT5 1
+#endif
+
+#ifndef SP_BIT6
+#define SP_BIT6 0
+#else
+#undef SP_BIT6
+#define SP_BIT6 1
+#endif
+
+#ifndef SP_BIT7
+#define SP_BIT7 0
+#else
+#undef SP_BIT7
+#define SP_BIT7 1
+#endif
+
+#ifndef SP_BIT8
+#define SP_BIT8 0
+#else
+#undef SP_BIT8
+#define SP_BIT8 1
+#endif
+
+#ifndef DEFAULT_SPEEDUP
+#define DEFAULT_SPEEDUP ((SP_BIT8<<8)|(SP_BIT7<<7)|(SP_BIT6<<6)|(SP_BIT5<<5)|(SP_BIT4<<4)|(SP_BIT3<<3)|(SP_BIT2<<2)|(SP_BIT1<<1)|(SP_BIT0))
+#endif
+#ifndef DEFAULT_COMP
+#define DEFAULT_COMP GUESS
+#endif
+#ifndef LISTSIZE
+#define LISTSIZE 1024
+#endif
+#ifndef MDFATCACHESIZE
+#define MDFATCACHESIZE 40
+#endif
+#ifndef DFATCACHESIZE
+#define DFATCACHESIZE 20
+#endif
+#ifndef BITFATCACHESIZE
+#define BITFATCACHESIZE 10
+#endif
+#ifndef MAX_CACHE_TIME
+#define MAX_CACHE_TIME 60
+#endif
+#ifndef CCACHESIZE
+#define CCACHESIZE 64
+#endif
+#ifndef MAX_CCACHE_TIME
+#define MAX_CCACHE_TIME 240
+#endif
+#ifndef MAX_READA
+#define MAX_READA 64
+#endif
+#ifndef READA_LIST_SIZE
+#define READA_LIST_SIZE 256
+#endif
+#ifndef READA_THRESHOLD
+#define READA_THRESHOLD 4095
+#endif
+#ifndef DEFAULT_LOGLEVEL
+#define DEFAULT_LOGLEVEL 0
+#endif
+#ifndef DEFAULT_CF
+#define DEFAULT_CF 11
+#endif
+#ifndef IDMSDOSD_TIME
+#define IDMSDOSD_TIME 30
+#endif
+
+
+#define DMSDOS_MAJOR 0
+#define DMSDOS_MINOR 9
+#define DMSDOS_ACT_REL 2
+#define DMSDOS_COMP_REL 2
+#define DMSDOS_PL "2"
+#define DMSDOS_EXTRA "(alpha test)"
+
+#define DMSDOS_VERSION ((DMSDOS_MAJOR<<16)|(DMSDOS_MINOR<<8)|DMSDOS_ACT_REL)
+#define DMSDOS_LOWEST_COMPATIBLE_VERSION ((DMSDOS_MAJOR<<16)|(DMSDOS_MINOR<<8)|DMSDOS_COMP_REL)
+#define DMSDOS_VLT "pl" DMSDOS_PL DMSDOS_EXTRA
+
+/* config hacks */
+#if (defined(DMSDOS_CONFIG_DBLSP_DRVSP) || defined(DMSDOS_CONFIG_DRVSP3))
+#define DMSDOS_CONFIG_DBL
+#endif
+#if (defined(DMSDOS_CONFIG_STAC3) || defined(DMSDOS_CONFIG_STAC4))
+#define DMSDOS_CONFIG_STAC
+#endif
+#if (!defined(DMSDOS_CONFIG_DBL) && !defined(DMSDOS_CONFIG_STAC))
+#error configuration: no CVF format to compile in !!!
+#endif
+
+/* known compression methods */
+#define DS_0_0 0x00005344
+#define DS_0_1 0x01005344
+#define DS_0_2 0x02005344
+#define JM_0_0 0x00004D4A
+/* drivespace 3 high compression */
+#define JM_0_1 0x01004D4A
+/* drivespace 3 ultra compression */
+#define SQ_0_0 0x00005153
+/* stacker 3 compression (no header) */
+#define SD_3 0x00000000
+/* stacker 4 compression */
+#define SD_4 0x00000081
+
+/* other defines for options */
+#define READ_ONLY -1
+#define UNCOMPRESSED -2
+#define GUESS -3
+
+#define MIN_FREE_SECTORS ( (dblsb->s_cvf_version==DRVSP3 \
+ &&(dmsdos_speedup&SP_NO_FRAG_WRITE)==0) \
+ ?dblsb->s_sectperclust+1 \
+ :10*dblsb->s_sectperclust )
+
+typedef struct
+{ unsigned long free_sectors;
+ unsigned long used_sectors;
+ unsigned long max_hole;
+ unsigned long free_clusters;
+ unsigned long used_clusters;
+ unsigned long lost_clusters;
+ unsigned long sectors_lo;
+ unsigned long sectors_hi;
+ unsigned long compressed_clusters;
+ unsigned long uncompressed_clusters;
+} Dblstat;
+
+typedef struct
+{ long sector_minus_1;
+ short size_lo_minus_1;
+ short size_hi_minus_1;
+ short unknown; /* some bits I don't know to handle.... */
+ short flags; /* 00...0uc - u=used, c=compressed */
+} Mdfat_entry;
+
+/* flag values */
+#define D_EMPTY 0
+#define D_VALID 1 /* entry is valid -> cluster to be compressed */
+#define D_IN_D_ACTION 2 /* is being compressed by daemon */
+#define D_OVERWRITTEN 3 /* has been overwritten by dmsdos while daemon
+ is compressing it -> throw away the result from
+ the daemon */
+#ifdef __KERNEL__
+# ifdef USE_XMALLOC
+ void* xmalloc(unsigned long);
+ void xfree(void*);
+# define MALLOC(x) xmalloc(x)
+# define FREE(x) xfree(x)
+# else
+# ifdef USE_VMALLOC
+# include<linux/mm.h>
+# ifdef __FOR_KERNEL_2_1_80
+# include<linux/vmalloc.h>
+# endif
+# define MALLOC(x) vmalloc(x)
+# define FREE(x) vfree(x)
+# else
+# include<linux/malloc.h>
+# define MALLOC(x) kmalloc(x,GFP_KERNEL)
+# define FREE(x) kfree(x)
+# endif
+# endif
+#endif
+
+/* this must be known outside the kernel too */
+typedef struct {
+ int s_dcluster;/*[45-46]*/
+ int s_mdfatstart;/*[36-37]+1*/
+ int s_fatstart;/*[39-40]+[14-15]*/
+ int s_rootdir;/*[41-42]+[39-40]*/
+ int s_rootdirentries;
+ int s_sectperclust;
+ int s_spc_bits;
+ int s_16bitfat;
+ int s_datastart;
+ int s_dataend;
+ int s_comp;
+ int s_bootblock;/*[39-40]*/
+ int s_cfaktor;
+ int s_full;
+ int s_max_cluster;
+ int s_max_cluster2;
+ int s_cvf_version; /* dblsp/drvsp/drvsp3/stac3/stac4 */
+ int s_2nd_fat_offset;
+ int s_lastnear;
+ int s_lastbig;
+ int s_free_sectors;
+ void * mdfat_alloc_semp;
+} Dblsb;
+
+#define DBLSP 0
+#define DRVSP 1
+#define DRVSP3 2
+#define STAC3 3
+#define STAC4 4
+
+#define UC_NORMAL 0
+#define UC_UNCOMPR 1
+#define UC_TEST 2
+#define UC_DIRECT 3
+
+/* cvf version capabilities - boolean values */
+#define DIR_MAY_BE_SHORT(dblsb) (dblsb->s_cvf_version==DRVSP3)
+#define DIR_MAY_BE_COMPRESSED(dblsb) (dblsb->s_cvf_version>=DRVSP3&&(dmsdos_speedup&SP_NO_DIR_COMPR)==0)
+#define UMOUNT_UCFLAG ((dmsdos_speedup&SP_NO_UNMOUNT_COMPR)?UC_UNCOMPR:UC_DIRECT)
+
+typedef struct {
+ struct buffer_head * a_buffer;
+ unsigned int a_area;
+ unsigned long a_time;
+ struct super_block* a_sb;
+ unsigned int a_acc;
+} Acache;
+
+#define C_FREE 0 /* cluster cache entry is free */
+#define C_VALID 1 /* data points to valid cluster data */
+#define C_DIRTY_NORMAL 2 /* like VALID but data need to be written */
+#define C_DIRTY_UNCOMPR 3 /* like VALID but data need to be written */
+#define C_DELETED 4 /* like VALID but last request was a delete */
+#define C_INVALID 5 /* data are junk but valid memory adress */
+#define C_NOT_MALLOCD 6 /* data pointer is not a valid address */
+
+#define C_KEEP_LOCK 1
+#define C_NO_READ 2
+
+#ifdef __KERNEL__
+typedef struct {
+ unsigned int c_time;
+ unsigned int c_flags;
+ unsigned int c_count;
+ unsigned int c_length;
+ unsigned int c_clusternr;
+ struct super_block* c_sb;
+ unsigned char* c_data;
+ struct semaphore c_sem;
+ unsigned int c_errors;
+} Cluster_head;
+#endif /*__KERNEL__*/
+
+int dbl_mdfat_value(struct super_block*sb, int clusternr,
+ Mdfat_entry*new,Mdfat_entry*mde);
+int dbl_fat_nextcluster(struct super_block*sb,int clusternr,int*);
+int dbl_bitfat_value(struct super_block*sb,int sektornr,int*);
+void exit_dbl(struct super_block*sb);
+int find_free_bitfat(struct super_block*sb, int sektornr, int size);
+int dbl_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*,unsigned char*);
+int stac_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*);
+int dbl_compress(unsigned char* clusterk, unsigned char* clusterd,
+ int size, int method,int);
+#if 0
+int stac_compress(void* pin,int lin, void* pout, int lout,
+ int method, int cfaktor);
+#else
+int stac_compress(unsigned char* pin,int lin, unsigned char* pout, int lout,
+ int method, int cfaktor);
+#endif
+int sq_comp(void* pin,int lin, void* pout, int lout, int flg);
+int dbl_decompress(unsigned char*clusterd, unsigned char*clusterk,
+ Mdfat_entry*mde);
+int dmsdos_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag);
+
+#define CHS(i) ( (unsigned short)i[0]|(unsigned short)i[1]<<8 )
+#define CHL(i) ( (unsigned long)i[0]|(unsigned long)i[1]<<8| \
+ (unsigned long)i[2]<<16|(unsigned long)i[3]<<24 )
+
+int dbl_mdfat_cluster2sector(struct super_block*sb,int clusternr);
+int simple_check(struct super_block*sb,int repair);
+void do_spc_init(void);
+void do_spc_exit(void);
+void lock_mdfat_alloc(Dblsb*);
+void unlock_mdfat_alloc(Dblsb*);
+void free_cluster_sectors(struct super_block*sb, int clusternr);
+void stac_special_free(struct super_block*sb, int clusternr);
+int stac_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag);
+int stac_read_cluster(struct super_block*sb,unsigned char*clusterd,
+ int clusternr);
+void free_idle_cache(void);
+void free_idle_ccache(void);
+void ccache_init(void);
+void free_ccache_dev(struct super_block*sb);
+#ifdef __KERNEL__
+Cluster_head* ch_read(struct super_block*sb,int clusternr,int flag);
+Cluster_head* find_in_ccache(struct super_block*sb,
+ int clusternr,Cluster_head**lastfree,
+ Cluster_head**oldest);
+int ch_dirty(Cluster_head*,int near,int ucflag);
+int ch_dirty_locked(Cluster_head*,int near,int ucflag);
+void lock_ch(Cluster_head*);
+void unlock_ch(Cluster_head*);
+void ch_dirty_retry_until_success(Cluster_head*,int near,int ucflag);
+void ch_free(Cluster_head*);
+#endif
+void sync_cluster_cache(int allow_daemon);
+void delete_cache_cluster(struct super_block*sb, int clusternr);
+void log_list_statistics(void);
+void log_ccache_statistics(void);
+void log_found_statistics(void);
+int sq_dec(void* pin,int lin, void* pout, int lout, int flg);
+
+/* Stacker cluster allocation types access */
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+/* structure for walking/working with each sector of cluster */
+typedef struct {
+ struct super_block*sb;
+ int clusternr;
+ int start_sect;
+ int start_len;
+ int flags;
+ int sect_cnt;
+ int compressed;
+ int bytes_in_last;
+ int bytes_in_clust;
+ struct buffer_head *fbh; /* first sector of fragmented clust */
+ /* changes during fragments reads */
+ int fcnt; /* count of unreaded fragments */
+ int flen; /* rest sectors in fragment */
+ int sect; /* actual sector */
+ int offset; /* byte offset in sector */
+ int bytes; /* number of data bytes in sector */
+ unsigned char* finfo; /* points to actual field in fbh */
+} Stac_cwalk;
+
+int stac_cwalk_init(Stac_cwalk *cw,struct super_block*sb,
+ int clusternr,int flg);
+int stac_cwalk_sector(Stac_cwalk *cw);
+void stac_cwalk_done(Stac_cwalk *cw);
+#endif /* __KERNEL__||__DMSDOS_LIB__*/
+
+/* loglevel defines */
+
+extern unsigned long loglevel;
+
+#ifdef SEQLOG
+int log_prseq(void);
+#define LOGCMD if(log_prseq())printk
+#else
+#define LOGCMD printk
+#endif
+
+#ifndef NOLOG
+#define LOG_FS if(loglevel&0x00000001)LOGCMD
+#define LOG_CLUST if(loglevel&0x00000002)LOGCMD
+#define LOG_LLRW if(loglevel&0x00000008)LOGCMD
+#define LOG_DFAT if(loglevel&0x00000010)LOGCMD
+#define LOG_MDFAT if(loglevel&0x00000020)LOGCMD
+#define LOG_BITFAT if(loglevel&0x00000040)LOGCMD
+#define LOG_DECOMP if(loglevel&0x00000080)LOGCMD
+#define LOG_COMP if(loglevel&0x00000100)LOGCMD
+#define LOG_ALLOC if(loglevel&0x00000200)LOGCMD
+#define LOG_DAEMON if(loglevel&0x00000400)LOGCMD
+#define LOG_CCACHE if(loglevel&0x00000800)LOGCMD
+#define LOG_ACACHE if(loglevel&0x00001000)LOGCMD
+#define LOG_REST if(loglevel&0x80000000)LOGCMD
+#else
+#define LOG_FS(x,args...)
+#define LOG_CLUST(x,args...)
+#define LOG_LLRW(x,args...)
+#define LOG_DFAT(x,args...)
+#define LOG_MDFAT(x,args...)
+#define LOG_BITFAT(x,args...)
+#define LOG_DECOMP(x,args...)
+#define LOG_COMP(x,args...)
+#define LOG_ALLOC(x,args...)
+#define LOG_DAEMON(x,args...)
+#define LOG_CCACHE(x,args...)
+#define LOG_ACACHE(x,args...)
+#define LOG_REST(x,args...)
+#endif
+
+#ifdef __FOR_KERNEL_2_1_80
+/* some hacks since the memcpy_from/tofs functions have changed here */
+#include <asm/uaccess.h>
+#define memcpy_fromfs copy_from_user
+#define memcpy_tofs copy_to_user
+#endif
+
+struct buffer_head *raw_bread (
+ struct super_block *sb,
+ int block);
+struct buffer_head *raw_getblk (
+ struct super_block *sb,
+ int block);
+void raw_brelse (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void raw_mark_buffer_dirty (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int dirty_val);
+void raw_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int raw_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void raw_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32]);
+
+#define FAKED_ROOT_DIR_OFFSET 1
+#define FAKED_DATA_START_OFFSET 1000
+int dmsdos_read_cluster(struct super_block*sb,
+ unsigned char*clusterd, int clusternr);
+
+struct buffer_head* dblspace_bread(struct super_block*sb,int vsector);
+struct buffer_head *dblspace_getblk (
+ struct super_block *sb,
+ int block);
+void dblspace_brelse(struct super_block* sb,struct buffer_head*bh);
+void dblspace_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,
+ int dirty_val);
+void dblspace_set_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh,
+ int val);
+int dblspace_is_uptodate (
+ struct super_block *sb,
+ struct buffer_head *bh);
+void dblspace_ll_rw_block (
+ struct super_block *sb,
+ int opr,
+ int nbreq,
+ struct buffer_head *bh[32]);
+int stac_bitfat_state(struct super_block*sb,int new_state);
+int dblspace_fat_access(struct super_block*sb, int clusternr,int newval);
+#ifdef __KERNEL__
+int dblspace_bmap(struct inode*inode, int block);
+int dblspace_smap(struct inode*inode, int block);
+#endif
+int ds_dec(void* pin,int lin, void* pout, int lout, int flg);
+#ifdef __KERNEL__
+void dblspace_zero_new_cluster(struct inode*,int clusternr);
+#ifdef __FOR_KERNEL_2_1_80
+ssize_t dblspace_file_read(struct file *filp,char *buf,size_t count,
+ loff_t *ppos);
+ssize_t dblspace_file_write(struct file *filp,const char *buf,size_t count,
+ loff_t *ppos);
+int dblspace_mmap(struct file*file,
+ struct vm_area_struct*vma);
+#else
+int dblspace_file_read(struct inode *inode,struct file *filp,char *buf,
+ int count);
+int dblspace_file_write(struct inode *inode,struct file *filp,const char *buf,
+ int count);
+int dblspace_mmap(struct inode*inode,struct file*file,
+ struct vm_area_struct*vma);
+#endif
+
+#ifdef READPAGE_DENTRY
+ int dblspace_readpage(struct dentry*dentry, struct page *page);
+#else
+ #ifdef READPAGE_FILE
+ int dblspace_readpage(struct file *file, struct page *page);
+ #else
+ #ifdef READPAGE_INODE
+ int dblspace_readpage(struct inode *inode, struct page *page);
+ #else
+ #error Unknown readpage parameters
+ #endif
+ #endif
+#endif
+
+int dmsdos_ioctl_dir(struct inode *dir,struct file *filp,
+ unsigned int cmd, unsigned long data);
+#endif /* __KERNEL__ */
+int try_daemon(struct super_block*sb,int clusternr, int length, int method);
+void remove_from_daemon_list(struct super_block*sb,int clusternr);
+void force_exit_daemon(void);
+void dblspace_reada(struct super_block*sb, int sector,int count);
+void init_reada_list(void);
+void kill_reada_list_dev(int dev);
+int daemon_write_cluster(struct super_block*sb,unsigned char*data,
+ int len, int clusternr, int rawlen);
+void check_free_sectors(struct super_block*sb);
+void get_memory_usage_acache(int*, int*max);
+void get_memory_usage_ccache(int*, int*max);
+int mount_dblspace(struct super_block*sb,char*options);
+int mount_stacker(struct super_block*sb,char*options);
+int detect_dblspace(struct super_block*sb);
+int detect_stacker(struct super_block*sb);
+int unmount_dblspace(struct super_block*sb);
+
+typedef struct
+{ int clusternr;
+ struct super_block*sb;
+ int length; /* in bytes */
+ char flag;
+ int method;
+} Rwlist;
+
+void init_daemon(void);
+void exit_daemon(void);
+void clear_list_dev(struct super_block*sb);
+
+/* speedup bits */
+#define SP_NO_DIR_COMPR 0x0001
+#define SP_NO_EMD_COMPR 0x0002
+#define SP_NO_EXACT_SEARCH 0x0004
+#define SP_NO_UNMOUNT_COMPR 0x0008
+#define SP_USE_WRITE_BACK 0x0010
+#define SP_USE_READ_AHEAD 0x0020
+#define SP_FAST_BITFAT_ALLOC 0x0040
+#define SP_USE_DAEMON 0x0080
+#define SP_NO_FRAG_WRITE 0x0100
+
+typedef struct
+{ int ccachebytes;
+ int max_ccachebytes;
+ int acachebytes;
+ int max_acachebytes;
+} Memuse;
+
+
+#define DMSDOS_IOCTL_MIN 0x2000
+#define DMSDOS_IOCTL_MAX 0x201F
+#define DMSDOS_GET_DBLSB 0x2000
+#define DMSDOS_EXTRA_STATFS 0x2001
+#define DMSDOS_READ_BLOCK 0x2002
+#define DMSDOS_WRITE_BLOCK 0x2003
+#define DMSDOS_READ_DIRENTRY 0x2004 /* obsolete */
+#define DMSDOS_WRITE_DIRENTRY 0x2005 /* obsolete */
+#define DMSDOS_READ_BITFAT 0x2006
+#define DMSDOS_WRITE_BITFAT 0x2007
+#define DMSDOS_READ_MDFAT 0x2008
+#define DMSDOS_WRITE_MDFAT 0x2009
+#define DMSDOS_READ_DFAT 0x200a
+#define DMSDOS_WRITE_DFAT 0x200b
+#define DMSDOS_SET_COMP 0x200c
+#define DMSDOS_SET_CF 0x200d
+#define DMSDOS_SIMPLE_CHECK 0x200e
+#define DMSDOS_DUMPCACHE 0x200f
+#define DMSDOS_D_ASK 0x2010
+#define DMSDOS_D_READ 0x2011
+#define DMSDOS_D_WRITE 0x2012
+#define DMSDOS_D_EXIT 0x2013
+#define DMSDOS_MOVEBACK 0x2014 /* obsolete */
+#define DMSDOS_SET_MAXCLUSTER 0x2015 /* currently not supported */
+#define DMSDOS_READ_CLUSTER 0x2016
+#define DMSDOS_FREE_IDLE_CACHE 0x2017
+#define DMSDOS_SET_LOGLEVEL 0x2018
+#define DMSDOS_SYNC_CCACHE 0x2019
+#define DMSDOS_LOG_STATISTICS 0x201a
+#define DMSDOS_SET_SPEEDUP 0x201b
+#define DMSDOS_RECOMPRESS 0x201c /* obsolete */
+#define DMSDOS_REPORT_MEMORY 0x201d
+#define IS_DMSDOS_IOCTL(cmd) ((cmd)>=DMSDOS_IOCTL_MIN&&(cmd)<=DMSDOS_IOCTL_MAX)
+
+/* dmsdos library interface */
+struct super_block* open_cvf(char*filename,int rwflag);
+void close_cvf(struct super_block*sb);
+
+#endif
diff --git a/src/dmsdosfsck.c b/src/dmsdosfsck.c
new file mode 100644
index 0000000..8e66b7f
--- /dev/null
+++ b/src/dmsdosfsck.c
@@ -0,0 +1,674 @@
+/*
+
+dmsdosfsck.c
+
+DMSDOS: filesystem check utility for CVFs.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<ctype.h>
+#include<unistd.h>
+#include"dmsdos.h"
+#include"lib_interface.h"
+
+int check_dir(int parent,int clusternr);
+
+/*this is not good - but currently we have only one CVF open at a time*/
+struct super_block*sb;
+Dblsb*dblsb;
+
+typedef struct
+{ int referrenced_from;
+ int start;
+} Fatdata;
+
+Fatdata fat[65536];
+
+#define FAT_EOF -1
+
+int seenlist[65536];
+int repair_automatically=0;
+int repair_interactively=0;
+int verbose=0;
+int listfiles=0;
+
+#define vprintf if(verbose)printf
+#define lprintf if(listfiles)printf
+
+int repair(char*text)
+{ int c;
+
+ if(repair_automatically)return 1;
+ if(repair_interactively==0)return 0;
+ fflush(stdin);
+ printf("%s",text);
+ fflush(stdout);
+ c=fgetc(stdin);
+ fflush(stdin);
+ if(c=='y'||c=='Y')return 1;
+ return 0;
+}
+
+int check_fat_loop(int begin)
+{ int seen=0;
+ int next;
+ int i;
+ int newval;
+
+ while(begin>=2&&begin<=dblsb->s_max_cluster)
+ { /* add to seenlist */
+ seenlist[seen]=begin;
+ ++seen;
+ if(seen>65535)return -1; /* cannot happen */
+ next=dbl_fat_nextcluster(sb,begin,NULL);
+
+ /* check whether it was already seen */
+ for(i=0;i<seen;++i)
+ { if(seenlist[i]==next)
+ { /* here begins a fat loop */
+ printf("FAT loop at cluster %d found.\n",begin);
+ if(repair("Break it?")==0)return 1;
+ newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,begin,&newval);
+ return 0;
+ }
+ }
+
+ begin=next;
+ }
+
+ return 0;
+}
+
+int check_fat()
+{ int i;
+ int val;
+ int errors=0;
+ int newval=0;
+
+ for(i=0;i<65536;++i)
+ { fat[i].referrenced_from=0;
+ fat[i].start=0;
+ }
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { vprintf("Checking cluster %d...\n",i);
+
+ val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val)
+ { if(val==1)
+ { printf("cluster %d: invalid fat entry\n",i);
+ if(repair("Correct it?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,i,&newval);
+ }
+ }
+ if(check_fat_loop(i))
+ { printf("Unresolved FAT loop in filesystem. Can't continue, sorry.\n");
+ close_cvf(sb);
+ exit(4);
+ }
+ }
+ if(val>=2&&val<=dblsb->s_max_cluster)
+ { if(fat[val].referrenced_from)
+ { /* this is a crosslink */
+ printf("cluster %d is crosslinked with %d to %d\n",
+ i,fat[val].referrenced_from,val);
+ if(repair("Break it?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,i,&newval);
+ dbl_fat_nextcluster(sb,fat[val].referrenced_from,&newval);
+ fat[val].referrenced_from=0;
+ }
+ }
+ fat[val].referrenced_from=i;
+ }
+ }
+
+ return errors;
+}
+
+int check_chains()
+{ int i;
+ int val;
+ int errors=0;
+ int newval=0;
+ int next;
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val>=2&&val<=dblsb->s_max_cluster&&fat[i].referrenced_from==0)
+ { /* this is the start of a chain */
+ vprintf("checking chain beginning at cluster %d...\n",i);
+
+ rchain:
+ next=dbl_fat_nextcluster(sb,val,NULL);
+ if(next==FAT_EOF)continue;
+
+ if(next==0||next>dblsb->s_max_cluster)
+ { printf("chain breaks unexpectedly.\n");
+ if(repair("Set proper end?")==0)++errors;
+ else
+ { newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,val,&newval);
+ }
+ }
+ else
+ { val=next;
+ goto rchain;
+ }
+
+ }
+ }
+ return 0;
+}
+
+struct nametest
+{ unsigned char name[12];
+ struct nametest*next;
+} nametest;
+
+int add_name(struct nametest**namelist,unsigned char*text)
+{ struct nametest*akt=*namelist;
+
+ while(akt)
+ { if(strncmp(akt->name,text,11)==0)return -1;
+ akt=akt->next;
+ }
+
+ akt=malloc(sizeof(struct nametest));
+ if(akt==NULL)return -2;
+
+ strncpy(akt->name,text,11);
+ akt->next=*namelist;
+ *namelist=akt;
+
+ return 0;
+}
+
+void free_namelist(struct nametest**namelist)
+{ struct nametest*akt=*namelist;
+ struct nametest*merk;
+
+ while(akt)
+ { merk=akt->next;
+ free(akt);
+ akt=merk;
+ }
+
+ *namelist=NULL;
+}
+
+int check_char(unsigned char c,int noprint)
+{ if(c==0x5)c=0xE5; /* M$ hack for languages where E5 is a valid char */
+ if(c<32||strchr("+\\?*<>|\"=,;",c)!=NULL)
+ { if(!noprint)lprintf("?");
+ return 1;
+ }
+ if(!noprint)lprintf("%c",c);
+ return 0;
+}
+
+int check_direntry(int dirstartclust, unsigned char*data, int*need_write,
+ struct nametest**namelist)
+{ int i;
+ unsigned int x;
+ unsigned char*pp;
+ unsigned long size;
+ int cluster, prevcluster, newval;
+ unsigned long fatsize;
+ int clustersize;
+ int invchar;
+
+ *need_write=0;
+
+ if(data[0]==0||data[0]==0xE5)return 0;
+
+ if(data[11]&8)return 0; /* ignore V entries */
+
+ invchar=0;
+ for(i=0;i<11;++i)
+ { if(i==8)lprintf(" ");
+ invchar+=check_char(data[i],0);
+ }
+
+ if(listfiles)
+ {
+ printf(" ");
+ if(data[11]&1)printf("R");else printf(" ");
+ if(data[11]&2)printf("H");else printf(" ");
+ if(data[11]&4)printf("S");else printf(" ");
+ if(data[11]&8)printf("V");else printf(" ");
+ if(data[11]&16)printf("D");else printf(" ");
+ if(data[11]&32)printf("A");else printf(" ");
+ if(data[11]&64)printf("?");else printf(" ");
+ if(data[11]&128)printf("?");else printf(" ");
+
+ pp=&(data[22]);
+ x=CHS(pp);
+ printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);
+
+ pp=&(data[24]);
+ x=CHS(pp);
+ printf(" %02d.%02d.%4d",x&31,(x>>5)&15,(x>>9)+1980); /* y2k compliant :) */
+ }
+
+ pp=&(data[26]);
+ cluster=CHS(pp);
+ lprintf(" %5d",cluster);
+
+ pp=&(data[28]);
+ size=CHL(pp);
+ lprintf(" %7lu ",size);
+
+ if(invchar)
+ { printf("name has invalid chars, ");
+ if(repair("Replace them?")!=0)
+ { for(i=0;i<11;++i)
+ { if(check_char(data[i],1))
+ { data[i]='~';
+ *need_write=1;
+ }
+ }
+ }
+ }
+
+ if(add_name(namelist,data))
+ { printf("duplicate filename ");
+ for(i=0;i<11;++i)
+ { if(i==8)printf(" ");
+ printf("%c",data[i]);
+ }
+ printf("\n");
+
+ if(repair("Rename?")!=0)
+ { char teststr[10];
+ int n=1;
+
+ i=7;
+ while(i>=0)
+ { if(data[i]==' ')data[i]='~';
+ else break;
+ --i;
+ }
+
+ do
+ { sprintf(teststr,"~%d",n++);
+ for(i=0;i<strlen(teststr);++i)data[i+8-strlen(teststr)]=teststr[i];
+ }
+ while(add_name(namelist,data));
+ *need_write=1;
+ }
+ }
+
+ if(cluster<0||cluster==1||cluster>dblsb->s_max_cluster)
+ { printf("clusternr invalid\n");
+ if(repair("Truncate?")==0)return -1;
+ data[26]=0;
+ data[27]=0;
+ cluster=0;
+ *need_write=1;
+ }
+ else
+ { if(cluster)
+ { if(fat[cluster].referrenced_from!=0||fat[cluster].start!=0)
+ { printf("first cluster crosslink\n");
+ if(repair("Truncate?")==0)return -1;
+ data[26]=0;
+ data[27]=0;
+ cluster=0;
+ *need_write=1;
+ }
+ else fat[cluster].start=1;
+ }
+ }
+
+ if(data[11]&16) /* dir */
+ { if(cluster==0)
+ { printf("clusternr invalid for subdir\n");
+ goto irrepdir;
+ }
+ lprintf("OK\n");
+ lprintf("descending directory...\n");
+ i=check_dir(dirstartclust,cluster);
+ lprintf("ascending...\n");
+ if(i>=0)
+ { if(size)
+ { printf("directory entry has size !=0\n");
+ if(repair("Correct this?")==0)++i;
+ else
+ { data[28]=0;
+ data[29]=0;
+ data[30]=0;
+ data[31]=0;
+ size=0;
+ *need_write=1;
+ }
+ }
+ return i;
+ }
+ irrepdir:
+ printf("directory is irreparably damaged");
+ if(repair("Convert to file?")==0)return -1;
+ data[11]&=~16;
+ *need_write=1;
+ /* fall through */
+ }
+
+ if(cluster==0)
+ { if(size==0)
+ { lprintf("OK\n");
+ return 0;
+ }
+ printf("wrong size\n");
+ if(repair("Correct this?")==0)return -1;
+ else
+ { data[28]=0;
+ data[29]=0;
+ data[30]=0;
+ data[31]=0;
+ size=0;
+ *need_write=1;
+ }
+ return 0;
+ }
+
+ clustersize=dblsb->s_sectperclust*SECTOR_SIZE;
+
+ fatsize=0;
+ prevcluster=0;
+ while(cluster>1&&cluster<=dblsb->s_max_cluster)
+ { prevcluster=cluster;
+ cluster=dbl_fat_nextcluster(sb,cluster,NULL);
+ fatsize+=clustersize;
+ }
+ if(cluster==0)
+ { printf("fat alloc ends with zero\n");
+ cluster=prevcluster;
+ fatsize-=clustersize;
+ if(repair("Correct this?")==0)return -1;
+ newval=FAT_EOF;
+ dbl_fat_nextcluster(sb,cluster,&newval);
+ }
+ if(cluster==1||cluster>dblsb->s_max_cluster)
+ { printf("fat alloc invalid\n");
+ return -1;
+ }
+
+ if(size==fatsize)
+ { lprintf("OK\n");
+ return 0;
+ }
+ if(size/clustersize==(fatsize-1)/clustersize)
+ { lprintf("OK\n");
+ return 0;
+ }
+ printf("file size wrong\n");
+ if(repair("Recalculate file size?")==0)return -1;
+ data[28]=fatsize;
+ data[29]=fatsize>>8;
+ data[30]=fatsize>>16;
+ data[31]=fatsize>>24;
+ *need_write=1;
+ return 0;
+}
+
+int check_root_dir(void)
+{ int i,j,errors,r;
+ struct buffer_head*bh;
+ int need_write=0;
+ struct nametest*namelist=NULL;
+
+ errors=0;
+
+ for(i=0;i<dblsb->s_rootdirentries/16;++i)
+ { bh=raw_bread(sb,dblsb->s_rootdir+i);
+ if(bh==NULL)return -1;
+ for(j=0;j<16;++j)
+ { r=check_direntry(0,&(bh->b_data[j*32]),&need_write,&namelist);
+ if(r)++errors;
+ else if(need_write)raw_mark_buffer_dirty(sb,bh,1);
+ }
+ raw_brelse(sb,bh);
+ }
+
+ free_namelist(&namelist);
+ return errors;
+}
+
+int check_dir(int parent,int clusternr)
+{ unsigned char*data;
+ int j,errors,r;
+ int next;
+ int start=2;
+ int dirstartclust=clusternr;
+ int need_write=0;
+ int len;
+ struct nametest*namelist=NULL;
+
+ errors=0;
+ data=malloc(dblsb->s_sectperclust*SECTOR_SIZE);
+ if(data==NULL)return -1;
+
+ vprintf("checking directory at start cluster %d...\n",clusternr);
+
+ next=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(next==0)
+ { printf("warning: dir cluster %d is marked free\n",clusternr);
+ }
+
+ while(clusternr>0)
+ { len=dmsdos_read_cluster(sb,data,clusternr);
+ if(len<0){free(data);return -1;}
+
+ if(start)
+ { unsigned char*pp;
+
+ /* check . 12345678EXT */
+ if(strncmp(data,". ",11)!=0)
+ { printf("first entry is not '.'\n");
+ ++errors;
+ }
+ pp=&(data[26]);
+ if(CHS(pp)!=clusternr)
+ { printf("self cluster nr is wrong in '.'\n");
+ ++errors;
+ }
+
+ /* check .. 12345678EXT */
+ if(strncmp(data+32,".. ",11)!=0)
+ { printf("second entry is not '..'\n");
+ ++errors;
+ }
+ pp=&(data[26+32]);
+ if(CHS(pp)!=parent)
+ { printf("parent cluster nr is wrong in '..'\n");
+ ++errors;
+ }
+
+ if(errors)
+ { printf("This doesn't look like a directory, skipped\n");
+ free(data);
+ return -1;
+ }
+ }
+
+ for(j=start;j<dblsb->s_sectperclust*16;++j)
+ { r=check_direntry(dirstartclust,&(data[j*32]),&need_write,&namelist);
+ if(r)++errors;
+ else if(need_write)dmsdos_write_cluster(sb,data,len,clusternr,0,1);
+ }
+
+ start=0;
+ next=dbl_fat_nextcluster(sb,clusternr,NULL);
+ if(next==1||next>dblsb->s_max_cluster)
+ { printf("directory ends with fat alloc error\n");
+ ++errors;
+ break;
+ }
+ clusternr=next;
+ }
+
+ free(data);
+ free_namelist(&namelist);
+ return errors;
+}
+
+int check_unused_chains()
+{ int i;
+ int val;
+ int errors=0;
+
+ for(i=2;i<=dblsb->s_max_cluster;++i)
+ { val=dbl_fat_nextcluster(sb,i,NULL);
+ if(val>=2&&val<=dblsb->s_max_cluster&&fat[i].referrenced_from==0
+ &&fat[i].start==0)
+ { vprintf("chain beginning with cluster %d is unused.\n",i);
+ /* if(repair("Delete it?")==0)++errors;
+ else
+ { free_chain(sb,i);
+ }
+ */
+ ++errors;
+ }
+ }
+
+ return errors;
+}
+
+int main(int argc, char*argv[])
+{ int errors=0;
+ int i;
+ char*filename=NULL;
+
+ fprintf(stderr, "dmsdosfsck 0.0.2 ALPHA TEST (be extremely careful with repairs)\n");
+
+ if(argc==1)
+ { usage:
+ fprintf(stderr, "usage: dmsdosfsck [ -aflrtvVw ] [ -d path -d ... ] [ -u path -u ... ] device\n"
+ "-a automatically repair the file system\n"
+ "-d path (*) drop that file\n"
+ "-f (*) salvage unused chains to files\n"
+ "-l list path names\n"
+ "-r interactively repair the file system\n"
+ "-t (*) test for bad clusters\n"
+ "-u path (*) try to undelete that (non-directory) file\n"
+ "-v verbose mode\n"
+ "-V (*) perform a verification pass\n"
+ "-w (*) write changes to disk immediately\n"
+ "(*) not yet implemented but option accepted for dosfsck compatibility\n");
+ exit(16);
+ }
+
+ for(i=1;i<argc;++i)
+ { if(strcmp(argv[i],"-a")==0)repair_automatically=1;
+ else if(strcmp(argv[i],"-r")==0)repair_interactively=1;
+ else if(strcmp(argv[i],"-l")==0)listfiles=1;
+ else if(strcmp(argv[i],"-v")==0)verbose=1;
+ else if(argv[i][0]!='-')filename=argv[i];
+ }
+
+ if(filename==NULL)goto usage;
+
+ if(repair_automatically!=0||repair_interactively!=0)
+ { printf("\n\nWARNING: repair functions are incomplete. Interrupt within 5 seconds to abort.\7\n\n\n");
+ sleep(5);
+ }
+
+ sb=open_cvf(filename,repair_automatically|repair_interactively);
+ if(sb==NULL)
+ { fprintf(stderr,"open_cvf %s failed - maybe this isn't a CVF.\n",filename);
+ exit(16);
+ }
+
+ dblsb=MSDOS_SB(sb)->private_data;
+
+ printf("pass 1: checking FAT...\n");
+
+ i=check_fat();
+ if(i)
+ { printf("Filesystem has fatal FAT errors\n");
+ printf("Cannot continue, sorry.\n");
+ printf("Don't mount this filesystem, it may crash or hang the FAT driver.\n");
+ close_cvf(sb);
+ exit(4);
+ }
+
+ printf("pass 2: checking cluster chains...\n");
+
+ i=check_chains();
+ if(i)
+ { printf("filesystem has errors in cluster chains\n");
+ ++errors;
+ }
+
+ printf("pass 3: calling dmsdos simple_check...\n");
+
+ i=simple_check(sb,0);
+ if(i==-1) /* there were fatal FAT errors detected */
+ { printf("Filesystem still has fatal FAT errors\n");
+ printf("CANNOT HAPPEN. THIS IS A BUG.\n");
+ close_cvf(sb);
+ abort();
+ }
+ if(i)
+ { printf("filesystem has low-level structure errors\n");
+ if(repair("Try to fix them?")==0)++errors;
+ else
+ { i=simple_check(sb,1);
+ if(i)
+ { printf("couldn't fix all low-level structure errors\n");
+ ++errors;
+ }
+ }
+ }
+
+ printf("pass 4: checking directories...\n");
+
+ if(check_root_dir())
+ { printf("filesystem has msdos-level structure errors\n");
+ ++errors;
+ }
+
+ printf("pass 5: checking for unused chains...\n");
+
+ i=check_unused_chains();
+ if(i)
+ { printf("filesystem has unused cluster chains\n");
+ ++errors;
+ }
+
+ close_cvf(sb);
+
+ if(errors==0)
+ { printf("filesystem has no errors\n");
+ }
+
+ return (errors==0)?0:4;
+}
diff --git a/src/dstacker_alloc.c b/src/dstacker_alloc.c
new file mode 100644
index 0000000..a0b3762
--- /dev/null
+++ b/src/dstacker_alloc.c
@@ -0,0 +1,477 @@
+/*
+dstacker_alloc.c
+
+DMSDOS CVF-FAT module: stacker allocation routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<errno.h>
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+
+/* initializes Stac_cwalk structure, which can be used for sequential
+ access to all sectors of cluster and when needed informs about
+ all data areas in every sector. flg parameter speedups initialization
+ when some informations are not necessarry
+ flg = 0 .. only sector numbers
+ flg = 1 .. sector numbers and data without ending suballocation
+ flg = 2 .. sector numbers and exact data areas
+ flg = 3 .. same as 2 but more checking
+*/
+int stac_cwalk_init(Stac_cwalk *cw,struct super_block*sb,
+ int clusternr,int flg)
+{
+ __u8 *pp;
+ unsigned u,v;
+ int i;
+ int last_sect;
+ Mdfat_entry mde;
+ struct buffer_head *bh;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+ cw->finfo=NULL;
+ cw->fbh=NULL;
+ cw->sect=0;
+ cw->sb=sb;
+ cw->clusternr=clusternr;
+ /* -------------------------------------------- */
+ dbl_mdfat_value(sb,clusternr,NULL,&mde);
+ cw->start_sect=mde.sector_minus_1+1;
+ cw->sect_cnt=cw->start_len=mde.size_lo_minus_1+1;
+ cw->flags=mde.flags;
+ if(!cw->start_sect) {cw->fcnt=0;return 0;};
+ /* -------------------------------------------- */
+ cw->fcnt=1;
+ cw->flen=cw->sect_cnt=cw->start_len;
+ cw->bytes_in_clust=cw->sect_cnt*SECTOR_SIZE;
+ cw->offset=0;
+ cw->bytes=SECTOR_SIZE;
+ cw->bytes_in_last=0;
+ last_sect=cw->start_sect+cw->sect_cnt-1;
+ /* -------------------------------------------- */
+ u=(cw->flags&0xe0)>>5;
+ if(cw->start_len==dblsb->s_sectperclust) u|=0x8;
+ switch(u)
+ { case 0x0: /* ffff=000x, len<ClustSize, compressed, regular file/dir */
+ case 0x2: /* ffff=010x, deleted 0 */
+ cw->compressed=1;
+ break;
+
+ case 0x1: /* ffff=001x, not compressed but not full size, regular file/dir */
+ case 0x3: /* ffff=011x, deleted 1 */
+ case 0x8: /* ffff=000x, len=ClustSize, not compressed, regular file/dir */
+ case 0xa: /* ffff=010x, deleted 8 */
+ case 0xc: /* ffff=100x, len=ClustSize, directory, never compressed */
+ case 0xe: /* ffff=110x, deleted c */
+ cw->compressed=0;
+ break;
+
+ case 0x4: /* ffff=100x, len<ClustSize, fragmented, regular file/dir */
+ case 0x6: /* ffff=110x, deleted 4 */
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=bh->b_data;
+ if(pp[0]!=0xed) /* check fragment signature */
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: fragment signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fcnt=pp[1]+1; /* number of pieces */
+ if((cw->fcnt>dblsb->s_sectperclust)||(cw->fcnt*4>SECTOR_SIZE))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: too much fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ cw->compressed=!(pp[2]&0x80);
+ v=cw->sect_cnt;
+ cw->sect_cnt+=(pp[2]&0x3F)+1;
+ for(i=1;i<cw->fcnt;++i)
+ { pp+=4;
+ u=(pp[2]&0xf)+((pp[3]>>2)&0x30);
+ v+=u+1;
+ };
+ last_sect=pp[0]+(pp[1]<<8)+((pp[3]&0x3f)<<16)+u; /* for suballocation tests */
+ if(v!=cw->sect_cnt)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: sector count mismash fragmented cluster=%d!\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ cw->fbh=bh;
+ cw->finfo=bh->b_data+4;
+ cw->offset=4*cw->fcnt;
+ cw->bytes=SECTOR_SIZE-cw->offset;
+ cw->bytes_in_clust=(cw->sect_cnt-1)*SECTOR_SIZE+cw->bytes;
+ break;
+
+ case 0x5: /* ffff=101x, len<ClustSize, suballocated, regular file/dir(?) */
+ case 0x7: /* ffff=111x, deleted 5 */
+ case 0xd: /* ffff=101x, len=ClustSize, suballocated, regular file/dir(?) */
+ case 0xf: /* ffff=111x, deleted d */
+ if(flg==0) {cw->bytes_in_clust=0;cw->bytes=0;return 1;};
+ bh=raw_bread(sb,cw->start_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation signature not found cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ }
+ if(cw->start_len==1)
+ { /* short suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-6]);
+ cw->offset= CHS(pp) & (SECTOR_SIZE - 1); /* begin of area */
+ cw->compressed= !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->bytes=CHS(pp) & (SECTOR_SIZE - 1); /* end of area */
+ /* some more paranoic checking of allocated space */
+ if (cw->bytes&&(cw->bytes<=SECTOR_SIZE-8)&&(cw->bytes>cw->offset))
+ cw->bytes-=cw->offset;
+ else
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in short subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ cw->bytes_in_clust=cw->bytes;
+ last_sect=0;
+ }
+ else
+ { /* long suballocation */
+ pp = &(bh->b_data[SECTOR_SIZE-8]);
+ cw->offset = CHS(pp) & (SECTOR_SIZE - 1);
+ cw->compressed = !(CHS(pp) & 0x8000);
+ if(CHS(pp)&0x4000)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation not present, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh); return -1;
+ };
+ cw->bytes=SECTOR_SIZE-cw->offset-8;
+ cw->bytes_in_clust+=cw->bytes-SECTOR_SIZE;
+ if (cw->bytes < 0) {
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: count = %d < 0 in long subalocated\n", cw->bytes);
+ printk(KERN_ERR "DMSDOS: cluster %d read error\n",clusternr);
+ raw_brelse(sb,bh); return -1;
+ }
+ };
+ raw_brelse(sb,bh);
+ break;
+
+ case 0x9: /* ffff=001x, contradiction ??? */
+ case 0xb: /* ffff=011x, deleted 9 */
+ default:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: unknown flags 0x%2x cluster %d\n",
+ mde.flags,clusternr);
+ return -1;
+ };
+
+ /* if flg>=2 we are checking subalocated end of cluster */
+ /* because of we did not know if stacker can subalocate clusters
+ which are not in order we must check nonlinear
+ suballocation */
+ /* text above is question but now I know that stacker is able to do everything
+ nonlinear suballocation regulary exist */
+ if(last_sect&&(dblsb->s_cvf_version>=STAC4)&&(flg>=2))
+ { /* check for subalocated end of cluster */
+ /* 1) check start of next cluster for linear subalocation */
+ if(clusternr<dblsb->s_max_cluster)
+ { dbl_mdfat_value(sb,clusternr+1,NULL,&mde);
+ i=(mde.sector_minus_1+1)==last_sect;
+ if(i&&((mde.flags&0xA0)!=0xA0))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: wrong cluster types for subalocation, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ } else i=0;
+ /* 2) check for nonlinear subalocation */
+ if((u=dbl_bitfat_value(sb,last_sect,NULL))==0) if(flg>=3)goto error1;
+ if((u>1)||(i))
+ { if((u<=1)&&(flg>=3))
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 1, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ if(!i)LOG_ALLOC("DMSDOS: stac_cwalk_init: nonlinear suballocation, cluster %d\n",
+ clusternr);
+ /* now we know that our cluster is subalocated, we must find
+ number of bytes in last sector of cluster */
+ bh=raw_bread(sb,last_sect);
+ if(bh==NULL)return -1;
+ pp=&(bh->b_data[SECTOR_SIZE-2]);
+ if(CHS(pp)!=0x1234)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 2, cluster %d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ return -1;
+ };
+ pp = &(bh->b_data[SECTOR_SIZE-6]); /* begin of short suball. clust */
+ u = CHS(pp) & (SECTOR_SIZE - 1);
+ pp = &(bh->b_data[SECTOR_SIZE-8]); /* begin of long suball. clust */
+ v = CHS(pp) & (SECTOR_SIZE - 1);
+ raw_brelse(sb,bh);
+ /* u contains number of bytes of our cluster in last_sect */
+ if(v<u)
+ {
+ pp = &(bh->b_data[SECTOR_SIZE-6]);u = CHS(pp);
+ pp = &(bh->b_data[SECTOR_SIZE-8]);v = CHS(pp);
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 3, cluster %d, zerro offset 0x%X 0x%X\n",
+ clusternr,u,v);
+ return -1;
+ };
+ cw->bytes_in_last=u;
+ if(cw->sect_cnt>1)
+ cw->bytes_in_clust-=SECTOR_SIZE-u;
+ else if((i=cw->bytes+cw->offset-cw->bytes_in_last)>0)
+ { if((cw->bytes-=i)<=0)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_init: suballocation error 4, cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ cw->bytes_in_clust-=i;
+ };
+ cw->bytes_in_last|=0x4000;
+ };
+ };
+
+ if ((i=cw->bytes_in_clust-dblsb->s_sectperclust*SECTOR_SIZE)>0)
+ { cw->bytes_in_clust-=i;
+ if(!cw->bytes_in_last) cw->bytes_in_last=SECTOR_SIZE-i;
+ else if((cw->bytes_in_last-=i)<0x4000)
+ { error1:
+ printk(KERN_ERR "DMSDOS: stac_cwalk_init: bad bytes_in_cluster %d\n",
+ clusternr);
+ return -1;
+ };
+ };
+
+ return cw->bytes_in_clust;
+};
+
+/* returns in order all sectors of cluster */
+/* in Stac_cwalk updates fields sect, offset and bytes */
+int stac_cwalk_sector(Stac_cwalk *cw)
+{ if(!cw->sect)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ cw->flen--;
+ cw->sect=cw->start_sect;
+ }
+ else
+ { if(!cw->flen)
+ { if(!cw->fcnt) return 0;
+ cw->fcnt--;
+ if(cw->finfo==NULL)
+ { printk(KERN_ERR "DMSDOS: stac_cwalk_sector: finfo==NULL, cluster %d\n",
+ cw->clusternr);
+ return 0;
+ };
+ cw->sect=cw->finfo[0]+(cw->finfo[1]<<8)+((cw->finfo[3]&0x3f)<<16);
+ cw->flen=(cw->finfo[2]&0xf)+((cw->finfo[3]>>2)&0x30);
+ cw->finfo+=4;
+ }
+ else
+ { cw->sect++;
+ cw->flen--;
+ };
+ cw->offset=0;
+ if(!cw->flen&&!cw->fcnt&&cw->bytes_in_last)
+ cw->bytes=cw->bytes_in_last&(SECTOR_SIZE-1);
+ else cw->bytes=SECTOR_SIZE;
+ };
+ return cw->sect;
+};
+
+void stac_cwalk_done(Stac_cwalk *cw)
+{
+ if(cw->fbh!=NULL) raw_brelse(cw->sb,cw->fbh);
+};
+
+
+void stac_special_free(struct super_block*sb, int clusternr)
+{
+ int val;
+ int sect;
+ Mdfat_entry newmde,dummy_mde;
+ Stac_cwalk cw;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ val=stac_cwalk_init(&cw,sb,clusternr,0);
+ if(val<=0)
+ { if(val<0)
+ printk(KERN_ERR "DMSDOS: stac_special_free: alloc error in cluster %d\n", clusternr);
+ else
+ LOG_CLUST("DMSDOS: stac_special_free: already free cluster %d\n", clusternr);
+ return;
+ };
+ newmde.sector_minus_1=-1;
+ newmde.size_lo_minus_1=0;
+ newmde.size_hi_minus_1=0;
+ newmde.flags=0;
+
+ dbl_mdfat_value(sb,clusternr,&newmde,&dummy_mde);
+ if((cw.flags&0xA0)==0xA0)
+ { /* mark part of suballocated sector as free */
+ struct buffer_head *bh;
+ bh=raw_bread(sb,cw.start_sect);
+ if(bh!=NULL)
+ { if(cw.start_len==1)
+ bh->b_data[SECTOR_SIZE-6+1] |= 0x40;
+ else
+ bh->b_data[SECTOR_SIZE-8+1] |= 0x40;
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ }
+ }
+ /* free sectors from BITFAT */
+ while((sect=stac_cwalk_sector(&cw))>0)
+ {
+ val=dbl_bitfat_value(sb,sect,NULL);
+ if(val>0)
+ { --val;
+ dbl_bitfat_value(sb,sect,&val);
+ dblsb->s_full=0;
+ /* adapt s_free_sectors, -1 unknown */
+ /*if(val==0&&dblsb->s_free_sectors>=0) dblsb->s_free_sectors++;*/
+ /* Hi Pavel,
+ I have commented this out since free sector count is now
+ maintained in dbl_bitfat_value.
+ */
+ } else LOG_CLUST("DMSDOS: stac_special_free: sector not alocated\n");
+ }
+
+ stac_cwalk_done(&cw);
+
+}
+
+/* replaces an existing cluster for stacker;
+ this unusual function must be called before rewriting any file cluster;
+ *** size must be known (encoded in mde) ***
+ it does nothing if called too often;
+ returns first sector nr
+*/
+
+int stac_replace_existing_cluster(struct super_block*sb, int cluster,
+ int near_sector,
+ Mdfat_entry*mde)
+{ Mdfat_entry old_mde,new_mde,dummy;
+ int i;
+ int newval;
+ int sector;
+ int old_sector;
+ int old_size;
+ int new_size;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ lock_mdfat_alloc(dblsb);
+
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster cluster=%d near_sector=%d\n",
+ cluster,near_sector);
+ dbl_mdfat_value(sb,cluster,NULL,&old_mde);
+ old_size=old_mde.size_lo_minus_1+1;
+ old_sector=old_mde.sector_minus_1+1;
+ new_size=mde->size_lo_minus_1+1;
+ if(old_mde.flags&2)
+ {
+ /* stacker routines always replace mdfat entry */
+ newval=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing old sectors...\n");
+ stac_special_free(sb,cluster);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: freeing finished\n");
+ }
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: call find_free_bitfat...\n");
+ sector=find_free_bitfat(sb,near_sector,new_size);
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: find_free_bitfat returned %d\n",
+ sector);
+ if(sector<=0)
+ { if(old_mde.flags&2)
+ { /* Stacker routines don't have an undo list for now.
+ We cannot restore the state before. Sorry data are lost now. */
+ new_mde.sector_minus_1=0;
+ new_mde.size_lo_minus_1=0;
+ new_mde.size_hi_minus_1=0;
+ new_mde.flags=mde->flags=0;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: deleting mdfat entry...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ }
+ unlock_mdfat_alloc(dblsb);
+ return -ENOSPC; /* disk full */
+ }
+ /* check whether really free (bug supposed in find_free_bitfat) */
+ for(i=0;i<new_size;++i)
+ { if(dbl_bitfat_value(sb,sector+i,NULL))
+ { printk(KERN_EMERG "DMSDOS: find_free_bitfat returned sector %d size %d but they are not all free!\n",
+ sector,new_size);
+ unlock_mdfat_alloc(dblsb);
+ panic("DMSDOS: stac_replace_existing_cluster: This is a bug - reboot and check filesystem\n");
+ return -EIO;
+ }
+ }
+ newval=1;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: allocating in bitfat...\n");
+ for(i=0;i<new_size;++i)dbl_bitfat_value(sb,sector+i,&newval);
+
+ new_mde.sector_minus_1=sector-1;
+ new_mde.size_lo_minus_1=mde->size_lo_minus_1;
+ new_mde.size_hi_minus_1=mde->size_hi_minus_1;
+ new_mde.flags=mde->flags|2;
+ LOG_ALLOC("DMSDOS: stac_replace_existing_cluster: writing mdfat...\n");
+ dbl_mdfat_value(sb,cluster,&new_mde,&dummy);
+ unlock_mdfat_alloc(dblsb);
+ return sector; /* okay */
+}
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/dstacker_compr.c b/src/dstacker_compr.c
new file mode 100644
index 0000000..ae7d8b6
--- /dev/null
+++ b/src/dstacker_compr.c
@@ -0,0 +1,1234 @@
+/*
+dstacker_compr.c
+
+DMSDOS CVF-FAT module: stacker compression routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+
+#ifdef DMSDOS_CONFIG_STAC
+
+#ifdef __DMSDOS_DAEMON__
+#include<malloc.h>
+#include<string.h>
+#include<asm/unaligned.h>
+#include<asm/types.h>
+#include <asm/byteorder.h>
+#define MALLOC malloc
+#define FREE free
+int printk(const char * fmt, ...) __attribute__ ((format (printf, 1, 2)));
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* compare blocks, overlaping repeat test */
+/* pointers and counter are modified to point after block */
+/* D and S points to first diff adr */
+#define M_FIRSTDIFF(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "cmpsb\n\t" \
+ "je 1f\n\t" \
+ "dec %0\n\t" \
+ "dec %1\n\t" \
+ "1:\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ )
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ __asm__("xchgb %b0,%h0" /* swap bytes */
+ : "=q" (x)
+ : "0" (x));
+ return x;
+ }
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_FIRSTDIFF(D,S,C) for(;(*(__u8*)(D)==*(__u8*)(S))&&(C);\
+ (__u8*)(D)++,(__u8*)(S)++,(C)--)
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+ }
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define cpu_to_le16(v) (v)
+ #define be16_to_cpu(v) (swap_bytes_in_word(v))
+ #define cpu_to_be16(v) (swap_bytes_in_word(v))
+#endif
+
+/* store and load __u16 in any byteorder on any */
+/* address (odd or even). */
+/* this is problematic on architectures, */
+/* which cannot do __u16 access to odd address. */
+/* used for temporary storage of LZ intercode. */
+#define C_ST_u16(p,v) {put_unaligned(v,((__u16*)p)++);}
+#define C_LD_u16(p,v) {v=get_unaligned(((__u16*)p)++);}
+
+/* for reading and writting from/to bitstream */
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* not read bits count in buffer */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs1.c *******************************/
+
+typedef unsigned int count_t;
+typedef __u8* hash_t;
+typedef
+ struct {
+ count_t cn;
+ unsigned ch;
+ } ch_tab_t;
+
+typedef
+ struct {
+ __u16 cod[0x180]; /* characters codes */
+ __u8 ln[0x180]; /* characters lens */
+ }huf_wr_t;
+
+#ifdef MAX_COMP
+ #define MAX_HASH_HIST 0x1000
+#else
+ #define MAX_HASH_HIST 0x800
+#endif
+
+#define TK_END 0x4F
+#define TK_CHRS 0xF0
+#define TKWR_CHRS(p,v) {if(v<15) *(p++)=TK_CHRS+(__u8)v;\
+ else {*(p++)=TK_CHRS+15;C_ST_u16(p,v);};}
+
+/* compression level table */
+ const unsigned comp_rat_tab[]=
+ { /*0*/ 0x7F9,0x7F9,0x621,0x625,
+ /*4*/ 0x665,0x669,0x6E9,0x6ED,
+ /*8*/ 0x7D1,0x7D9,0x6E9,0x47D9,
+ /*12*/0x46E9};
+
+/* token decoding tables */
+ const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+
+#if 0
+
+INLINE unsigned sd4_hash(__u8 *p)
+{
+ unsigned a;
+ /*a=(p[0]>>1)^(p[1]<<7)^(p[1]>>4)^(p[2]<<2);*/
+ a =p[1]; a<<=5;
+ a^=p[2]; a<<=2+6;
+ a^=p[1]; a>>=5;
+ a^=p[0]; a>>=1;
+ return a&0x3FF;
+};
+
+#else
+
+INLINE unsigned sd4_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<2)^((__u16)p[1]<<4)^(__u16)p[2])&0x3FF;
+};
+
+#endif
+
+INLINE hash_t sd4_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sd4_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hash_mask))=hash_cur;
+ return(hash_cur);
+};
+
+/* finds repetitions in *pin and writes intermediate code to *pout */
+unsigned sd4_complz(void* pin,int lin,void* pout,int lout,int flg,count_t* ch_cn)
+{
+ void *work_mem;
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab; /* pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ __u8 *pi, *po, *pc, *pd, *pend, *poend;
+ hash_t hash_cur;
+ unsigned cn;
+ unsigned max_match, match, token;
+ int try_cn;
+ hash_t max_hash=NULL;
+ #ifdef MAX_COMP
+ int delay_count=0; /* test next # characters for better match */
+ int delay_cn;
+ int delay_best;
+ #endif
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ #ifdef MAX_COMP
+ if(flg&0x4000)
+ {
+ hash_mask=MAX_HASH_HIST-1; /* maximal compression */
+ delay_count=2;
+ try_count*=4;
+ hash_skiped*=4;
+ };
+ #endif
+
+ /* stack is small in kernel space, using MALLOC */
+ work_mem=MALLOC((0x400+hash_mask+1)*sizeof(hash_t));
+ if(work_mem==NULL) return 0;
+ hash_tab =(hash_t*)work_mem;
+ hash_hist=((hash_t*)work_mem)+0x400;
+
+ pi=(__u8*)pin;
+ po=(__u8*)pout;
+ if(!lin) goto return_error;
+ pend=pi+(lin-1);
+
+ /*
+ printk("There we are,work_mem=%X hash_hist=%X pin=%X lin=%X pend=%X pout=%X\n",
+ work_mem,hash_hist,pin,lin,pend,pout);
+ */
+
+ if(lout<0x20) goto return_error; /* some minimal space for lz interform buffer */
+ poend=po+(lout-0x20);
+ for(cn=0;cn<0x400;cn++) hash_tab[cn]=pend; /* none ocurence of hash */
+ for(cn=0;cn<=hash_mask;cn++) hash_hist[cn]=pend; /* should not be be needed */
+ pend--; /* last two bytes cannot be hashed */
+ cn=0;
+ while(pi<pend)
+ {
+ hash_cur=sd4_newhash(pi,hash_tab,hash_hist,hash_mask);
+ /* goto single_char; */ /* to by pass LZ for tests */
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=2; /* minimal coded match-1 */
+ do{
+ if(pi-hash_cur>=0xAA0) break; /* longer offsets are not allowed */
+ if((hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[max_match]==pi[max_match]))
+ {
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match)&&((match>=6)||(pi-hash_cur<0x800)))
+ {
+ max_hash=hash_cur; /* find maximal hash */
+ max_match=match;
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ if(max_match<3) goto single_char;
+
+ #ifdef MAX_COMP
+ /* tests if better matchs on next characters */
+ delay_cn=0;
+ if(delay_count)
+ {
+ delay_best=0;
+ while((delay_cn<delay_count)&&(pi+max_match<pend)&&
+ (max_match<0x100))
+ {
+ pi++;delay_cn++;
+ hash_cur=sd4_newhash(pi,hash_tab,hash_hist,hash_mask);
+ try_cn=try_count;
+ if (hash_cur<pi) do
+ {
+ if(pi-hash_cur>=0xAA0) break; /* longer offsets are not allowed */
+ if((hash_cur[0]==pi[0])&&(hash_cur[1]==pi[1])&&
+ /* pi[2]=hash_cur[2] from hash function */
+ (hash_cur[max_match-1]==pi[max_match-1])&&
+ (hash_cur[max_match]==pi[max_match])&&
+ (hash_cur!=max_hash+delay_cn-delay_best))
+ { /* do not test actual max match */
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if((match>max_match+delay_cn)&&((match>=6)||(pi-hash_cur<0x800)))
+ {
+ max_hash=hash_cur;max_match=match; /* find maximal hash */
+ delay_best=delay_cn;
+ if(pd>pend+1)break; /* match to end of block */
+ };
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ };
+ pi-=delay_cn;
+ delay_cn-=delay_best;
+ while(delay_best)
+ {
+ delay_best--;
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) goto return_error;};
+ }
+ };
+ #endif
+
+ if(cn) TKWR_CHRS(po,cn);
+ cn=pi-max_hash-1; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+ if(max_match<6)
+ {
+ token=max_match-3;
+ if(cn<3){
+ token+=cn+(cn<<1);
+ *(po++)=token;
+ }else{
+ token=max_match-3+9;
+ cn-=3;
+ match=4;
+ while(cn>=match){token+=6;cn-=match;match<<=1;};
+ match>>=1;if(cn>=match){token+=3;cn-=match;};
+ *(po++)=token;
+ C_ST_u16(po,cn); /* history offset */
+ };
+ }else{
+ if(max_match<21)
+ {token=max_match-6+0x3F;*(po++)=token;C_ST_u16(po,cn);}
+ else{
+ token=0x4E;
+ *(po++)=token;
+ C_ST_u16(po,cn); /* history offset */
+ C_ST_u16(po,max_match); /* repeat count */
+ };
+ };
+ ch_cn[token+0x100]++;
+ if(hash_skiped&&(pi<pend))
+ {
+ #ifdef MAX_COMP
+ max_match-=delay_cn;
+ #endif
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sd4_newhash(pi++,hash_tab,hash_hist,hash_mask);
+ };
+ if(poend<po) goto return_error;
+ cn=0;
+ continue;
+ single_char:
+ ch_cn[*(pi++)]++;
+ if(++cn>=0x8000u)
+ {TKWR_CHRS(po,0x8000u);cn-=0x8000u;if(poend<po) goto return_error;};
+ };
+
+ pend+=2;
+ while(pi!=pend) {ch_cn[*(pi++)]++;cn++;};
+ if(cn)
+ {
+ if(cn>=0x8000u) {TKWR_CHRS(po,0x8000u);cn-=0x8000u;};
+ TKWR_CHRS(po,cn);
+ };
+
+ ch_cn[TK_END+0x100]++;
+ *po++=TK_END;
+ FREE(work_mem);
+ return(po-(__u8*)pout);
+
+ return_error:
+ FREE(work_mem);
+ return(0);
+};
+
+INLINE void sd4b_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE void sd4b_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->pb-=n;
+ pbits->buf|=(__u32)cod<<pbits->pb;
+ if(pbits->pb<16)
+ {
+ *(pbits->pd++)=cpu_to_le16((__u16)(pbits->buf>>16));
+ pbits->buf<<=16;
+ pbits->pb+=16;
+ };
+};
+
+INLINE int sd4b_wrhufi(huf_wr_t *phuf,count_t* ch_cn, unsigned* ch_blcn,int cod_num)
+{
+ unsigned i,u,t,blen;
+ u=0;
+ for(i=0;i<16;i++) {u<<=1;t=u;u+=ch_blcn[i];ch_blcn[i]=t;};
+ if(u!=0x8000u) return(1);
+ for(i=0;i<cod_num;i++)
+ {
+ if((blen=ch_cn[i])!=0)
+ {
+ phuf->cod[i]=ch_blcn[blen]++;
+ phuf->ln[i]=blen;
+ };
+ };
+ return(0);
+};
+
+INLINE void sd4b_wrh(bits_t *pbits,const huf_wr_t *phuf,const unsigned ch)
+{
+ sd4b_wrn(pbits,phuf->cod[ch],phuf->ln[ch]);
+};
+
+/*** Hacked generation of character codes ***/
+
+void sd4_hsort1(ch_tab_t* ch_tab,int ch_num,int cl, ch_tab_t a)
+{
+ int ch;
+ ch_tab_t b;
+ ch_tab_t c;
+ ch=cl*2;
+ while(ch<ch_num)
+ {
+ b=ch_tab[ch-1];c=ch_tab[ch];
+ if((c.cn<b.cn)||((c.cn==b.cn)&&(c.ch<=b.ch))) {b=c;ch++;};
+ if((b.cn>a.cn)||((b.cn==a.cn)&&(b.ch>=a.ch))) {ch_tab[cl-1]=a;return;}
+ ch_tab[cl-1]=b;cl=ch;ch*=2;
+ };
+ if(ch==ch_num)
+ {
+ b=ch_tab[ch-1];
+ if((b.cn<a.cn)||((b.cn==a.cn)&&(b.ch<a.ch)))
+ {ch_tab[cl-1]=b;cl=ch;ch*=2;};
+ };
+ ch_tab[cl-1]=a;
+};
+
+int sd4_huffman(count_t* ch_cn,unsigned* ch_blcn,int cod_num,void *work_mem)
+{
+ ch_tab_t *ch_tab; /* normaly 0x152 entries */
+ int i,ch_num,cl;
+ ch_tab_t a;
+ ch_tab_t b;
+
+ ch_tab=(ch_tab_t*)work_mem;
+ redo_reduced:
+ ch_num=0;
+ for(i=0;i<cod_num;i++) if(ch_cn[i])
+ {ch_tab[ch_num].cn=ch_cn[i];ch_tab[ch_num].ch=i|0x800;ch_num++;};
+ ch_tab[ch_num].ch=0;
+ if(ch_num==1)
+ {
+ ch_tab[ch_num]=ch_tab[ch_num-1];
+ ch_tab[ch_num].ch&=0x801;
+ ch_tab[ch_num].ch^=1;ch_num++;
+ };
+ cl=ch_num/2;
+ while(cl>1)
+ {
+ sd4_hsort1(ch_tab,ch_num,cl,ch_tab[cl-1]);
+ cl--;
+ };
+
+ cl=ch_num; a=ch_tab[0];
+ while(cl>2)
+ {
+ sd4_hsort1(ch_tab,cl,1,a);
+ b=ch_tab[0];
+ a=ch_tab[--cl];
+ ch_tab[cl].ch=b.ch;
+ sd4_hsort1(ch_tab,cl,1,a);
+ a=ch_tab[0];
+ ch_tab[cl].cn=a.ch;
+ if(a.ch<=b.ch) {a.ch=b.ch;};
+ a.ch=(a.ch&0x7800)+cl+0x800;
+ if(a.ch>=0x8000u)
+ {
+ printk("DMSDOS: sd4_huffman: Problems with number of bits\n");
+ for(i=0;i<cod_num;i++) ch_cn[i]=(ch_cn[i]+1)>>1;
+ goto redo_reduced;
+ };
+ a.ch+=0x8000u;
+ a.cn+=b.cn;
+ };
+ ch_tab[1].cn=a.ch;
+
+ {
+ int st[0x20];
+ int k=0,l=1,blen=0;
+
+ memset(ch_blcn,0,sizeof(ch_blcn[0])*16);
+ while(1)
+ {
+ do
+ {
+ k|=0x4000;
+ do
+ {
+ st[blen]=k;
+ blen++;
+ k=l&0x7FF;
+ l=ch_tab[k].ch&0x87FF;
+ }while(l&0x8000);
+ ch_cn[l]=blen;
+ ch_blcn[blen]++;
+ l=ch_tab[k].cn&0x87FF;
+ }while(l&0x8000);
+ do
+ {
+ ch_cn[l]=blen;
+ ch_blcn[blen]++;
+ do
+ {
+ if(!--blen) goto code_done;
+ k=st[blen];
+ }while(k&0x4000);
+ l=ch_tab[k].cn&0x87FF;
+ }while(!(l&0x8000));
+ };
+ code_done:;
+ };
+ return(0);
+};
+
+/*** Main compression routine ***/
+
+int sd4_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ count_t *ch_cn=NULL;
+ char *work_mem=NULL;
+ unsigned lz_length;
+
+ ch_cn=(count_t*)MALLOC(sizeof(count_t)*0x151);
+ memset(ch_cn,0,sizeof(count_t)*0x151);
+
+ if((lz_length=sd4_complz(pin,lin,pout,lout,flg,ch_cn))==0) goto return_error;
+ {
+ unsigned ch_blcn[16]; /* bitlength couts for codes */
+ int i,j;
+ int bl_dat;
+ unsigned *bl_buf; /* prepared data of table 2 with tokens */
+ count_t act_bl;
+ count_t bl_cn[0x16]; /* occurecces of bit lens */
+ unsigned bl_blcn[0x16]; /* bitlength couts for bit lens */
+ int min_bl, max_bl;
+ __u8* lz_pos;
+ __u8* pdata;
+ bits_t bits;
+ unsigned token, u;
+ huf_wr_t *huf;
+
+/* for converting local variables to allocated memory - kernel stack
+ is too small */
+#define SIZE_OF_bl_buf (sizeof(unsigned)*0x151)
+#define SIZE_OF_huf (((MAX(sizeof(huf_wr_t),sizeof(ch_tab_t)*0x152)+63)/64)*64)
+ work_mem=(char*)MALLOC(SIZE_OF_huf+SIZE_OF_bl_buf);
+ huf=(huf_wr_t*)work_mem;
+ bl_buf=(unsigned*)(work_mem+SIZE_OF_huf);
+ memset(bl_buf,0,SIZE_OF_bl_buf);
+
+ /* ch_cn .. ocurrences of codes */
+ sd4_huffman(ch_cn,ch_blcn,0x150,work_mem);
+ /* ch_cn .. bit lengths of codes */
+ /* ch_blcn .. counts of specific values of bit length */
+ memset(bl_cn,0,sizeof(bl_cn));
+ i=0;bl_dat=0;
+ min_bl=8;max_bl=0;
+ while(i<0x150)
+ {
+ /* case 0x10: 2 times zerro */
+ /* case 0x11: 3 times zerro */
+ /* case 0x12: zerro fill */
+ /* case 0x13: 2 times last char */
+ /* case 0x14: 3 times last char */
+ /* case 0x15: repeat last chr */
+
+ act_bl=ch_cn[i++]; j=i;
+ while((i<0x150)&&(act_bl==ch_cn[i])) i++;
+ j=i-j;
+ if(!act_bl)
+ {
+ if(!j--) {bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;}
+ else if(!j--) {bl_cn[0x10]++;bl_buf[bl_dat++]=0x10;}
+ else if(!j--) {bl_cn[0x11]++;bl_buf[bl_dat++]=0x11;}
+ else {bl_cn[0x12]++;bl_buf[bl_dat++]=0x12;bl_buf[bl_dat++]=j;};
+ }else{
+ bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;
+ if(act_bl<min_bl) min_bl=act_bl;
+ if(act_bl>max_bl) max_bl=act_bl;
+ if(j--)
+ {
+ if(!j--) {bl_cn[act_bl]++;bl_buf[bl_dat++]=act_bl;}
+ else if(!j--) {bl_cn[0x13]++;bl_buf[bl_dat++]=0x13;}
+ else if(!j--) {bl_cn[0x14]++;bl_buf[bl_dat++]=0x14;}
+ else {bl_cn[0x15]++;bl_buf[bl_dat++]=0x15;bl_buf[bl_dat++]=j;};
+ };
+ };
+ };
+ sd4_huffman(bl_cn,bl_blcn,0x16,work_mem);
+
+ sd4b_wri(&bits,pout,lout-lz_length);
+ lz_pos=(__u8*)pout+lout-lz_length;
+ memmove(lz_pos,pout,lz_length);
+
+ /* write magic */
+ sd4b_wrn(&bits,0x81,16);
+
+ /* write table 1 */
+ sd4b_wrn(&bits,min_bl-1,3);
+ sd4b_wrn(&bits,max_bl,5);
+ sd4b_wrn(&bits,bl_cn[0],4);
+ for(i=min_bl;i<=max_bl;i++) sd4b_wrn(&bits,bl_cn[i],4);
+ for(i=0x10;i<=0x15;i++) sd4b_wrn(&bits,bl_cn[i],4);
+
+ /* write table 2 */
+ if(sd4b_wrhufi(huf,bl_cn,bl_blcn,0x16))
+ {printk("DMSDOS: sd4_comp: Hufman code leakage in table 1\n");goto return_error;};
+ for(i=0;i<bl_dat;)
+ {
+ sd4b_wrh(&bits,huf,j=bl_buf[i++]);
+ if(j==0x12)
+ {
+ j=bl_buf[i++];
+ if(j>=7)
+ {
+ sd4b_wrn(&bits,7,3); j-=7;
+ while(j>=0x7F) {sd4b_wrn(&bits,0x7F,7);j-=0x7F;};
+ sd4b_wrn(&bits,j,7);
+ }
+ else sd4b_wrn(&bits,j,3);
+ }
+ else if(j==0x15)
+ {
+ j=bl_buf[i++];
+ while(j>=7) {sd4b_wrn(&bits,7,3);j-=7;};
+ sd4b_wrn(&bits,j,3);
+ };
+ };
+
+ /* write compressed data */
+ {
+ pdata=(__u8*)pin;
+ if(sd4b_wrhufi(huf,ch_cn,ch_blcn,0x150))
+ {printk("DMSDOS: sd4_comp: Hufman code leakage in table 2\n");goto return_error;};
+ while(1)
+ {
+ /* check of LZ and huff contact in output buffer */
+ if((__u8*)bits.pd+0x20>=lz_pos) goto return_error;
+ token=*(lz_pos++);
+ if(token>TK_CHRS)
+ { /* characters */
+ u=token-TK_CHRS;
+ if(u==15)
+ {
+ C_LD_u16(lz_pos,u);
+ while(u--)
+ if((__u8*)bits.pd+1>=lz_pos) goto return_error;
+ else sd4b_wrh(&bits,huf,*(pdata++));
+ }
+ else while(u--) sd4b_wrh(&bits,huf,*(pdata++));
+ }
+ else
+ { /* repetitions coded as tokens */
+ sd4b_wrh(&bits,huf,token+0x100);
+ if(token<0x3F)
+ { /* short repeat tokens */
+ token++;
+ u=sd4b_reps_div3[token];
+ pdata+=token+sd4b_reps_n[u];
+ i=sd4b_reps_b[u];
+ if(i)
+ {
+ C_LD_u16(lz_pos,u);
+ sd4b_wrn(&bits,u,i);
+ };
+ }
+ else if(token<TK_END)
+ { /* repeat n times last m characters */
+ C_LD_u16(lz_pos,u); u++; /* history offset */
+ if(u<0x21) i=0;
+ else if(u<0xA1) i=1;
+ else if(u<0x2A1) i=2;
+ else i=3;
+ sd4b_wrn(&bits,i,2);
+ sd4b_wrn(&bits,u-sd4b_prog_add[i],sd4b_prog_len[i]);
+ if(token==0x4E)
+ { /* repeat n>=21 */
+ C_LD_u16(lz_pos,u); /* repeat count */
+ pdata+=u;
+ u-=0x15;
+ if(u<0xF) sd4b_wrn(&bits,u,4);
+ else {u-=0xF;sd4b_wrn(&bits,0xF,4);if(u<0xFF) sd4b_wrn(&bits,u,8);
+ else {u-=0xFF;sd4b_wrn(&bits,0xFF,8);if(u<0xFFF) sd4b_wrn(&bits,u,12);
+ else {u-=0xFFF;sd4b_wrn(&bits,0xFFF,12);sd4b_wrn(&bits,u,16);};};};
+ } else pdata+=token+6-0x3F;
+ }
+ else break;
+ };
+ };
+ if((token!=TK_END)||(pdata-(__u8*)pin!=lin))
+ {
+ printk("DMSDOS: sd4_comp: Compression ends with mismash\n");
+ goto return_error;
+ };
+ };
+ sd4b_wrn(&bits,0,16);
+ FREE(ch_cn);
+ FREE(work_mem);
+ return((__u8*)bits.pd-(__u8*)pout);
+ };
+ return_error:
+ if(ch_cn!=NULL) FREE(ch_cn);
+ if(work_mem!=NULL) FREE(work_mem);
+ return(0);
+};
+
+/*************** end code from sd4_bs1.c *********************************/
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs0.c *******************************/
+
+INLINE void sd3b_wri(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->buf=0;
+ pbits->pb=32;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE void sd3b_wrn(bits_t *pbits,int cod, int n)
+{
+ pbits->pb-=n;
+ pbits->buf|=(__u32)cod<<pbits->pb;
+ if(pbits->pb<16)
+ {
+ *(pbits->pd++)=cpu_to_be16((__u16)(pbits->buf>>16));
+ pbits->buf<<=16;
+ pbits->pb+=16;
+ };
+};
+
+INLINE __u8 sd3_xorsum(__u8 *data,int len)
+{
+ __u8 sum=0xFF;
+ while(len--) sum^=*(data++);
+ return(sum);
+};
+
+INLINE unsigned sd3_hash(__u8 *p)
+{
+ return (((__u16)p[0]<<4)^((__u16)p[1]<<0))&0x3FF;
+};
+
+INLINE hash_t sd3_newhash(__u8 *p,hash_t* hash_tab,hash_t* hash_hist,unsigned hash_mask)
+{
+ hash_t* hash_ptr;
+ hash_t hash_cur;
+ hash_ptr=hash_tab+sd3_hash(p);
+ hash_cur=*hash_ptr;
+ *hash_ptr=p;
+ *(hash_hist+((unsigned)p&hash_mask))=hash_cur;
+ return(hash_cur);
+};
+
+int sd3_comp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ bits_t bits; /* output bitstream */
+ int try_count; /* number of compares to find best match */
+ int hash_skiped; /* last bytes of repetition are hashed too */
+ hash_t *hash_tab;
+ /* [0x400] pointers to last occurrence of same hash, index hash */
+ hash_t *hash_hist;
+ /* [0x800] previous occurences of hash, index actual pointer&hash_mask */
+ unsigned hash_mask=0x7FF;/* mask for index into hash_hist */
+ __u8 *pi, *pc, *pd, *pend;
+ hash_t hash_cur;
+ unsigned offs;
+ unsigned max_match, match, rep;
+ int try_cn;
+ hash_t max_hash=NULL;
+
+ try_count=2<<((flg>>2)&0x7); /* number of tested entries with same hash */
+ hash_skiped=((flg>>5)+1)&0xF;/* when rep found last # bytes are procesed */
+
+ pi=(__u8*)pin;
+ if(!lin) return(0);
+ pend=pi+(lin-1);
+ if(lout<0x20) return(0);
+ sd3b_wri(&bits,pout,lout-0x10); /* initialize output bitstream */
+ hash_tab=MALLOC(0x400*sizeof(hash_t));
+ if(hash_tab==NULL) return 0;
+ hash_hist=MALLOC(0x800*sizeof(hash_t));
+ if(hash_hist==NULL) {FREE(hash_tab);return 0;}
+
+ for(offs=0;offs<0x400;offs++) hash_tab[offs]=pend; /* none ocurence of hash */
+ for(offs=0;offs<=hash_mask;offs++) hash_hist[offs]=pend; /* should not be needed */
+ pend--; /* last two bytes cannot be hashed */
+ while(pi<pend)
+ {
+ if(bits.pd>bits.pe)
+ {/* aborting */
+ FREE(hash_hist);
+ FREE(hash_tab);
+ return 0;
+ };
+ hash_cur=sd3_newhash(pi,hash_tab,hash_hist,hash_mask);
+ if(hash_cur>=pi) goto single_char;
+ try_cn=try_count;
+ max_match=0;
+ do{
+ if(pi-hash_cur>=0x800) break; /* longer offsets are not alloved */
+ if(hash_cur[0]==pi[0]) /* pi[1]=hash_cur[1] from hash function */
+ {
+ match=pend-pi; /* length of rest of data */
+ pd=pi+2;
+ pc=hash_cur+2;
+ M_FIRSTDIFF(pd,pc,match);/* compare */
+ match=pd-pi; /* found match length */
+ if(match>max_match)
+ {max_hash=hash_cur;max_match=match;}; /* find maximal hash */
+ };
+ pc=hash_cur;
+ }while(--try_cn&&((hash_cur=hash_hist[(unsigned)pc&hash_mask])<pc));
+ if(max_match<2) goto single_char;
+
+ offs=pi-max_hash; /* history offset */
+ pi+=max_match; /* skip repeated bytes */
+
+ if(offs<0x80) sd3b_wrn(&bits,0x180+offs,9);
+ else
+ {sd3b_wrn(&bits,0x100+offs/16,9);
+ sd3b_wrn(&bits,offs%16,4);
+ };
+
+ rep=max_match-2;
+ if(rep<3)sd3b_wrn(&bits,rep,2);
+ else
+ {rep-=3;sd3b_wrn(&bits,3,2);
+ if(rep<3)sd3b_wrn(&bits,rep,2);
+ else
+ {rep-=3;sd3b_wrn(&bits,3,2);
+ for(;rep>=15;rep-=15)sd3b_wrn(&bits,15,4);
+ sd3b_wrn(&bits,rep,4);
+ };
+ };
+
+ if(hash_skiped&&(pi<pend))
+ {
+ if(--max_match>hash_skiped) max_match=hash_skiped;
+ pi-=max_match;
+ while(max_match--) sd3_newhash(pi++,hash_tab,hash_hist,hash_mask);
+ };
+ continue;
+ single_char:
+ sd3b_wrn(&bits,*pi++,9);
+ };
+
+ pend+=2;
+ while(pi!=pend) sd3b_wrn(&bits,*pi++,9);
+
+ sd3b_wrn(&bits,0x180,9);
+ bits.pb&=~7;
+ sd3b_wrn(&bits,sd3_xorsum(pin,lin),8);
+
+ sd3b_wrn(&bits,0,15);
+
+ FREE(hash_hist);
+ FREE(hash_tab);
+ return((__u8*)bits.pd-(__u8*)pout);
+
+};
+
+/*************** end code from sd4_bs0.c *********************************/
+
+
+/* This function will be called by the dmsdos driver *and* by the daemon
+ in order to compress stacker data (the daemon links the object file, too).
+ Decision can be made with ifdef __KERNEL__ .
+
+ Hi Frank,
+ I know, that it is different from doublespace, but I
+ decide change this, because stacker 4 compression likes know
+ exactly free space in output buffer. It uses this space for
+ intermediate data representation ( which is always shorter
+ then original data ), then moves it to end and writes new
+ compressed data from begining (if write catch read compression
+ returns false). Theoreticaly length -> length must sucees or
+ all compression is useless, but who knows. And there is such
+ lovely 32 kB buffer in daemon.
+
+*/
+
+#if 0
+/* this confuses some compilers in the memset command below */
+int stac_compress(void* pin,int lin, void* pout, int lout,
+ int method, int cfaktor)
+#else
+int stac_compress(unsigned char* pin,int lin, unsigned char* pout, int lout,
+ int method, int cfaktor)
+#endif
+{ int ret=-1;
+ int i;
+ if(((i=lin%512)!=0)||!lin) /* useless but stacker like it */
+ { memset(pin+lin,0,512-i);
+ lin+=512-i;
+ };
+ if((cfaktor<=0)||(cfaktor>12)) cfaktor=11;
+ if(method==SD_4) ret=sd4_comp(pin,lin,pout,lout,comp_rat_tab[cfaktor]);
+ else if(method==SD_3) ret=sd3_comp(pin,lin,pout,lout,comp_rat_tab[cfaktor]);
+ if(ret>lin-512) ret=0; /* for now */
+ return ret;
+}
+
+/* Specification:
+ This function writes a stacker cluster.
+ It must take care of clustermap and allocationmap manipulation
+ including freeing the old sectors.
+ It must not touch the FAT.
+ It must take care of compression, if implemented.
+ It must write uncompressed if ucflag==UC_UNCOMPR.
+ Depending on the daemon implementation, it should be able to process
+ raw writes (ucflag<0).
+ near_sector may be ignored in case this is too difficult to implement.
+ ucflag==UC_TEST means check for free space or reserve space for the
+ cluster on the disk, but don't actually write the data.
+ It is for write-back cluster caching. When a cluster is marked as dirty
+ by calling the ch_dirty function, the cluster caching code calls the
+ write_cluster function with the UC_TEST flag. The cluster
+ write access is delayed only if the UC_TEST call succeeds (returns a
+ value >=0). Otherwise the cluster caching code immediately falls back to
+ write-through mode and calls the write function again.
+ If the UC_TEST call succeeds, be prepared to be called again later
+ at any time without the UC_TEST flag when the cluster caching code has
+ decided to actually write the data back to disk.
+*/
+
+/* write a stacker cluster, compress before if possible;
+ length is the number of used bytes, may be < SECTOR_SIZE*sectperclust only
+ in the last cluster of a file;
+ cluster must be allocated by allocate_cluster before if it is a new one;
+ unable to write dir clusters;
+ to avoid MDFAT level fragmentation, near_sector should be the sector no
+ of the preceeding cluster;
+ if ucflag==UC_UNCOMPR uncompressed write is forced.
+ if ucflag==UC_TEST check for free space or reserve space on the
+ disk but don't actually write the data.
+
+ If ucflag<0 raw write is forced with compressed size -ucflag (in bytes),
+ in that case parameter length is *uncompressed* size. This is the new
+ dmsdosd/ioctl interface.
+
+ If clusterd==NULL the cluster is to be removed instead of written. This
+ is called by the rest of the dmsdos code when a file is deleted. So
+ the stacker code is expected to free up mdfat/bitfat allocation for the
+ cluster, but it must not touch the fat.
+
+ if ucflag==UC_DIRECT do like ucflag==UC_NORMAL but don't use the daemon
+ for compression.
+ This should guarantee that the data are written when the function exits.
+ It is unimportant whether compression fails or not - it's just important
+ that the data use *least* disk space. This must not override the
+ method=UNCOMPRESSED or compression level selected by user, though.
+ It is intended for error recovery when the filesystem gets full - if
+ we use the daemon, the uncompressed data might not fit to the disk while
+ the compressed data may still do.
+*/
+
+#if defined(__KERNEL__)||defined(__DMSDOS_LIB__)
+
+int stac_write_cluster(struct super_block*sb,
+ unsigned char* clusterd, int length, int clusternr,
+ int near_sector, int ucflag)
+{
+ int method;
+ unsigned char* clusterk;
+ int size;
+ int sect,count;
+ int i,val;
+ int res;
+ struct buffer_head*bh;
+ int max_clen;
+ Stac_cwalk cw;
+ int cfaktor;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* check if we are deleting a cluster */
+ if(clusterd==NULL||length==0)
+ {
+ lock_mdfat_alloc(dblsb);
+ stac_special_free(sb,clusternr);
+ unlock_mdfat_alloc(dblsb);
+ return 0;
+ }
+
+ /* for now */
+ /*if(ucflag==UC_TEST) return -EIO;*/
+ if(ucflag==UC_TEST)
+ { if( dblsb->s_full==0 &&
+ /* well, this is estimated */
+ dblsb->s_sectperclust*CCACHESIZE+100<dblsb->s_free_sectors
+ ) return 0;
+ else return -ENOSPC;
+ }
+
+ if(dblsb->s_comp==GUESS)
+ {
+ if(dblsb->s_cvf_version==STAC3)
+ dblsb->s_comp=SD_3;
+ else
+ dblsb->s_comp=SD_4;
+ };
+
+ method=dblsb->s_comp; /* default compression method */
+ max_clen=dblsb->s_sectperclust*SECTOR_SIZE; /* maximal data size */
+
+ if( ( (dblsb->s_cvf_version==STAC3)&&(length<=SECTOR_SIZE) ) ||
+ (ucflag==UC_UNCOMPR)
+ ) /* uncompressed forced or no need to compress */
+ { method=UNCOMPRESSED;
+ clusterk=clusterd;
+ }
+ else if(ucflag<0)
+ { /* raw compressed data from daemon */
+ length=-ucflag;
+ method=UNCOMPRESSED^1; /* not uncompressed */ /* is this correct ??? */
+ /* Hi Pavel,
+ Please check the code whether it works
+ correctly for daemon writes. I think this may
+ cause a FREE(data not to free) at the very
+ end. I added a ucflag>=0 test there to avoid
+ the problem.
+ */
+ clusterk=clusterd;
+ }
+ else if(method!=UNCOMPRESSED)
+ { /* ucflag==3 test added - Frank */
+ if((ucflag==UC_DIRECT)?0:try_daemon(sb,clusternr,length,method)) clusterk=NULL;
+ else if((clusterk=(unsigned char*)MALLOC(max_clen))==NULL)
+ printk(KERN_WARNING "DMSDOS: stac_write_cluster: no memory for compression, writing uncompressed!\n");
+ if(clusterk==NULL) method=UNCOMPRESSED;
+ else
+ { /* We test possible compression */
+ /* stacker needs length before compression to be
+ multiple of SECTOR_SIZE */
+ if(((i=length%SECTOR_SIZE)!=0)||!length)
+ { memset(clusterd+length,0,SECTOR_SIZE-i);
+ i=length+SECTOR_SIZE-i;
+ } else i=length;
+
+ cfaktor=dblsb->s_cfaktor;
+ if((cfaktor<=0)||(cfaktor>12)) cfaktor=11;
+
+ if(method==SD_4)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing sd4...\n");
+ i=sd4_comp(clusterd,i,clusterk,max_clen,comp_rat_tab[cfaktor]);
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else if(method==SD_3)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing sd3...\n");
+ i=sd3_comp(clusterd,i,clusterk,max_clen,comp_rat_tab[cfaktor]);
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else if(method==DS_0_0)
+ { LOG_CLUST("DMSDOS: stac_write_cluster: compressing ds00...\n");
+ i=dbl_compress(clusterk,clusterd,(i+511)/512,method,cfaktor)*512;
+ LOG_CLUST("DMSDOS: stac_write_cluster: compressing finished\n");
+ }
+ else i=0;
+
+ LOG_CLUST("DMSDOS: Cluster %i compressed from %i to %i\n",clusternr,length,i);
+ if((i<=0)||(i>=length)||(i>max_clen-SECTOR_SIZE))
+ { method=UNCOMPRESSED;
+ FREE(clusterk);
+ }
+ else length=i;
+ }
+ }
+ if(method==UNCOMPRESSED) clusterk=clusterd;
+
+ /* Now we have data and must decide where to write them */
+ val=stac_cwalk_init(&cw,sb,clusternr,3);
+ if (val<0)
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: alloc error in cluster %d\n",
+ clusternr);
+ res=-EIO;
+ goto error_return;
+ };
+
+ /* decide if it is necessary to reallocate cluster */
+ if((val==0)||(cw.bytes_in_clust<length)||
+ (cw.bytes_in_clust>=length+SECTOR_SIZE)||(cw.flags&0x40)||
+ ((cw.compressed==0)!=(method==UNCOMPRESSED)))
+ { /* It is necessary realocate space */
+ /* this piece of code is dirty hack and must be rewriten */
+ Mdfat_entry mde;
+
+ stac_cwalk_done(&cw);
+
+ size=(length+511)/512;
+ if(!size) size=1;
+ mde.size_lo_minus_1=size-1;
+ mde.size_hi_minus_1=size-1;
+ if(method==UNCOMPRESSED)
+ if(size==dblsb->s_sectperclust)
+ mde.flags=2;
+ else
+ mde.flags=0x23;
+ else
+ mde.flags=2;
+
+ LOG_CLUST("DMSDOS: stac_write_cluster: Replace size %2i flg 0x%02X cluster %i\n",
+ size,mde.flags,clusternr);
+ sect=stac_replace_existing_cluster(sb,clusternr,near_sector,&mde);
+ LOG_CLUST("DMSDOS: stac_write_cluster: stac_replace_existing_cluster returned %d\n",
+ sect);
+
+ if(sect<0) {res=-ENOSPC;goto error_return;};
+
+ val=stac_cwalk_init(&cw,sb,clusternr,3);
+ if ((val<0)||(length>cw.bytes_in_clust))
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: alloc error in cluster %d\n",
+ clusternr);
+ res=-EIO;
+ goto error_return;
+ };
+ }
+
+ { res=0; count=0;
+ while((sect=stac_cwalk_sector(&cw))>0)
+ { if(cw.bytes==SECTOR_SIZE) bh=raw_getblk(sb,sect);
+ else bh=raw_bread(sb,sect);
+ if(bh==NULL)res=-EIO;
+ else
+ { if(count+cw.bytes>cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_write_cluster: internal cw error 1 cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ stac_cwalk_done(&cw);
+ goto error_return;
+ };
+ if(count+cw.bytes<=length)
+ memcpy(bh->b_data+cw.offset,clusterk+count,cw.bytes);
+ else
+ { if((i=length-count)>0)
+ { memcpy(bh->b_data+cw.offset,clusterk+count,i);
+ memset(bh->b_data+cw.offset+i,0,cw.bytes-i);
+ } else memset(bh->b_data+cw.offset,0,cw.bytes);
+ };
+ raw_set_uptodate(sb,bh,1);/*getblk needs this*/
+ raw_mark_buffer_dirty(sb,bh,1);
+ raw_brelse(sb,bh);
+ };
+ count+=cw.bytes;
+ }
+ }
+ stac_cwalk_done(&cw);
+ if((count<length)||(count!=cw.bytes_in_clust))
+ printk(KERN_ERR "DMSDOS: stac_write_cluster: internal cw error 2 cluster=%d\n",
+ clusternr);
+
+ error_return:
+ if(method!=UNCOMPRESSED&&ucflag>=0)FREE(clusterk);
+ /* better not free the daemon raw data here - Frank */
+
+ return res;
+}
+
+#endif /*__KERNEL__||__DMSDOS_LIB__*/
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/dstacker_dec.c b/src/dstacker_dec.c
new file mode 100644
index 0000000..28ded1c
--- /dev/null
+++ b/src/dstacker_dec.c
@@ -0,0 +1,824 @@
+/*
+dstacker_dec.c
+
+DMSDOS CVF-FAT module: stacker decompression routines.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Stacker decompression (based on sd4_cc package):
+
+ (C) Copyright 1996 by Jaroslav Fojtik (stacker 3 decompression)
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+*****************************************************************************
+
+*/
+
+#ifdef __KERNEL__
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/locks.h>
+#include <asm/segment.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/msdos_fs.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/shm.h>
+#include <linux/mman.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#endif
+
+#include "dmsdos.h"
+
+#ifdef __DMSDOS_LIB__
+/* some interface hacks */
+#include"lib_interface.h"
+#include<malloc.h>
+#include<string.h>
+#include<errno.h>
+#endif
+
+#ifdef __GNUC__
+#define INLINE static inline
+#else
+/* non-gnu compilers may not like inline */
+#define INLINE static
+#endif
+
+#ifdef DMSDOS_CONFIG_STAC
+
+#if defined(__GNUC__) && defined(__i386__) && defined(USE_ASM)
+#define USE_GNU_ASM_i386
+
+/* copy block, overlaping part is replaced by repeat of previous part */
+/* pointers and counter are modified to point after block */
+#define M_MOVSB(D,S,C) \
+__asm__ /*__volatile__*/(\
+ "cld\n\t" \
+ "rep\n\t" \
+ "movsb\n" \
+ :"=D" (D),"=S" (S),"=c" (C) \
+ :"0" (D),"1" (S),"2" (C) \
+ :"memory")
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ __asm__("xchgb %b0,%h0" /* swap bytes */
+ : "=q" (x)
+ : "0" (x));
+ return x;
+ }
+
+
+#else
+
+#ifdef __GNUC__
+/* non-gnu compilers may not like warning directive */
+#warning USE_GNU_ASM_I386 not defined, using "C" equivalent
+#endif
+
+#define M_MOVSB(D,S,C) for(;(C);(C)--) *((__u8*)(D)++)=*((__u8*)(S)++)
+
+INLINE __u16 swap_bytes_in_word(__u16 x)
+ {
+ return ((x & 0x00ff) << 8) | ((x & 0xff00) >> 8);
+ }
+
+#endif
+
+#if !defined(cpu_to_le16)
+ /* for old kernel versions - works only on i386 */
+ #define le16_to_cpu(v) (v)
+ #define be16_to_cpu(v) (swap_bytes_in_word(v))
+#endif
+
+/***************************************************************************/
+/***************************************************************************/
+/********* begin code from sd3_bs0.c ***************************************/
+
+/*#define INLINE inline*/
+
+typedef struct
+ {
+ __u8 *ptr;
+ int x;
+ int pos;
+ int max_x;
+ }bitstreamC;
+
+void InitBitStream(bitstreamC *b,void *k,int max_x)
+{
+ b->ptr=(__u8 *)k;
+ b->pos=0x8;
+ b->x=0;
+ b->max_x=max_x;
+}
+
+
+INLINE int Read9BitC(bitstreamC *b)
+{
+unsigned int a;
+
+a = (unsigned) *(b->ptr++) << 8;
+a|= *b->ptr;
+a=(a >> (--b->pos));
+b->x++;
+if(b->pos==0)
+ {
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ }
+return(a & 0x1FF);
+}
+
+INLINE int Read4BitC(bitstreamC *b)
+{
+unsigned int a;
+
+if(b->pos<=3) {
+ b->pos+=4;
+ a = (unsigned)*(b->ptr++) << 8;
+ a|= *b->ptr;
+ a=(a >> (b->pos));
+ b->x++;
+ return(a & 0xF);
+ }
+ else
+ {
+ if(b->pos==4)
+ {
+ a=*(b->ptr);
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ return(a & 0x0F);
+ }
+
+ b->pos-=4;
+ return((*(b->ptr) >> (b->pos))& 0x0F);
+ }
+}
+
+INLINE int Read2BitC(bitstreamC *b)
+{
+unsigned char a;
+
+
+
+if(b->pos<=1) {
+ a=*(b->ptr++) << 1;
+ b->x++;
+ b->pos=0x7;
+ if(*b->ptr >=128) a++;
+ return(a & 0x3);
+ }
+ else
+ {
+ if(b->pos==2)
+ {
+ a=*(b->ptr);
+ (b->ptr)++;
+ b->x++;
+ b->pos=0x8;
+ return(a & 0x03);
+ }
+
+ b->pos-=2;
+ return((*(b->ptr) >> (b->pos))& 0x03);
+ }
+}
+
+int ReadBitC(bitstreamC *b)
+{
+int a;
+
+a=(*(b->ptr) >> --(b->pos)) & 1;
+if(b->pos==0)
+ {
+ (b->ptr)++;
+ b->x++;
+ b->pos=8;
+ }
+return(a);
+}
+
+/*---------------------------------------------------------*/
+
+int ReadNC(bitstreamC *b)
+{
+int repeater,rep;
+
+ rep=repeater=Read2BitC(b);
+ if (rep==3)
+ {
+ rep=Read2BitC(b);
+ repeater += rep;
+ if (rep==3)
+ {
+ rep=Read4BitC(b);
+ repeater += rep;
+ while(rep==15)
+ {
+ if(b->x>=b->max_x)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: ReadNC error!\n");
+ return(0);
+ };
+ rep=Read4BitC(b);
+ repeater += rep;
+ }
+ }
+ }
+return(repeater);
+}
+
+#define __dcflDebugInfo 0x8000
+
+INLINE __u8 sd3_xorsum_D(__u8 *data,int len)
+{
+ __u8 sum=0xFF;
+ while(len--) sum^=*(data++);
+ return(sum);
+};
+
+int sd3_decomp(void *data,int CompSize,void *DecompData,int DecompSize,
+ int Flags)
+{
+ bitstreamC bb;
+ int DataSize=DecompSize;
+ int token,repN;
+ __u8 *Decomp,*P;
+
+ InitBitStream(&bb,data,CompSize);
+ Decomp=(__u8 *)DecompData;
+
+ while(CompSize>bb.x+2)
+ {
+ token=Read9BitC(&bb);
+ if(DataSize<=0)
+ {
+ if(token!=0x180) printk(KERN_INFO "DMSDOS: stac3_decomp: end token 0x%02X\n",
+ (unsigned)token);
+ break;
+ };
+
+ if(token>=256)
+ {
+ token=token & 0xFF;
+ if(token==0x81)
+ {
+ repN=ReadNC(&bb)+2;
+#ifdef dcflDebugInfo
+ printk(KERN_DEBUG "DMSDOS: stac3_decomp: Rep:(%dx) ",repN);
+#endif
+ if(DataSize<repN)
+ {
+ repN=DataSize;
+ printk(KERN_ERR "DMSDOS: stac3_decomp: char repeat overrun!\n");
+ return(0);
+ }
+ memset((void *)Decomp,*(Decomp-1),repN);
+ Decomp+=repN;
+ DataSize-=repN;
+ continue;
+ }
+
+ if (token >= 0x80)
+ {
+ token=token & 0x7F;
+ if(!token) break;
+ }
+ else
+ {
+ if (token<8)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Unknown token %d on pos 0x%X->0x%X\n",
+ token, bb.ptr-(__u8*)data, Decomp-(__u8*)DecompData);
+ return(0);
+ }
+ token=16*token+Read4BitC(&bb);
+ }
+ repN=ReadNC(&bb)+2;
+#ifdef dcflDebugInfo
+ printk(KERN_DEBUG "DMSDOS: stac3_decomp: Multi rep:(%dx %d) ",token,repN);
+#endif
+ if(DataSize<repN)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Multi rep overrun 0x%x at pos 0x%x->0x%x\n",
+ repN,bb.ptr-(__u8*)data,Decomp-(__u8*)DecompData);
+ repN=DataSize;
+ return(0);
+ }
+/* memmove(Decomp,Decomp-token,repN); Decomp+=repN; */
+ DataSize-=repN;
+
+ P=Decomp-token;
+ /* this prevents segfaults in case of strange error */
+ if(P<(__u8*)DecompData)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: Illegal back pointer length 0x%x at pos 0x%x->0x%x\n",
+ token,bb.ptr-(__u8*)data,Decomp-(__u8*)DecompData);
+ break;
+ };
+ while(repN--) *(Decomp++)=*(P++);
+
+ }
+ else
+ {
+ *Decomp=token; /*ReadnBitC(&bb,8);*/
+/* printk(" %c",*Decomp,*Decomp);*/
+ Decomp++;
+ if(DataSize!=0) DataSize--;
+ }
+ }
+
+ if(bb.pos!=8) {bb.x++;bb.ptr++;};
+ if(CompSize>bb.x)
+ {
+ /* Check data xor sum */
+ __u8 sum;
+ sum=sd3_xorsum_D((__u8*)DecompData,DecompSize-DataSize);
+ if(sum^*bb.ptr)
+ {
+ printk(KERN_ERR "DMSDOS: stac3_decomp: xor sum error!\n");
+ return(0);
+ };
+ };
+
+ return(DecompSize-DataSize);
+}
+
+
+/**************** end code from sd3_bs0.c ********************************/
+
+/*************************************************************************/
+/*************************************************************************/
+/*************** begin code from sd4_bs1.c *******************************/
+
+typedef
+ struct {
+ __u32 buf; /* bit buffer */
+ int pb; /* not read bits count in buffer */
+ __u16 *pd; /* first not readed input data */
+ __u16 *pe; /* after end of data */
+ } bits_t;
+
+typedef
+ struct {
+ __u8 ch[0x400]; /* characters codes */
+ __u8 ln[0x400]; /* characters lens .. if >=0x80 controll */
+ __u8 ch1[0x200]; /* for codes vith more than bn bits */
+ __u8 ln1[0x200];
+ int bn; /* ch,ln array max convert bits, longer use ch1,cl1 */
+ __u16 cd_ln[16]; /* distribution of bits */
+ __u16 cd_ch[16]; /* distribution of codes codes */
+ }huf_t;
+
+const unsigned sd4b_bmsk[]=
+ {0x0,0x1,0x3,0x7,0xF,0x1F,0x3F,0x7F,0xFF,
+ 0x1FF,0x3FF,0x7FF,0xFFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF};
+
+#define RDN_G16(bits) \
+ { \
+ (bits).buf<<=16; \
+ (bits).pb+=16; \
+ if((bits).pd<(bits).pe) \
+ { \
+ (bits).buf|=le16_to_cpu(*((bits).pd++)); \
+ }; \
+ }
+
+#define RDN_PR(i,bits,n,G16) \
+ { \
+ if((bits).pb<16) G16(bits); \
+ i=(bits).buf>>((bits).pb-=(n)); \
+ }
+
+INLINE void sd4b_rdi(bits_t *pbits,void *pin,unsigned lin)
+{
+ pbits->pb=0;
+ pbits->pd=(__u16*)pin;
+ pbits->pe=pbits->pd+((lin+1)>>1);
+};
+
+INLINE unsigned sd4b_rdn(bits_t *pbits,int n)
+{
+ unsigned i;
+ RDN_PR(i,*pbits,n,RDN_G16);
+ i&=sd4b_bmsk[n];
+ return i;
+};
+
+#define OUT_OVER 0x100
+
+/* read and huffman decode of characters, stops on tokens or buffer ends */
+INLINE unsigned sd4b_rdh(bits_t *pbits,const huf_t *phuf,__u8 **pout,__u8 *pend)
+{
+
+ unsigned ch;
+ unsigned bmsk=sd4b_bmsk[phuf->bn];
+
+ while(1)
+ {while(1)
+ {if(pbits->pb<16)
+ RDN_G16(*pbits);
+ if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+
+ if(pbits->pb>=16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+
+ if(pbits->pb>=16)
+ {if (*pout>=pend) return OUT_OVER;
+ ch=(pbits->buf>>(pbits->pb-phuf->bn))&bmsk;
+ if((pbits->pb-=phuf->ln[ch])<0) break;
+ *((*pout)++)=phuf->ch[ch];
+ };
+ };
+ };
+
+ ch=phuf->ch[ch];
+ pbits->pb+=0x40; if(ch) return ch;
+ /* code longer than phuf->bn */
+ if(pbits->pb<16) RDN_G16(*pbits);
+ ch=(pbits->buf>>(pbits->pb-16))&0xFFFF;
+ {
+ int i;
+ i=phuf->bn;
+ do
+ i++;
+ while(phuf->cd_ch[i]<=(ch>>(16-i))&&(i<15));
+ ch=(ch>>(16-i))-phuf->cd_ch[i]+phuf->cd_ln[i];
+ };
+ if((pbits->pb-=phuf->ln1[ch])<0)
+ {pbits->pb+=0x40;
+ return phuf->ch1[ch];
+ };
+ *((*pout)++)=phuf->ch1[ch];
+ };
+};
+
+INLINE int sd4b_rdhufi(huf_t *phuf,int m,int bn,__u8 *ca)
+{
+ if(bn>10) bn=10;
+ phuf->bn=bn;
+ {
+ int i;
+ unsigned u,us,ut;
+ memset(phuf->cd_ln,0,sizeof(phuf->cd_ln));i=0;
+ while((u=ca[i++])<16) phuf->cd_ln[u]++;
+ memset(phuf->cd_ch,0,sizeof(phuf->cd_ch));
+ phuf->cd_ln[0]=0;us=0;ut=0;
+ for(i=1;i<16;i++)
+ {
+ u=phuf->cd_ln[i];phuf->cd_ln[i]=ut;
+ phuf->cd_ch[i]=us;ut+=u;us+=u;us<<=1;
+ };
+ /* if suceed us should be 0x10000 */
+ if (us&0xFFFF) return(0);
+ };
+ {
+ int i,ln,ch,sh,cod;
+ for(i=0;(ln=ca[i])<16;i++) if(ln)
+ {
+ sh=(bn-ln);
+ cod=(phuf->cd_ch[ln])++;
+ if(i<m) ch=i; else {ch=i-m+1;ln+=0x40;};
+ if (sh>0)
+ {
+ memset(phuf->ch+(cod<<sh),ch,1<<sh);
+ memset(phuf->ln+(cod<<sh),ln,1<<sh);
+ } else if (sh==0) {
+ phuf->ch[cod]=ch;
+ phuf->ln[cod]=ln;
+ } else {
+ cod>>=-sh;
+ phuf->ch[cod]=0x00;
+ phuf->ln[cod]=0x40;
+ cod=(phuf->cd_ln[ln&0xF])++;
+ phuf->ch1[cod]=ch;
+ phuf->ln1[cod]=ln;
+ };
+ };
+ /* if suceed ln should be 0xFF */
+ };
+ return(1);
+};
+
+#if 0
+/* token decoding tables */
+ const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+#endif
+#if 1
+ extern const unsigned int sd4b_prog_len[];
+ extern const unsigned int sd4b_prog_add[];
+ extern const signed char sd4b_reps_div3[];
+ extern const signed char sd4b_reps_n[];
+ extern const unsigned char sd4b_reps_b[];
+ extern const unsigned int sd4b_reps_m[];
+#endif
+#if 0
+ static const unsigned int sd4b_prog_len[]={ 5, 7, 9, 11};
+ static const unsigned int sd4b_prog_add[]={ 0x1,0x21,0xA1,0x2A1};
+ static const signed char sd4b_reps_div3[]={0,0,0,0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,
+ 6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13,13,14,14,14,
+ 15,15,15,16,16,16,17,17,17,18,18,18,19,19,19,20,20,20};
+ static const signed char sd4b_reps_n[] = {3-1,3-4,3-7,3-10,3-13,3-16,3-19,3-22,
+ 3-25,3-28,3-31,3-34,3-37,3-40,3-43,3-46,3-49,3-52,3-55,3-58,3-61};
+ static const unsigned char sd4b_reps_b[] = {0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9};
+ static const unsigned int sd4b_reps_m[] = {1,2,3,4,6,8,12,16,24,32,48,64,96,128,
+ 192,256,384,512,768,1024,1536};
+#endif
+
+int sd4_decomp(void* pin,int lin, void* pout, int lout, int flg)
+{
+ bits_t bits;
+ huf_t *huf;
+ unsigned u;
+ __u8 len_150;
+
+ sd4b_rdi(&bits,pin,lin);
+ u=sd4b_rdn(&bits,16);
+ if(u!=0x81) {printk(KERN_ERR "DMSDOS: sd4_decomp: Magic = %X => error!\n",u);return 0;};
+
+ huf=(huf_t*)MALLOC(sizeof(huf_t));
+ if(!huf) {printk(KERN_ERR "DMSDOS: sd4_decomp: no memory!\n");return 0;};
+
+ {
+ int i;
+ int ie;
+ int bmax1,bmax2;
+ unsigned u;
+ __u8 ca[0x180];/* 12B4 */
+ __u8 *pca;
+ __u8 *pcae;
+
+ memset(ca,0,22);
+ i=sd4b_rdn(&bits,3)+1;
+ ie=bmax2=sd4b_rdn(&bits,5);
+ if(i>ie) {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 1 error\n");goto error;};
+ ca[0]=bmax1=sd4b_rdn(&bits,4);
+ while(1)
+ {
+ while(i<=ie)
+ {
+ u=sd4b_rdn(&bits,4);
+ ca[i++]=u;if(u>bmax1) bmax1=u;
+ };
+ if(ie==0x15) break;
+ i=0x10;ie=0x15;
+ };
+ ca[22]=0xFF;
+
+ if(!sd4b_rdhufi(huf,0x10,7<bmax1?7:bmax1,ca))
+ {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 1 consistency check !!!!\n");goto error;};
+
+ pca=ca;
+ pcae=ca+0x150;
+ while((u=sd4b_rdh(&bits,huf,&pca,pcae))<=6)
+ {
+ switch (u)
+ {
+ unsigned n;
+ case 1: /* 2 times zerro */
+ pca[1]=pca[0]=0;pca+=2;
+ break;
+ case 2: /* 3 times zerro */
+ pca[2]=pca[1]=pca[0]=0;pca+=3;
+ break;
+ case 3: /* zerro fill */
+ n=4+(u=sd4b_rdn(&bits,3));
+ if (u==7) do {u=sd4b_rdn(&bits,7);n+=u;} while (u==0x7F);
+ if ((pca+n)>pcae) n=pcae-pca;
+ memset(pca,0,n);
+ pca+=n;
+ break;
+ case 4: /* 2 times last char */
+ pca[1]=pca[0]=*(pca-1);pca+=2;
+ break;
+ case 5: /* 3 times last char */
+ u=*(pca-1);
+ if (pca<pcae) {pca[0]=pca[1]=pca[2]=u;pca+=3;};
+ break;
+ case 6: /* repeat last chr */
+ n=4;
+ do {u=sd4b_rdn(&bits,3);n+=u;} while (u==7);
+ if ((pca+n)>pcae) n=pcae-pca;
+ memset(pca,*(pca-1),n);
+ pca+=n;
+ break;
+ };
+ };
+ ca[0x150]=0xFF;
+ len_150=ca[0x14F];
+
+ if(!sd4b_rdhufi(huf,0x100,bmax2,ca))
+ {printk(KERN_ERR "DMSDOS: sd4_decomp: Table 2 consistency check !!!!\n");goto error;};
+
+ };
+ {
+ __u8 *p,*r,*pe;
+ p=(__u8*)pout;pe=p+lout;
+ while((u=sd4b_rdh(&bits,huf,&p,pe))<0x50)
+ {
+ {
+ unsigned n,m;
+
+ if (u<0x40) {
+ m=sd4b_reps_div3[u]; /* short repeat tokens */
+ n=u+sd4b_reps_n[m];
+ u=sd4b_reps_b[m];
+ m=sd4b_reps_m[m];
+ if (u) m+=sd4b_rdn(&bits,u);
+ } else {
+ m=sd4b_rdn(&bits,2); /* Repeat n times last m characters */
+ m=sd4b_rdn(&bits,sd4b_prog_len[m])+sd4b_prog_add[m];
+ if((n=u-0x40+6)==0x15)
+ if((n+=sd4b_rdn(&bits,4))==0x15+0xF)
+ if((n+=sd4b_rdn(&bits,8))==0x15+0xF+0xFF)
+ if((n+=sd4b_rdn(&bits,12))==0x15+0xF+0xFF+0xFFF)
+ n+=sd4b_rdn(&bits,16);
+ };
+ if ((__u8*)pout+m>p)
+ {m=p-(__u8*)pout;printk(KERN_ERR "DMSDOS: sd4_decomp: Under !!!\n");};
+ if (p+n>pe)
+ {n=pe-p;printk(KERN_ERR "DMSDOS: sd4_decomp: Over !!!!\n");};
+ /*memcpy(p,p-m,n);p+=n;*/
+ r=p-m;M_MOVSB(p,r,n); /* copy/repeat function */
+ };
+ };
+ if((u==OUT_OVER)&&len_150)
+ {
+ int i;
+ if((i=sd4b_rdn(&bits,len_150))==huf->cd_ch[len_150]-1) u=0x50;
+ else printk(KERN_ERR "DMSDOS: sd4_decomp: End read %X and should be %X\n",i,(int)huf->cd_ch[len_150]-1);
+ };
+ if(u==0x50)
+ {
+ FREE(huf);
+ return(p-(__u8*)pout);
+ }
+ else {printk(KERN_ERR "DMSDOS: sd4_decomp: Error end token %X\n",u);};
+ };
+
+ error:
+ FREE(huf);
+ return 0;
+};
+
+/*************** end code from sd4_bs1.c *********************************/
+
+int stac_decompress(unsigned char*buf_in, int len_in,
+ unsigned char*buf_out, int len_out)
+{ int alg_info;
+
+ alg_info=le16_to_cpu(*(__u16*)buf_in);
+ switch(alg_info)
+ { case 0x0081:
+ return(sd4_decomp(buf_in,len_in,buf_out,len_out,0));
+ case 0x5344:
+ /* call DS decompression from dmsdos_dec */
+ /* mde.size_hi_minus_1=(len_out-1)/SECTOR_SIZE; */
+ /* return(dbl_decompress(buf_out,buf_in,&mde)); */
+ return(ds_dec(buf_in,len_in,buf_out,len_out,0x4000));
+ default:
+ return(sd3_decomp(buf_in,len_in,buf_out,len_out,0));
+ };
+}
+
+/* Specification:
+ This function reads a stacker cluster into clusterd.
+ It must take care of fragmentation and decompression.
+ In case of failure it must return a negative error code,
+ otherwise it returns number of used bytes in cluster.
+*/
+int stac_read_cluster(struct super_block*sb,unsigned char*clusterd,
+ int clusternr)
+{
+ int sect;
+ int count,val,bytesperclust;
+ struct buffer_head*bh;
+ __u8 * clusterk;
+ Stac_cwalk cw;
+ Dblsb*dblsb=MSDOS_SB(sb)->private_data;
+
+ /* Prepare buffers for next read of cluster */
+ if(clusterd==NULL)
+ { if((val=stac_cwalk_init(&cw,sb,clusternr,0))>0)
+ { while((sect=stac_cwalk_sector(&cw))>0)
+ { dblspace_reada(sb,sect,cw.flen+1);
+ cw.flen=0;
+ };
+ };
+ stac_cwalk_done(&cw);
+ return 0;
+ }
+
+ /* Regular start of cluster read */
+
+ val=stac_cwalk_init(&cw,sb,clusternr,2);
+ if (val<0)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: alloc error in cluster %d\n",
+ clusternr);
+ return -EIO;
+ };
+
+ bytesperclust=dblsb->s_sectperclust*SECTOR_SIZE;
+ if(val==0)
+ { memset(clusterd,0,bytesperclust);
+ /* I am not happy, that I cannot consider this as error (printk),
+ but dblspace_getblk must fill rest of cluster and cannot
+ call noread, some cases in dblspace_file_write are problematic too,
+ Pavel */
+ LOG_CLUST("DMSDOS: stac_read_cluster: lost cluster (cluster %d)\n",
+ clusternr);
+ return 0;
+ }
+
+ if(cw.compressed)
+ { clusterk=(unsigned char*)MALLOC(cw.bytes_in_clust);
+ if(clusterk==NULL)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: no memory!\n");
+ stac_cwalk_done(&cw);
+ return -EIO;
+ }
+ }
+ else clusterk=clusterd;
+ count=0;
+
+ while((sect=stac_cwalk_sector(&cw))>0)
+ { bh=raw_bread(sb,sect);
+ if(bh==NULL)
+ { error1:
+ if(cw.compressed) FREE(clusterk);
+ stac_cwalk_done(&cw);
+ return -EIO;
+ }
+ if(count+cw.bytes>cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: internal cw error 1 cluster=%d\n",
+ clusternr);
+ raw_brelse(sb,bh);
+ goto error1;
+ };
+ memcpy(clusterk+count,bh->b_data+cw.offset,cw.bytes);
+ count+=cw.bytes;
+ raw_brelse(sb,bh);
+ };
+ if(count!=cw.bytes_in_clust)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: internal cw error 2 cluster=%d\n",
+ clusternr);
+ goto error1;
+ };
+ if(cw.compressed)
+ { count=stac_decompress(clusterk,count,clusterd,bytesperclust);
+ FREE(clusterk);
+ if(!count)
+ { printk(KERN_ERR "DMSDOS: stac_read_cluster: decompression error cluster=%d\n",
+ clusternr);
+ };
+ };
+ stac_cwalk_done(&cw);
+ if(count<=0) return -EIO;
+ if(bytesperclust-count>0) memset(clusterd+count,0,bytesperclust-count);
+ return (count);
+}
+
+#endif /* DMSDOS_CONFIG_STAC */
diff --git a/src/dutil.c b/src/dutil.c
new file mode 100644
index 0000000..aa30db4
--- /dev/null
+++ b/src/dutil.c
@@ -0,0 +1,419 @@
+/*
+dutil.c
+
+DMSDOS CVF-FAT module: external dmsdos utility.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include "dmsdos.h"
+#include<sys/ioctl.h>
+#include<sys/types.h>
+#include<sys/stat.h>
+#include<fcntl.h>
+#include<string.h>
+#include<errno.h>
+#include<unistd.h>
+#include<malloc.h>
+
+int scan(char*arg)
+{ int w;
+
+ if(strncmp(arg,"0x",2)==0)sscanf(arg+2,"%x",&w);
+ else sscanf(arg,"%d",&w);
+
+ return w;
+}
+
+void error(void)
+{ perror("ioctl failed");
+ exit();
+}
+
+int main(int argc, char*argv[])
+{ Dblsb dblsb;
+ int fd;
+ int ret;
+ unsigned long w[10];
+ Dblstat dblstat;
+ double ratio,dosratio;
+ struct
+ { unsigned long w;
+ unsigned char data[512];
+ } buffer;
+ int i;
+ char a[100];
+ char b[100];
+ char c[100];
+ Mdfat_entry mde;
+
+ if(argc<2)
+ {
+ printf("DMSDOS utility (C) 1995-1998 Frank Gockel, Pavel Pisa\n");
+ printf("compiled " __DATE__ " " __TIME__ " under dmsdos version %d.%d.%d%s\n\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL,DMSDOS_VLT);
+
+ printf("Usage: %s (directory)\n",argv[0]);
+ printf(" %s (directory) cluster (clusterno)\n",argv[0]);
+ printf(" %s (directory) sector (sectorno) [(file)]\n",argv[0]);
+ printf(" %s (directory) rcluster (clusterno) [(file)]\n",argv[0]);
+ printf(" %s (directory) rrawcluster (clusterno) [(file)]\n",argv[0]);
+ printf(" %s (directory) bitfat (sectorno)\n",argv[0]);
+ printf(" %s (directory) setcomp (comp_option)\n",argv[0]);
+ printf(" %s (directory) setcf (cf_option)\n",argv[0]);
+ printf(" %s (directory) dumpcache\n",argv[0]);
+ printf(" %s (directory) synccache [(allow_daemon)]\n",argv[0]);
+ printf(" %s (directory) logstat\n",argv[0]);
+ printf(" %s (directory) memory\n",argv[0]);
+ printf(" %s (directory) checkfs [(repair)]\n",argv[0]);
+ printf(" %s (directory) setloglevel (value)\n",argv[0]);
+ printf(" %s (directory) setspeedup (value)\n",argv[0]);
+ return 0;
+ }
+
+ fd=open(argv[1],O_RDONLY);
+ if(fd<0)
+ { perror(argv[1]);
+ return 2;
+ }
+
+ /* this hack enables reverse version check */
+ /* it must not be changed in order to recognize incompatible older versions */
+ /* this also depends on s_dcluster being the first record in Dblsb */
+ dblsb.s_dcluster=DMSDOS_VERSION;
+
+ ret=ioctl(fd,DMSDOS_GET_DBLSB,&dblsb);
+ if(ret<0)
+ { printf("This is not a DMSDOS directory.\n");
+ close(fd);
+ return 2;
+ }
+ printf("You are running DMSDOS driver version %d.%d.%d.\n",(ret&0xff0000)>>16,
+ (ret&0x00ff00)>>8,ret&0xff);
+ /*printf("debug: ret=0x%08x\n",ret);*/
+ if(ret!=DMSDOS_VERSION)printf("This utility was compiled for DMSDOS version %d.%d.%d",
+ (DMSDOS_VERSION&0xff0000)>>16,(DMSDOS_VERSION&0x00ff00)>>8,DMSDOS_VERSION&0xff);
+ if(ret&0x0f000000)
+ { printf("\nSorry, this utility is too old for the actual DMSDOS driver version.\n");
+ close(fd);
+ return 2;
+ }
+ if(ret<0x00000902)
+ { printf("\nSorry, this utility requires at least DMSDOS driver version 0.9.2.\n");
+ close(fd);
+ return 2;
+ }
+ if(ret!=DMSDOS_VERSION)printf(" but should still work.\n\n");
+ else printf("\n");
+
+ printf("Parameters of the CVF the directory specified belongs to:\n");
+ printf("dcluster: %5d ",dblsb.s_dcluster);
+ printf("mdfatstart: %5d ",dblsb.s_mdfatstart);
+ printf("fatstart: %5d ",dblsb.s_fatstart);
+ printf("rootdir: %5d\n",dblsb.s_rootdir);
+ printf("root_entries:%5d ",dblsb.s_rootdirentries);
+ printf("sectperclust:%5d ",dblsb.s_sectperclust);
+ printf("bootblock: %5d ",dblsb.s_bootblock);
+ printf("16bitfat: %5s\n",dblsb.s_16bitfat?"yes":"no");
+ printf("datastart: %7d ",dblsb.s_datastart);
+ printf("dataend: %7d ",dblsb.s_dataend);
+ printf("comp: 0x%08x ",dblsb.s_comp);
+ printf("cfaktor: %7d\n",dblsb.s_cfaktor+1);
+ printf("max_cluster: %5d ",dblsb.s_max_cluster);
+ printf("max_cluster2:%5d ",dblsb.s_max_cluster2);
+ printf("cvf_version: %5d ",dblsb.s_cvf_version);
+ printf("free_sec: %7d\n",dblsb.s_free_sectors);
+ if(argc==3)
+ {
+ if(strcmp(argv[2],"dumpcache")==0)
+ { if(ioctl(fd,DMSDOS_DUMPCACHE,w)<0)error();
+ printf("Cache status written to syslog.\n");
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"logstat")==0)
+ { if(ioctl(fd,DMSDOS_LOG_STATISTICS,w)<0)error();
+ printf("Statistics written to syslog.\n");
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"memory")==0)
+ { if(ioctl(fd,DMSDOS_REPORT_MEMORY,w)<0)error();
+ printf("DMSDOS memory usage (in bytes):\n");
+ printf("Cluster cache: %8ld maximum: ",w[0]);
+ if(w[1]>0)printf("%8ld (estimated)\n",w[1]);
+ else printf(" -unknown- \n");
+ printf("Buffer cache: %8ld maximum: %8ld\n",w[2],w[3]);
+ return 0;
+ }
+
+ }
+
+ if(argc==3||argc==4)
+ {
+ if(strcmp(argv[2],"checkfs")==0)
+ { printf("Please wait while filesystem is checked...\n");
+ w[0]= (argc==4) ? scan(argv[3]) : 0;
+ if(ioctl(fd,DMSDOS_SIMPLE_CHECK,w)<0)error();
+ if(w[0]==1||w[0]==2)printf("Check aborted due to lack of kernel memory.\n");
+ if(w[0]==0)printf("No filesystem error found.\n");
+ if(w[0]==-1)printf("Filesystem has serious errors: FAT level crosslink(s) found.\n");
+ if(w[0]==-2)printf("Filesystem has serious errors: MDFAT level crosslink(s) found.\n");
+ if(w[0]==-3)printf("Filesystem BITFAT mismatches MDFAT.\n");
+ if(w[0]==-1||w[0]==-2||w[0]==-3)
+ { if(ioctl(fd,DMSDOS_SET_COMP,READ_ONLY)<0)error();
+ printf("The filesystem has been set to read-only mode.\n");
+ }
+ close(fd);
+ return 0;
+ }
+ if(strcmp(argv[2],"synccache")==0)
+ { printf("Syncing cluster cache....be patient, this may take some time...\n");
+ if(ioctl(fd,DMSDOS_SYNC_CCACHE,(argc==4) ? scan(argv[3]) : 0)<0)error();
+ printf("Cluster cache synced.\n");
+ close(fd);
+ return 0;
+ }
+
+ }
+
+ if(argc<4)
+ {
+
+ printf("\nPlease wait while filesystem is scanned...\n\n");
+
+ if(ioctl(fd,DMSDOS_EXTRA_STATFS,&dblstat)<0)error();
+
+ printf("free sectors: %7ld ",dblstat.free_sectors);
+ printf("used sectors: %7ld ",dblstat.used_sectors);
+ printf("all sectors: %7ld\n",dblstat.free_sectors+dblstat.used_sectors);
+ printf("max free hole: %7ld ",dblstat.max_hole);
+ i=(100*(dblstat.free_sectors-dblstat.max_hole))/dblstat.free_sectors;
+ printf("fragmentation:%7d%% ",i);
+ i=(100*dblstat.used_sectors)/(dblstat.free_sectors+dblstat.used_sectors);
+ printf("capacity: %7d%%\n",i);
+ printf("free clusters: %7ld ",dblstat.free_clusters);
+ printf("used clusters: %7ld ",dblstat.used_clusters);
+ printf("all clusters: %7ld\n",dblstat.free_clusters+dblstat.used_clusters
+ +dblstat.lost_clusters);
+ printf("compressed: %7ld ",dblstat.compressed_clusters);
+ printf("uncompressed: %7ld ",dblstat.uncompressed_clusters);
+ printf("lost clusters: %7ld\n",dblstat.lost_clusters);
+ printf("cluster compression: %5ld%% ",
+ (dblstat.compressed_clusters+dblstat.uncompressed_clusters==0) ? 0 :
+ (100*dblstat.compressed_clusters)/
+ (dblstat.compressed_clusters+dblstat.uncompressed_clusters)
+ );
+
+ if(dblstat.sectors_lo!=0)
+ ratio=((double)dblstat.sectors_hi)/((double)dblstat.sectors_lo);
+ else ratio=2.0;
+
+ if(dblstat.used_clusters!=0)
+ dosratio=((double)dblstat.used_clusters*dblsb.s_sectperclust)/
+ ((double)dblstat.used_sectors);
+ else dosratio=2.0;
+
+ printf("compression ratio: %5.2f : 1 / %5.2f : 1\n",ratio,dosratio);
+ printf("space allocated by clusters (real allocated space): %7ldKB\n",
+ dblstat.sectors_lo/2);
+ printf("space allocated by clusters (space after decompression): %7ldKB\n",
+ dblstat.sectors_hi/2);
+ printf("compressed free space (estimated free space): %7dKB\n",
+ ((int)(dblstat.free_sectors*ratio))/2);
+ printf("uncompressed free space: %7ldKB\n",
+ dblstat.free_sectors/2);
+ printf("maximum free space due to cluster limit: %7ldKB\n",
+ dblstat.free_clusters*dblsb.s_sectperclust/2);
+
+ if(dblstat.max_hole<=dblsb.s_sectperclust*3&&
+ dblstat.free_sectors>dblsb.s_sectperclust*3)
+ printf("Warning: This CVF should be defragmented at internal MDFAT level.\n");
+ else if(dblstat.free_sectors<=dblsb.s_sectperclust*3||dblsb.s_full==2)
+ printf("Warning: This CVF is full. Do not write to it.\n");
+ else if(dblsb.s_full==1)
+ printf("Warning: This CVF is almost full or highly fragmented at internal MDFAT level.\n");
+ else if(dblstat.free_clusters*dblsb.s_sectperclust<dblstat.free_sectors)
+ {
+ printf("Warning: You cannot use all free space of this CVF due to the cluster limit.\n");
+ /*
+ i=((int)(dblstat.free_sectors*ratio))/dblsb.s_sectperclust
+ -dblstat.free_clusters+dblsb.s_max_cluster;
+ if(i>dblsb.s_max_cluster2)i=dblsb.s_max_cluster2;
+ if(i>dblsb.s_max_cluster)
+ printf(" Enlarge the max_cluster value or the dos compression ratio.\n"
+ " I recommend the following max_cluster value for this CVF: %d\n",i);
+ */
+ printf(" Adapt the compression ratio under Dos.\n");
+ }
+
+ }
+
+ else if(strcmp(argv[2],"bitfat")==0)
+ { w[0]=scan(argv[3]);
+ if(ioctl(fd,DMSDOS_READ_BITFAT,w)<0)error();
+ if(w[1]==0)printf("\nbitfat: sector is free\n");
+ if(((signed long)w[1])>0)printf("\nbitfat: sector is allocated\n");
+ if(((signed long)w[1])<0)printf("\nbitfat: value out of range\n");
+ }
+
+ else if(strcmp(argv[2],"cluster")==0)
+ { w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+ printf("used: %s\n",(mde.flags&2)?"yes":"no");
+ printf("compressed: %s\n",(mde.flags&1)?"no":"yes");
+ printf("flags (raw): 0x%x\n",mde.flags);
+ printf("size uncompressed: %d compressed: %d\n",
+ mde.size_hi_minus_1+1,mde.size_lo_minus_1+1);
+ printf("first sector: %ld\n",mde.sector_minus_1+1);
+ printf("unknown bits: %d\n",mde.unknown);
+ if(ioctl(fd,DMSDOS_READ_DFAT,w)<0)error();
+ printf("next cluster: %ld\n",w[1]);
+ }
+
+ else if(strcmp(argv[2],"rrawcluster")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+
+ if(mde.flags&2)
+ { int s;
+ for(s=0;s<mde.size_lo_minus_1+1;++s)
+ { buffer.w=s+mde.sector_minus_1+1;
+ if(ioctl(fd,DMSDOS_READ_BLOCK,&buffer)<0)error();
+ for(i=0;i<512;++i)
+ { if(f)fputc(buffer.data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i+512*s);strcpy(b,"");}
+ sprintf(c," %02X",buffer.data[i]);
+ strcat(a,c);
+ if(buffer.data[i]>=32&&buffer.data[i]<128)
+ sprintf(c,"%c",buffer.data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ }
+ }
+ else printf("unused cluster, contains no raw data.\n");
+
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"rcluster")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ w[0]=scan(argv[3]);
+ w[1]=(unsigned long)(&mde);
+ if(ioctl(fd,DMSDOS_READ_MDFAT,w)<0)error();
+ else
+ {
+ struct
+ { unsigned long w;
+ unsigned char data[1];
+ } *buf = malloc(dblsb.s_sectperclust * 512 + 32);
+ if(buf==NULL)
+ { printf("Uhh... unable to get memory...\n");
+ exit(3);
+ }
+ buf->w = w[0];
+ if(ioctl(fd,DMSDOS_READ_CLUSTER,buf)<0)error();
+ for(i=0;i<512*dblsb.s_sectperclust;++i)
+ { if(f)fputc(buf->data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i);strcpy(b,"");}
+ sprintf(c," %02X",buf->data[i]);
+ strcat(a,c);
+ if(buf->data[i]>=32&&buf->data[i]<128)
+ sprintf(c,"%c",buf->data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ }
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"sector")==0)
+ { FILE*f=NULL;
+ if(argc==5)f=fopen(argv[4],"wb");
+ buffer.w=scan(argv[3]);
+ if(ioctl(fd,DMSDOS_READ_BLOCK,&buffer)<0)error();
+
+ for(i=0;i<512;++i)
+ { if(f)fputc(buffer.data[i],f);
+ if(i%16==0){sprintf(a,"%3X : ",i);strcpy(b,"");}
+ sprintf(c," %02X",buffer.data[i]);
+ strcat(a,c);
+ if(buffer.data[i]>=32&&buffer.data[i]<128)
+ sprintf(c,"%c",buffer.data[i]);
+ else strcpy(c,".");
+ strcat(b,c);
+
+ if(i%16==15)printf("%s %s\n",a,b);
+ }
+ if(f)fclose(f);
+ }
+
+ else if(strcmp(argv[2],"setcomp")==0)
+ { ret=0;
+ if(strcmp(argv[3],"ro")==0)ret=ioctl(fd,DMSDOS_SET_COMP,READ_ONLY);
+ else if(strcmp(argv[3],"no")==0)ret=ioctl(fd,DMSDOS_SET_COMP,UNCOMPRESSED);
+ else if(strcmp(argv[3],"guess")==0)ret=ioctl(fd,DMSDOS_SET_COMP,GUESS);
+ else if(strcmp(argv[3],"ds00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_0);
+ else if(strcmp(argv[3],"ds01")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_1);
+ else if(strcmp(argv[3],"ds02")==0)ret=ioctl(fd,DMSDOS_SET_COMP,DS_0_2);
+ else if(strcmp(argv[3],"jm00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,JM_0_0);
+ else if(strcmp(argv[3],"jm01")==0)ret=ioctl(fd,DMSDOS_SET_COMP,JM_0_1);
+ else if(strcmp(argv[3],"sq00")==0)ret=ioctl(fd,DMSDOS_SET_COMP,SQ_0_0);
+ else if(strcmp(argv[3],"sd4")==0)ret=ioctl(fd,DMSDOS_SET_COMP,SD_4);
+ else printf("??? mode %s not recognized.\n",argv[3]);
+ if(ret<0)error();
+ }
+
+ else if(strcmp(argv[2],"setcf")==0)
+ { if(ioctl(fd,DMSDOS_SET_CF,scan(argv[3])-1)<0)error();
+ }
+
+ else if(strcmp(argv[2],"setmaxcluster")==0)
+ { /*if(ioctl(fd,DMSDOS_SET_MAXCLUSTER,scan(argv[3]))<0)error();*/
+ printf("setmaxcluster is depreciated (sorry, it became too problematic).\n"
+ "Please use the tools that came with your CVF package under Dos.\n");
+ }
+
+ else if(strcmp(argv[2],"setloglevel")==0)
+ { if(ioctl(fd,DMSDOS_SET_LOGLEVEL,scan(argv[3]))<0)error();
+ }
+
+ else if(strcmp(argv[2],"setspeedup")==0)
+ { if(ioctl(fd,DMSDOS_SET_SPEEDUP,scan(argv[3]))<0)error();
+ }
+
+ else printf("??? syntax error in command line.\n");
+
+ close(fd);
+ return 0;
+}
diff --git a/src/lib_interface.c b/src/lib_interface.c
new file mode 100644
index 0000000..3ff6796
--- /dev/null
+++ b/src/lib_interface.c
@@ -0,0 +1,726 @@
+/*
+lib_interface.c
+
+DMSDOS library: interface functions, hacks, dummies, and fakes.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<string.h>
+#include<malloc.h>
+#ifdef USE_FLOCK
+#include<unistd.h>
+#include<sys/file.h>
+#endif
+#ifdef USE_SOPEN
+#include<share.h>
+#endif
+#include<fcntl.h>
+#include<errno.h>
+
+#define fat_boot_sector msdos_boot_sector
+
+/* some interface hacks */
+#include"lib_interface.h"
+#undef MALLOC
+#undef FREE
+#undef CURRENT_TIME
+#undef memcpy
+#undef memset
+#define MALLOC malloc
+#define FREE free
+#define kmalloc(x,y) malloc(x)
+#define kfree free
+#define CURRENT_TIME time(NULL)
+
+#include"dmsdos.h"
+
+#ifndef cpu_to_le16
+/* works only for old kernels and little endian architecture */
+#define cpu_to_le16(v) (v)
+#define cpu_to_le32(v) (v)
+#endif
+
+#define MSDOS_FAT12 4078 /* maximum number of clusters in a 12 bit FAT */
+
+long int blk_size[1][1];
+
+extern Acache mdfat[];
+extern Acache dfat[];
+extern Acache bitfat[];
+
+/* hacks for the low-level interface */
+
+#include <stdarg.h>
+int printk(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ char*p=buf;
+ int i;
+
+ va_start(ap, fmt);
+ i=vsprintf(buf,fmt,ap);
+ va_end(ap);
+
+ if(p[0]=='<'&&p[1]>='0'&&p[1]<='7'&&p[2]=='>')p+=3;
+ if(strncmp(p,"DMSDOS: ",8)==0)p+=8;
+ fprintf(stderr,"libdmsdos: %s",p);
+
+ return i;
+}
+
+void panic(const char *fmt, ...)
+{ va_list ap;
+ char buf[500];
+ int i;
+
+ va_start(ap, fmt);
+ i=vsprintf(buf,fmt,ap);
+ va_end(ap);
+
+ fprintf(stderr,"libdmsdos panic: %s",buf);
+
+ exit(1);
+}
+
+int translate_direct(struct super_block*sb,int block)
+{ int i;
+
+ if(block>=sb->directsize)
+ { printk("DMSDOS: access beyond end of CVF in direct mode (wanted=%d limit=%d)\n",
+ block,sb->directsize-1);
+ return 0;
+ }
+
+ /* calculate physical sector */
+ i=0;
+ do
+ { block-=sb->directlen[i];
+ ++i;
+ }
+ while(block>=0&&i<MAXFRAGMENT);
+ --i;
+ block+=sb->directlen[i]+sb->directlist[i];
+ return block;
+}
+
+struct buffer_head* raw_bread(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(sb->directlist)
+ { block=translate_direct(sb,block);
+ if(!block)
+ { printk("raw_bread: translate_direct failed\n");
+ return NULL;
+ }
+ }
+
+ if(lseek(fd,block*512,SEEK_SET)<0)
+ { printk("raw_bread: lseek block %d failed: %s\n",block,strerror(errno));
+ return NULL;
+ }
+ bh=malloc(sizeof(struct buffer_head));
+ if(bh==NULL)
+ { printk("raw_bread: malloc(%d) failed\n",sizeof(struct buffer_head));
+ return NULL;
+ }
+
+ bh->b_data=malloc(512);
+ if(bh->b_data==NULL)
+ { free(bh);
+ printk("raw_bread: malloc(512) failed\n");
+ return NULL;
+ }
+
+ bh->b_blocknr=block;
+
+ if(read(fd,bh->b_data,512)>=0)return bh;
+
+ printk("raw_bread: read failed: %s\n",strerror(errno));
+ free(bh->b_data);
+ free(bh);
+
+ return NULL;
+}
+
+struct buffer_head* raw_getblk(struct super_block*sb,int block)
+{ struct buffer_head*bh;
+ int fd=sb->s_dev;
+
+ if(sb->directlist)
+ { block=translate_direct(sb,block);
+ if(!block)return NULL;
+ }
+
+ if(lseek(fd,block*512,SEEK_SET)<0)
+ { printk("raw_getblk: lseek block %d failed: %s\n",block,strerror(errno));
+ return NULL;
+ }
+ bh=malloc(sizeof(struct buffer_head));
+ if(bh==NULL)return NULL;
+
+ bh->b_data=malloc(512);
+ if(bh->b_data==NULL)
+ { free(bh);
+ return NULL;
+ }
+
+ bh->b_blocknr=block;
+ return bh;
+}
+
+void raw_brelse(struct super_block*sb,struct buffer_head*bh)
+{ if(bh==NULL)return;
+ free(bh->b_data);
+ free(bh);
+}
+
+void raw_set_uptodate(struct super_block*sb,struct buffer_head*bh,int v)
+{ /* dummy */
+}
+
+void raw_mark_buffer_dirty(struct super_block*sb,struct buffer_head*bh,int dirty_val)
+{ int fd=sb->s_dev;
+
+ if(dirty_val==0)return;
+ if(bh==NULL)return;
+
+#ifdef DBL_WRITEACCESS
+
+ if(lseek(fd,bh->b_blocknr*512,SEEK_SET)<0)
+ { printk("can't seek block %ld: %s\n",bh->b_blocknr,strerror(errno));
+ return;
+ }
+
+ if(write(fd,bh->b_data,512)<0)
+ printk("writing block %ld failed: %s\n",bh->b_blocknr,strerror(errno));
+
+#else
+ printk("DMSDOS: write access not compiled in, ignored\n");
+#endif
+}
+
+void dblspace_reada(struct super_block*sb, int sector,int count)
+{ /* dummy */
+}
+
+int try_daemon(struct super_block*sb,int clusternr, int length, int method)
+{ return 0;
+}
+
+int host_fat_lookup(struct super_block *sb,int nr)
+{
+ struct buffer_head *bh,*bh2;
+ unsigned char *p_first,*p_last;
+ int first,last,next,b;
+
+ if ((unsigned) (nr-2) >= MSDOS_SB(sb)->clusters)
+ return 0;
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ first = last = nr*2;
+ } else {
+ first = nr*3/2;
+ last = first+1;
+ }
+ b = MSDOS_SB(sb)->fat_start + (first >> SECTOR_BITS);
+ if (!(bh = raw_bread(sb, b))) {
+ printk("DMSDOS: bread in host_fat_access failed\n");
+ return 0;
+ }
+ if ((first >> SECTOR_BITS) == (last >> SECTOR_BITS)) {
+ bh2 = bh;
+ } else {
+ if (!(bh2 = raw_bread(sb, b+1))) {
+ raw_brelse(sb, bh);
+ printk("DMSDOS: 2nd bread in host_fat_lookup failed\n"); return 0;
+ }
+ }
+ if (MSDOS_SB(sb)->fat_bits == 16) {
+ p_first = p_last = NULL; /* GCC needs that stuff */
+ next = cpu_to_le16(((unsigned short *) bh->b_data)[(first &
+ (SECTOR_SIZE-1)) >> 1]);
+ if (next >= 0xfff7) next = -1;
+ }
+ else {
+ p_first = &((unsigned char *) bh->b_data)[first & (SECTOR_SIZE-1)];
+ p_last = &((unsigned char *) bh2->b_data)[(first+1) &
+ (SECTOR_SIZE-1)];
+ if (nr & 1) next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
+ else next = (*p_first+(*p_last << 8)) & 0xfff;
+ if (next >= 0xff7) next = -1;
+ }
+
+ raw_brelse(sb, bh);
+ if (bh != bh2)
+ raw_brelse(sb, bh2);
+ return next;
+}
+
+int dos_cluster2sector(struct super_block * sb,int clusternr)
+{ return (clusternr-2)*MSDOS_SB(sb)->cluster_size+MSDOS_SB(sb)->data_start;
+}
+
+int setup_fragment(struct super_block*sb, int startcluster)
+{ int fragmentzaehler;
+ int clusterzaehler;
+ int akt_cluster;
+ int folge_cluster;
+ int i;
+ unsigned long* directlist;
+ unsigned long* directlen;
+
+ LOG_REST("DMSDOS: setup_fragment\n");
+
+ directlist=malloc(sizeof(unsigned long)*(MAXFRAGMENT+1));
+ if(directlist==NULL)
+ { printk("DMSDOS: out of memory (directlist)\n");
+ return -1;
+ }
+ directlen=malloc(sizeof(unsigned long)*(MAXFRAGMENT+1));
+ if(directlen==NULL)
+ { printk("DMSDOS: out of memory (directlen)\n");
+ free(directlist);
+ return -1;
+ }
+
+ fragmentzaehler=0;
+
+ folge_cluster=startcluster;
+
+ do
+ {
+ clusterzaehler=0;
+ directlist[fragmentzaehler]=folge_cluster;
+ do
+ { akt_cluster=folge_cluster;
+ folge_cluster=host_fat_lookup(sb,akt_cluster);
+ ++clusterzaehler;
+ }
+ while(folge_cluster==akt_cluster+1);
+
+ directlen[fragmentzaehler]=clusterzaehler;
+ LOG_REST("DMSDOS: firstclust=%d anz=%d\n",
+ directlist[fragmentzaehler],
+ directlen[fragmentzaehler]);
+
+ ++fragmentzaehler;
+ }
+ while(folge_cluster>0&&fragmentzaehler<MAXFRAGMENT);
+ if(fragmentzaehler==MAXFRAGMENT&&folge_cluster>0)
+ { /* zu fragmentiert, raus */
+ free(directlist);
+ free(directlen);
+ printk("DMSDOS: CVF too fragmented, not mounted.\n");
+ printk("Increase MAXFRAGMENT in lib_interface.h and recompile.\n");
+ return -1;
+ }
+ printk("DMSDOS: CVF has %d fragment(s)\n",fragmentzaehler);
+
+ /* convert cluster-oriented numbers into sector-oriented ones */
+ for(i=0;i<fragmentzaehler;++i)
+ { /*printk("DMSDOS: umrechnen 1\n");*/
+ directlist[i]=dos_cluster2sector(sb,directlist[i]);
+ /*printk("DMSDOS: umrechnen 2\n");*/
+ directlen[i]*=MSDOS_SB(sb)->cluster_size;
+ /*printk("DMSDOS: umrechnen 3\n");*/
+ }
+
+ /* hang in */
+ sb->directlist=directlist;
+ sb->directlen=directlen;
+
+ return 0;
+}
+
+int setup_translation(struct super_block*sb,char*ext)
+{ int i,j,testvers;
+ struct buffer_head* bh;
+ struct msdos_dir_entry* data;
+ char cvfname[20];
+
+ /* scan the root directory for a CVF */
+
+ for(i=0;i<MSDOS_SB(sb)->dir_entries/MSDOS_DPS;++i)
+ { bh=raw_bread(sb,MSDOS_SB(sb)->dir_start+i);
+ if(bh==NULL)
+ { printk("DMSDOS: unable to read msdos root directory\n");
+ return -1;
+ }
+ data=(struct msdos_dir_entry*) bh->b_data;
+
+ for(j=0;j<MSDOS_DPS;++j)
+ { testvers=0;
+ if(strncmp(data[j].name,"DRVSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"DBLSPACE",8)==0)testvers=1;
+ if(strncmp(data[j].name,"STACVOL ",8)==0)testvers=2;
+ if(testvers)
+ { if( ( data[j].name[8]>='0'&&data[j].name[8]<='9'
+ &&data[j].name[9]>='0'&&data[j].name[9]<='9'
+ &&data[j].name[10]>='0'&&data[j].name[10]<='9'
+ ) | (testvers==2&&strncmp(data[j].name+8,"DSK",3)==0)
+ )
+ { /* it is a CVF */
+ strncpy(cvfname,data[j].name,9-testvers);
+ cvfname[9-testvers]='\0';
+ strcat(cvfname,".");
+ strncat(cvfname,data[j].ext,3);
+ printk("DMSDOS: CVF %s in root directory found.\n",cvfname);
+ if(ext)
+ { if(strncmp(ext,data[j].ext,3)!=0)continue;
+ }
+ if(setup_fragment(sb,data[j].start)==0)
+ { sb->directsize=data[j].size/SECTOR_SIZE;
+ blk_size[0][0]=(data[j].size%1024)?(data[j].size/1024)+1:
+ data[j].size/1024;
+ raw_brelse(sb,bh);
+ printk("DMSDOS: using CVF %s.\n",cvfname);
+ return 0;
+ }
+ }
+ }
+ }
+ raw_brelse(sb,bh);
+ }
+ return -1;
+}
+
+/*okay, first thing is setup super block*/
+/* stolen from fatfs */
+/* Read the super block of an MS-DOS FS. */
+
+struct super_block *read_super(struct super_block *sb,char*ext)
+{
+ struct buffer_head *bh;
+ struct fat_boot_sector *b;
+ int data_sectors,logical_sector_size,sector_mult,fat_clusters=0;
+ int debug=0,error,fat=0;
+ int blksize = 512;
+ int i=-1;
+ int mt=0;
+ char cvf_options[101]="bitfaterrs=nocheck";
+
+ MSDOS_SB(sb)->cvf_format=NULL;
+ MSDOS_SB(sb)->private_data=NULL;
+
+ retry:
+ blksize = 512;
+
+ bh = raw_bread(sb, 0);
+ if (bh == NULL) {
+ raw_brelse (sb, bh);
+ sb->s_dev = 0;
+ printk("FAT bread failed\n");
+ return NULL;
+ }
+ b = (struct fat_boot_sector *) bh->b_data;
+/*
+ * The DOS3 partition size limit is *not* 32M as many people think.
+ * Instead, it is 64K sectors (with the usual sector size being
+ * 512 bytes, leading to a 32M limit).
+ *
+ * DOS 3 partition managers got around this problem by faking a
+ * larger sector size, ie treating multiple physical sectors as
+ * a single logical sector.
+ *
+ * We can accommodate this scheme by adjusting our cluster size,
+ * fat_start, and data_start by an appropriate value.
+ *
+ * (by Drew Eckhardt)
+ */
+
+#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
+ /* don't divide by zero */
+
+ logical_sector_size =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->sector_size));
+ sector_mult = logical_sector_size >> SECTOR_BITS;
+ MSDOS_SB(sb)->cluster_size = b->cluster_size*sector_mult;
+ MSDOS_SB(sb)->fats = b->fats;
+ MSDOS_SB(sb)->fat_start = cpu_to_le16(b->reserved)*sector_mult;
+ MSDOS_SB(sb)->fat_length = cpu_to_le16(b->fat_length)*sector_mult;
+ MSDOS_SB(sb)->dir_start = (cpu_to_le16(b->reserved)+b->fats*cpu_to_le16(
+ b->fat_length))*sector_mult;
+ MSDOS_SB(sb)->dir_entries =
+ cpu_to_le16(get_unaligned((unsigned short *) &b->dir_entries));
+ MSDOS_SB(sb)->data_start = MSDOS_SB(sb)->dir_start+ROUND_TO_MULTIPLE((
+ MSDOS_SB(sb)->dir_entries << MSDOS_DIR_BITS) >> SECTOR_BITS,
+ sector_mult);
+ data_sectors = cpu_to_le16(get_unaligned((unsigned short *) &b->sectors));
+ if (!data_sectors) {
+ data_sectors = cpu_to_le32(b->total_sect);
+ }
+ data_sectors = data_sectors * sector_mult - MSDOS_SB(sb)->data_start;
+ error = !b->cluster_size || !sector_mult;
+ if (!error) {
+ MSDOS_SB(sb)->clusters = b->cluster_size ? data_sectors/
+ b->cluster_size/sector_mult : 0;
+ MSDOS_SB(sb)->fat_bits = fat ? fat : MSDOS_SB(sb)->clusters >
+ MSDOS_FAT12 ? 16 : 12;
+ fat_clusters = MSDOS_SB(sb)->fat_length*SECTOR_SIZE*8/
+ MSDOS_SB(sb)->fat_bits;
+ /* this doesn't compile. I don't understand it either...
+ error = !MSDOS_SB(sb)->fats || (MSDOS_SB(sb)->dir_entries &
+ (MSDOS_DPS-1)) || MSDOS_SB(sb)->clusters+2 > fat_clusters+
+ MSDOS_MAX_EXTRA || (logical_sector_size & (SECTOR_SIZE-1))
+ || !b->secs_track || !b->heads;
+ */
+ }
+ raw_brelse(sb, bh);
+
+ if(error)goto c_err;
+
+ /*
+ This must be done after the brelse because the bh is a dummy
+ allocated by fat_bread (see buffer.c)
+ */
+ sb->s_blocksize = blksize; /* Using this small block size solves */
+ /* the misfit with buffer cache and cluster */
+ /* because clusters (DOS) are often aligned */
+ /* on odd sectors. */
+ sb->s_blocksize_bits = blksize == 512 ? 9 : 10;
+ i=0;
+
+#ifdef DMSDOS_CONFIG_DBL
+ if(i==0)
+ { i=detect_dblspace(sb);
+ if(i>0){mt++;i=mount_dblspace(sb,cvf_options);}
+ }
+#endif
+#ifdef DMSDOS_CONFIG_STAC
+ if(i==0)
+ { i=detect_stacker(sb);
+ if(i>0){mt++;i=mount_stacker(sb,cvf_options);}
+ }
+#endif
+ if(mt==0)
+ { /* looks like a real msdos filesystem */
+ printk("DMSDOS: trying to find CVF inside host MSDOS filesystem...\n");
+ i=setup_translation(sb,ext);
+ ++mt;
+ if(i==0)goto retry;
+ }
+ error=i;
+ c_err:
+ if (error || debug) {
+ /* The MSDOS_CAN_BMAP is obsolete, but left just to remember */
+ printk("MS-DOS FS Rel. 12 (hacked for libdmsdos), FAT %d\n",
+ MSDOS_SB(sb)->fat_bits);
+ printk("[me=0x%x,cs=%d,#f=%d,fs=%d,fl=%d,ds=%d,de=%d,data=%d,"
+ "se=%d,ts=%ld,ls=%d]\n",b->media,MSDOS_SB(sb)->cluster_size,
+ MSDOS_SB(sb)->fats,MSDOS_SB(sb)->fat_start,MSDOS_SB(sb)->fat_length,
+ MSDOS_SB(sb)->dir_start,MSDOS_SB(sb)->dir_entries,
+ MSDOS_SB(sb)->data_start,
+ cpu_to_le16(*(unsigned short *) &b->sectors),
+ (unsigned long)b->total_sect,logical_sector_size);
+ printk ("Transaction block size = %d\n",blksize);
+ }
+ if(!error&&i<0) if (MSDOS_SB(sb)->clusters+2 > fat_clusters)
+ MSDOS_SB(sb)->clusters = fat_clusters-2;
+ if (error) {
+ printk("Can't find a valid MSDOS CVF filesystem\n");
+ if(MSDOS_SB(sb)->private_data)kfree(MSDOS_SB(sb)->private_data);
+ MSDOS_SB(sb)->private_data=NULL;
+ return NULL;
+ }
+ sb->s_magic = MSDOS_SUPER_MAGIC;
+ /* set up enough so that it can read an inode */
+ MSDOS_SB(sb)->free_clusters = -1; /* don't know yet */
+ MSDOS_SB(sb)->fat_wait = NULL;
+ MSDOS_SB(sb)->fat_lock = 0;
+ MSDOS_SB(sb)->prev_free = 0;
+ return sb;
+}
+
+void init_daemon(void)
+{ /*dummy*/
+}
+
+void exit_daemon(void)
+{ /*dummy*/
+}
+
+void clear_list_dev(struct super_block*sb)
+{ /*dummy*/
+}
+
+void free_ccache_dev(struct super_block*sb)
+{ /*dummy*/
+}
+
+void remove_from_daemon_list(struct super_block*sb,int clusternr)
+{ /* dummy */
+}
+
+static int _wascalled=0;
+void do_lib_init(void)
+{ int i;
+
+ if(_wascalled)return;
+ _wascalled=1;
+
+ /* first call of DMSDOS library, initialising variables */
+
+ printk("DMSDOS library version %d.%d.%d" DMSDOS_VLT
+ " compiled " __DATE__ " " __TIME__ " with options:"
+#ifndef DBL_WRITEACCESS
+ " read-only"
+#else
+ " read-write"
+#endif
+#ifdef DMSDOS_CONFIG_DBLSP_DRVSP
+ ", doublespace/drivespace(<3)"
+#endif
+#ifdef DMSDOS_CONFIG_DRVSP3
+ ", drivespace 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC3
+ ", stacker 3"
+#endif
+#ifdef DMSDOS_CONFIG_STAC4
+ ", stacker 4"
+#endif
+ "\n",
+ DMSDOS_MAJOR,DMSDOS_MINOR,DMSDOS_ACT_REL);
+
+ for(i=0;i<MDFATCACHESIZE;++i)
+ { mdfat[i].a_time=0;
+ mdfat[i].a_acc=0;
+ mdfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<DFATCACHESIZE;++i)
+ { dfat[i].a_time=0;
+ dfat[i].a_acc=0;
+ dfat[i].a_buffer=NULL;
+ }
+ for(i=0;i<BITFATCACHESIZE;++i)
+ { bitfat[i].a_time=0;
+ bitfat[i].a_acc=0;
+ bitfat[i].a_buffer=NULL;
+ }
+}
+
+struct super_block* open_cvf(char*filename,int rwflag)
+{ struct super_block *sb;
+ int fd;
+ long int s;
+ char*ext=NULL;
+
+ do_lib_init();
+
+ ext=strrchr(filename,':');
+ if(ext)
+ { if(strlen(ext)==4)
+ { *ext='\0';
+ ++ext;
+ }
+ else
+ ext=NULL;
+ }
+
+ reopen:
+#ifndef USE_SOPEN
+ fd=open(filename,rwflag?O_RDWR:O_RDONLY);
+ if(fd<0)
+ { printk("unable to open CVF read-write: %s\n",strerror(errno));
+ if(rwflag==0)return NULL;
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ goto reopen;
+ }
+
+#ifdef USE_FLOCK
+ if(rwflag)
+ { if(flock(fd,LOCK_EX|LOCK_NB))
+ { printk("unable to lock CVF exclusively: %s",strerror(errno));
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ close(fd);
+ goto reopen;
+ }
+ }
+ else
+ { if(flock(fd,LOCK_SH|LOCK_NB))
+ { printk("unable to lock CVF with shared flag: %s",strerror(errno));
+ printk("probably some other process has opened the CVF read-write.\n");
+ close(fd);
+ return NULL;
+ }
+ }
+#endif /* USE_FLOCK */
+#else
+ /* open with win32 locking */
+ fd=sopen(filename,rwflag?O_RDWR:O_RDONLY,rwflag?SH_DENYRW:SH_DENYWR);
+ if(fd<0)
+ { printk("unable to open CVF read-write: %s\n",strerror(errno));
+ if(rwflag==0)return NULL;
+ printk("trying again in read-only mode\n");
+ rwflag=0;
+ goto reopen;
+ }
+#endif
+
+ s=lseek(fd,0,SEEK_END);
+ blk_size[0][0]=(s%1024)?(s/1024)+1:s/1024;
+ sb=malloc(sizeof(struct super_block));
+ if(sb==NULL)
+ { printk("malloc failed\n");
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ return NULL;
+ }
+
+ sb->s_dev=fd;
+ sb->s_flags=0;
+ if(rwflag==0)sb->s_flags|=MS_RDONLY;
+ sb->directlist=NULL;
+ sb->directlen=NULL;
+
+ if(read_super(sb,ext)==NULL)
+ {
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ free(sb);
+ return NULL;
+ }
+
+ return sb;
+}
+
+void close_cvf(struct super_block*sb)
+{ int fd=sb->s_dev;
+
+ unmount_dblspace(sb);
+#ifdef USE_FLOCK
+ flock(fd,LOCK_UN);
+#endif
+ close(fd);
+ if(sb->directlist)free(sb->directlist);
+ if(sb->directlen)free(sb->directlen);
+ free(sb);
+}
diff --git a/src/lib_interface.h b/src/lib_interface.h
new file mode 100644
index 0000000..551447b
--- /dev/null
+++ b/src/lib_interface.h
@@ -0,0 +1,189 @@
+/*
+lib_interface.h
+
+DMSDOS library: headers for interface functions, hacks, dummies, and fakes.
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+/* These are copied from the kernel include files in order to avoid
+ including those files. They are not 100% identical to the kernel types.
+ Most of them needn't be the same as in the kernel.
+ This has been done for libc6 support.
+*/
+
+/* machine and system dependent hacks */
+
+/* Linux section -- no problems here... :)) */
+#ifdef __linux__
+/* this defines machine-dependent __u8, __s8 etc. types */
+#include<asm/types.h>
+/* this defines get_unaligned and put_unaligned */
+#include<asm/unaligned.h>
+/* this defines cpu_to_le16 etc. in 2.1 kernels - a kind of nop for 2.0 */
+#include<asm/byteorder.h>
+
+/* Other systems usually do not have the asm include files */
+#else
+/* emulate asm/types.h */
+typedef unsigned char __u8;
+typedef signed char __s8;
+typedef unsigned short int __u16;
+typedef signed short int __s16;
+typedef unsigned int __u32;
+typedef signed int __s32;
+/* emulate asm/unaligned.h */
+/* edit these lines if your system cannot do unaligned access */
+#define get_unaligned(ptr) (*(ptr))
+#define put_unaligned(val, ptr) ((void)( *(ptr) = (val) ))
+/* emulate asm/byteorder.h */
+/* edit these lines if your system is non-linux and big endian */
+/* the examples are commented out; they are valid for a little endian cpu */
+/* #define cpu_to_le16(v) (v) */
+/* #define cpu_to_be16(v) ( (((v)&0xff)<<8) | (((v)&0xff00)>>8) ) */
+/* #define cpu_to_le32(v) (v) */
+/* hack: sometimes NULL is missing */
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+#endif
+
+int printk(const char *fmt, ...);
+void panic(const char * fmt, ...);
+
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define MSDOS_DPB (MSDOS_DPS) /* dir entries per block */
+#define MSDOS_DPB_BITS 4 /* log2(MSDOS_DPB) */
+#define MSDOS_DPS (SECTOR_SIZE/sizeof(struct msdos_dir_entry))
+#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */
+#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */
+
+#define MSDOS_SUPER_MAGIC 0x4d44 /* MD */
+
+#define MSDOS_MAX_EXTRA 3 /* tolerate up to that number of clusters which are
+ inaccessible because the FAT is too short */
+
+#define KERN_EMERG "<0>" /* system is unusable */
+#define KERN_ALERT "<1>" /* action must be taken immediately */
+#define KERN_CRIT "<2>" /* critical conditions */
+#define KERN_ERR "<3>" /* error conditions */
+#define KERN_WARNING "<4>" /* warning conditions */
+#define KERN_NOTICE "<5>" /* normal but significant condition */
+#define KERN_INFO "<6>" /* informational */
+#define KERN_DEBUG "<7>" /* debug-level messages */
+
+struct buffer_head {
+ unsigned long b_blocknr; /* block number */
+ char * b_data; /* pointer to data block */
+};
+
+#define MS_RDONLY 1 /* Mount read-only */
+#define MSDOS_SB(s) (&((s)->u.msdos_sb))
+
+struct msdos_dir_entry {
+ __s8 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) */
+};
+
+struct msdos_sb_info {
+ unsigned short cluster_size; /* sectors/cluster */
+ unsigned char fats,fat_bits; /* number of FATs, FAT bits (12 or 16) */
+ unsigned short fat_start,fat_length; /* FAT start & length (sec.) */
+ unsigned short dir_start,dir_entries; /* root dir start & entries */
+ unsigned short data_start; /* first data sector */
+ unsigned long clusters; /* number of clusters */
+ unsigned long root_cluster; /* first cluster of the root directory */
+ unsigned long fsinfo_offset; /* FAT32 fsinfo offset from start of disk */
+ void *fat_wait;
+ int fat_lock;
+ int prev_free; /* previously returned free cluster number */
+ int free_clusters; /* -1 if undefined */
+ /*struct fat_mount_options options;*/
+ struct nls_table *nls_disk; /* Codepage used on disk */
+ struct nls_table *nls_io; /* Charset used for input and display */
+ struct cvf_format* cvf_format;
+ void* private_data;
+};
+
+struct super_block {
+ int s_dev;
+ unsigned long s_blocksize;
+ unsigned char s_blocksize_bits;
+ unsigned long s_flags;
+ unsigned long s_magic;
+ unsigned long* directlist;
+ unsigned long* directlen;
+ unsigned long directsize;
+ union {
+ struct msdos_sb_info msdos_sb;
+ } u;
+
+};
+
+struct fat_boot_sector {
+ __s8 ignored[3]; /* Boot strap short or near jump */
+ __s8 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 */
+};
+
+#define MALLOC malloc
+#define FREE free
+#define kmalloc(x,y) malloc(x)
+#define kfree free
+#define CURRENT_TIME time(NULL)
+#define vmalloc malloc
+#define vfree free
+
+#define MAXFRAGMENT 300
diff --git a/src/listmsg.sh b/src/listmsg.sh
new file mode 100644
index 0000000..cb5d20d
--- /dev/null
+++ b/src/listmsg.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# This script tries to find all kernel messages in the dmsdos sources.
+# It's not 100% and may print some garbage, but better than nothing.
+
+# Usage:
+# LIST_MESSAGES list all (incl. debug) messages
+# LIST_MESSAGES pattern list only messages with pattern in it
+# LIST_MESSAGES -LOG list normal messages (without debug messages)
+
+if [ "$1" != "" ];
+then
+ if [ "$1" != "-LOG" ];
+ then
+ ( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | grep $1 | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+ else
+ ( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | grep -v LOG | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+ fi
+else
+( cat dblspace*.c *.c-2.0.33 *.c-2.1.80 dstacker*.c | awk -f remove_comments.awk | grep DMSDOS | cut -f2 -d\" -s | cut -f1 -d\\ | sort -b -d -f | uniq ) 2>/dev/null
+fi
diff --git a/src/mcdmsdos.c b/src/mcdmsdos.c
new file mode 100644
index 0000000..9b02915
--- /dev/null
+++ b/src/mcdmsdos.c
@@ -0,0 +1,409 @@
+/*
+
+mcdmsdos.c
+
+DMSDOS: external filesystem interface for Midnight Commander
+
+******************************************************************************
+DMSDOS (compressed MSDOS filesystem support) for Linux
+written 1995-1998 by Frank Gockel and Pavel Pisa
+
+ (C) Copyright 1995-1998 by Frank Gockel
+ (C) Copyright 1996-1998 by Pavel Pisa
+
+Some code of dmsdos has been copied from the msdos filesystem
+so there are the following additional copyrights:
+
+ (C) Copyright 1992,1993 by Werner Almesberger (msdos filesystem)
+ (C) Copyright 1994,1995 by Jacques Gelinas (mmap code)
+ (C) Copyright 1992-1995 by Linus Torvalds
+
+DMSDOS was inspired by the THS filesystem (a simple doublespace
+DS-0-2 compressed read-only filesystem) written 1994 by Thomas Scheuermann.
+
+The DMSDOS code is distributed under the Gnu General Public Licence.
+See file COPYING for details.
+******************************************************************************
+
+*/
+
+#include<stdio.h>
+#include<stdlib.h>
+#include<string.h>
+#include<ctype.h>
+
+#include"dmsdos.h"
+#include"lib_interface.h"
+
+#define M_LIST 1
+#define M_OUT 2
+#define M_IN 3
+
+/*this is not good - but currently we have only one CVF open at a time*/
+struct super_block*sb;
+Dblsb*dblsb;
+
+int scan(char*text)
+{ int v=0;
+ if(strncmp(text,"0x",2)==0||strncmp(text,"0X",2)==0)
+ sscanf(text+2,"%x",&v);
+ else
+ sscanf(text,"%d",&v);
+ return v;
+}
+
+unsigned char* get_root_dir(void)
+{ unsigned char* data;
+ struct buffer_head*bh;
+ int i;
+
+ data=malloc(dblsb->s_rootdirentries*32);
+ if(data==NULL)return NULL;
+
+ for(i=0;i<dblsb->s_rootdirentries*32/512;++i)
+ { bh=raw_bread(sb,dblsb->s_rootdir+i);
+ if(bh==NULL){free(data);return NULL;}
+ memcpy(data+i*512,bh->b_data,512);
+ raw_brelse(sb,bh);
+ }
+ return data;
+}
+
+int copy_cluster_out(int nr, int len, FILE*f)
+{ unsigned char*data;
+ int i,j;
+
+ if(nr==0)
+ { data=get_root_dir();
+ if(data==NULL)return -1;
+ i=dblsb->s_rootdirentries*32;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data==NULL)return -1;
+ i=dmsdos_read_cluster(sb,data,nr);
+ if(i<0){free(data);return -1;}
+ }
+ if(len<=0||len>i)len=i;
+
+ for(j=0;j<len;++j)fputc(data[j],f);
+
+ free(data);
+ return ferror(f);
+}
+
+int handle_dir_chain(int start,int rek,char*prefix);
+
+int display_dir_cluster(int nr, int rek, char*prefix)
+{ unsigned char*data;
+ int i,j;
+
+ /*printf("display_dir_cluster called with nr=%d rek=%d prefix=%s\n",
+ nr,rek,prefix);*/
+
+ if(nr==0)
+ { data=get_root_dir();
+ if(data==NULL)return -1;
+ i=dblsb->s_rootdirentries*32;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data==NULL)return -1;
+ i=dmsdos_read_cluster(sb,data,nr);
+ if(i<0){free(data);return -1;}
+ }
+
+ for(j=0;j<dblsb->s_sectperclust*512;j+=32)
+ { unsigned char*pp;
+ unsigned int x;
+ char filename[15]="";
+ int nstart;
+ long size;
+ char datestr[16][4]={"?00","Jan","Feb","Mar","Apr","May","Jun",
+ "Jul","Aug","Sep","Oct","Nov","Dec",
+ "?13","?14","?15"};
+ if(data[j]==0)break;
+ if(data[j]==0xe5)continue;
+ if(data[j+11]&8)continue;
+ if(data[j]=='.')continue;
+
+ for(i=0;i<11;++i)
+ { if(i==8&&data[j+i]!=' ')strcat(filename,".");
+ if(data[j+i]!=' ')strncat(filename,&(data[j+i]),1);
+ }
+ for(i=0;i<strlen(filename);++i)filename[i]=tolower(filename[i]);
+
+ if(data[j+11]&16)printf("dr");else printf("-r");
+ if(data[j+11]&1)printf("-");else printf("w");
+ printf("xr-xr-x 1 0 0"); /* bogus values :) */
+
+ /*
+ printf(" ");
+ if(data[j+11]&1)printf("R");else printf(" ");
+ if(data[j+11]&2)printf("H");else printf(" ");
+ if(data[j+11]&4)printf("S");else printf(" ");
+ if(data[j+11]&8)printf("V");else printf(" ");
+ if(data[j+11]&16)printf("D");else printf(" ");
+ if(data[j+11]&32)printf("A");else printf(" ");
+ if(data[j+11]&64)printf("?");else printf(" ");
+ if(data[j+11]&128)printf("?");else printf(" ");
+ */
+
+ pp=&(data[j+28]);
+ size=CHL(pp);
+ printf(" %7lu",size);
+
+ pp=&(data[j+24]);
+ x=CHS(pp);
+ /*printf(" %02d.%02d.%02d",x&31,(x>>5)&15,(x>>9)+80);*/
+ printf(" %s",datestr[(x>>5)&15]);
+ printf(" %02d",x&31);
+ printf(" %04d",(x>>9)+1980); /* y2k compliant :) */
+
+ pp=&(data[j+22]);
+ x=CHS(pp);
+ /*printf(" %02d:%02d:%02d",x>>11,(x>>5)&63,(x&31)<<1);*/
+ printf(" %02d:%02d",x>>11,(x>>5)&63);
+
+ pp=&(data[j+26]);
+ nstart=CHS(pp);
+ /*printf(" %5d",nstart);*/
+
+ printf(" %s%s\n",prefix,filename);
+
+ if((data[j+11]&16)!=0&&rek!=0&&filename[0]!='.')
+ { char *nprefix;
+ nprefix=malloc(strlen(prefix)+20);
+ if(nprefix==NULL)
+ { fprintf(stderr,"out of memory\n");
+ exit(3);
+ }
+ strcpy(nprefix,prefix);
+ strcat(nprefix,filename);
+ strcat(nprefix,"/");
+ handle_dir_chain(nstart,rek,nprefix);
+ free(nprefix);
+ }
+
+ }
+
+ free(data);
+ return 0;
+}
+
+int handle_dir_chain(int start,int rek,char*prefix)
+{ int i,next;
+
+ if(start==0)return display_dir_cluster(0,rek,prefix);
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ i=display_dir_cluster(start,rek,prefix);
+ if(i<0)return i;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { return -1;
+ }
+
+ return 0;
+}
+
+int handle_file_chain(int start, int len, FILE*f)
+{ int i,next;
+
+ if(start==0)return -1; /* never a file :) */
+ if(start==1||start<0||start>dblsb->s_max_cluster)return -1;
+
+ do
+ {
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ i=copy_cluster_out(start,len,f);
+ if(i<0)return i;
+ len-=dblsb->s_sectperclust*512;
+ if(len<=0)break;
+ start=next;
+ }
+ while(next>1&&next<=dblsb->s_max_cluster);
+
+ if(next>=0)
+ { return -1;
+ }
+
+ return 0;
+}
+
+int scan_dir(char*entry,int start,int*len)
+{ char buf[]=" ";
+ /*12345678EXT*/
+ int i;
+ int size;
+ unsigned char*data;
+ int next;
+
+ if(strcmp(entry,".")==0)return start;
+ else if(strcmp(entry,"..")==0)strncpy(buf,"..",2);
+ else if(*entry=='.') return -1;
+ else
+ for(i=0;i<11;++i)
+ { if(*entry=='.'&&i<=7){i=7;++entry;continue;}
+ if(*entry=='.'&&i==8){i=7;++entry;continue;}
+ if(*entry=='.')break;
+ if(*entry=='\0')break;
+ buf[i]=toupper(*entry);
+ ++entry;
+ }
+
+ do
+ {
+ /*printf("scan_dir: searching for %s in %d\n",buf,start);*/
+
+ if(start==0)
+ { data=get_root_dir();
+ size=dblsb->s_rootdirentries;
+ next=-1;
+ }
+ else
+ { data=malloc(dblsb->s_sectperclust*512);
+ if(data!=NULL)
+ { i=dmsdos_read_cluster(sb,data,start);
+ if(i<0){free(data);data=NULL;}
+ size=i/32;
+ next=dbl_fat_nextcluster(sb,start,NULL);
+ if(next==0)
+ fprintf(stderr,"warning: cluster %d is marked as unused in FAT\n",
+ next);
+ }
+ }
+ if(data==NULL)return -1;
+
+ for(i=0;i<size;++i)
+ { if(strncmp(&(data[i*32]),buf,11)==0)
+ { unsigned char*pp;
+ int cluster;
+
+ pp=&(data[i*32+26]);
+ cluster=CHS(pp);
+ pp=&(data[i*32+28]);
+ if(len)*len=CHL(pp);
+ free(data);
+ return cluster;
+ }
+ }
+
+ free(data);
+ start=next;
+ }
+ while(next>0&&next<=dblsb->s_max_cluster);
+ return -1;
+}
+
+int scan_path(char*path,int start, int*len)
+{ int i;
+ char*p;
+
+ for(p=strtok(path,"/");p;p=strtok(NULL,"/"))
+ { i=scan_dir(p,start,len);
+ if(i<0)
+ { fprintf(stderr,"path component %s not found\n",p);
+ return -1;
+ }
+ start=i;
+ }
+
+ return start;
+}
+
+int main(int argc, char*argv[])
+{ int mode=0;
+ int cluster;
+ int i;
+ char*p;
+ int len;
+ FILE*f;
+
+ fprintf(stderr,"mcdmsdos version 0.2.0 (for libdmsdos 0.9.x and newer)\n");
+ if(argc<2)
+ { fprintf(stderr,"\nusage: mcdmsdos <mc-extfs-command> ...\n");
+ fprintf(stderr,"where <mc-extfs-command> can be:\n");
+ fprintf(stderr," list <CVF>\n");
+ fprintf(stderr," copyout <CVF> <path/name_in_CVF> <outfile>\n");
+ fprintf(stderr," copyin <CVF> <path/name_in_CVF> <infile> [*]\n");
+ fprintf(stderr," [*] currently not implemented\n");
+ return 1;
+ }
+
+ /* check syntax */
+ if(strcmp(argv[1],"list")==0)
+ { mode=M_LIST;
+ if(argc!=3)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ }
+ else if(strcmp(argv[1],"copyout")==0)
+ { mode=M_OUT;
+ if(argc!=5)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ }
+ else if(strcmp(argv[1],"copyin")==0)
+ { mode=M_IN;
+ if(argc!=5)
+ { fprintf(stderr,"wrong number of arguments\n");
+ return 1;
+ }
+ fprintf(stderr,"copyin command is not implemented\n");
+ return -2;
+ }
+ else
+ { fprintf(stderr,"unknown command\n");
+ return -1;
+ }
+
+ sb=open_cvf(argv[2],0/*read-only*/);
+ if(sb==NULL)
+ { printf("open CVF %s failed\n",argv[1]);
+ return 2;
+ }
+ dblsb=MSDOS_SB(sb)->private_data;
+
+
+ if(mode==M_LIST)
+ { i=handle_dir_chain(0,1,"");
+ }
+
+ else if(mode==M_OUT)
+ { p=malloc(strlen(argv[3])+1);
+ strcpy(p,argv[3]);
+#ifdef _WIN32
+ /* convert to Unix style path */
+ for(i=0;i<strlen(p);++i){if(p[i]=='\\')p[i]='/';}
+#endif
+ if(*p=='/')++p;
+ cluster=scan_path(p,0,&len);
+ if(cluster<0)
+ { fprintf(stderr,"%s not found\n",argv[3]);
+ return 1;
+ }
+
+ f=fopen(argv[4],"wb");
+ if(f==NULL)
+ { perror("open write failed");
+ i=-1;
+ }
+ else
+ { i=handle_file_chain(cluster,len,f);
+ fclose(f);
+ }
+ }
+
+ close_cvf(sb);
+
+ return i;
+}
diff --git a/src/msdos.fsck-wrapper b/src/msdos.fsck-wrapper
new file mode 100644
index 0000000..cedf1bd
--- /dev/null
+++ b/src/msdos.fsck-wrapper
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+# This is an example shell script that can be used as a wrapper for a
+# msdos.fsck (called by the generic fsck frontend).
+#
+# WARNING: This script is an older implementation. See msdos.fsck-wrapper2
+# for a newer and cleaner one.
+#
+# WARNING: This script needs write access to /tmp which is probably not
+# possible on bootup. Better use msdos.fsck-wrapper2 instead.
+#
+# The script accepts a standard fsck command line and decides upon the
+# the raw filesystem data whether it is a CVF or not. If it is a CVF then
+# dmsdosfsck is invoked.
+#
+# The case when it is not a CVF but an uncompressed msdos partition is a bit
+# more complex. First, dosfsck is invoked to check that dos partition. Then
+# the script tries to mount that partition and scans its root directory for
+# CVFs. If there are CVFs it tries to check them, too, by calling dmsdosfsck
+# on them. After that, the msdos partition is unmounted again in order to
+# restore the previous state. If the -r option is present in the fsck
+# command line, some questions are asked.
+#
+# Note that this script needs a helper program that finds out whether a
+# file is a CVF or not. If you have added the file(1) magics for CVFs to
+# /etc/magic (see the dmsdos installation instructions) you can use a
+# combination of file and grep (for example) for that purpose. I think this
+# is a standard way. If you still prefer the older method (by calling the
+# helper program cvftest) just compile cvftest ('make cvftest') and change
+# some lines below (I've commented out the two cvftest calls and placed a
+# file command in the next line).
+
+###########################################################################
+
+# where to find the different filesystem checker utilities
+DMSDOSFSCK="dmsdosfsck"
+DOSFSCK="dosfsck"
+
+ARGS="$@"
+FILE=""
+ASK=n
+
+while [ "$1" != "" ];
+do
+ case "$1" in
+ -u) shift
+ shift ;;
+ -d) shift
+ shift ;;
+ -r) ASK=y
+ shift ;;
+ -*) shift ;;
+ *) FILE="$1"
+ shift ;;
+ esac;
+done
+
+#echo "ARGS=$ARGS"
+#echo "FILE=$FILE"
+
+#if cvftest $FILE ;
+if [ ! -z "`file $FILE | grep CVF`" ];
+then
+ echo "CVF detected, calling dmsdosfsck..."
+ $DMSDOSFSCK $ARGS
+ CODE="$?"
+else
+ echo "no CVF found, calling dosfsck..."
+ $DOSFSCK $ARGS
+ CODE="$?"
+ if [ "$CODE" != "0" ];
+ then
+ exit $CODE
+ fi
+ if [ $ASK = y ];
+ then
+ echo -n "search $FILE for CVFs in it and check them, too?"
+ read ANS JUNK
+ if [ "$ANS" != "y" -a "$ANS" != "Y" ];
+ then
+ exit 0
+ fi
+ fi
+ mkdir /tmp/fsckwrap.$$
+ if [ "$?" != "0" ];
+ then
+ echo "need write access to /tmp for automatic CVF check (skipped)"
+ exit 0
+ fi
+ chmod 700 /tmp/fsckwrap.$$
+ mount -t msdos $FILE /tmp/fsckwrap.$$
+ if [ "$?" != "0" ];
+ then
+ echo "cannot search $FILE for CVFs in it (skipped)"
+ exit 0
+ fi
+
+ CODE="0"
+ FIRST=y
+ for I in /tmp/fsckwrap.$$/dblspace.0?? /tmp/fsckwrap.$$/drvspace.0?? /tmp/fsckwrap.$$/stacvol.*
+ do
+ if [ -f $I ];
+ then
+ #if cvftest $I ;
+ if [ ! -z "`file $I | grep CVF`" ];
+ then
+ if [ $FIRST = y ];
+ then
+ echo "$FILE contains CVFs"
+ FIRST=n
+ fi
+ echo -n "checking CVF "
+ basename $I
+ $DMSDOSFSCK $ARGS $I
+ if [ "$?" != "0" ];
+ then
+ CODE="$?"
+ fi
+ fi
+ fi
+ done
+ umount /tmp/fsckwrap.$$
+ rmdir /tmp/fsckwrap.$$
+fi
+
+exit $CODE
diff --git a/src/msdos.fsck-wrapper2 b/src/msdos.fsck-wrapper2
new file mode 100644
index 0000000..1f79f55
--- /dev/null
+++ b/src/msdos.fsck-wrapper2
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+# This is an example shell script that can be used as a wrapper for a
+# msdos.fsck (called by the generic fsck frontend).
+#
+# This script is the second version. It uses a new feature of libdmsdos,
+# the built-in direct translation that can access a CVF even if the host
+# filesystem is not mounted. This is now the preferred way.
+#
+# The script accepts a standard fsck command line and decides upon the
+# the raw filesystem data whether it is a CVF or not. If it is a CVF then
+# dmsdosfsck is invoked.
+#
+# The case when it is not a CVF but an uncompressed msdos partition is a bit
+# more complex. First, dosfsck is invoked to check that dos partition. Then
+# the script scans its root directory for CVFs. If there are CVFs it tries
+# to check them, too, by calling dmsdosfsck on them. If the -r option is
+# present in the fsck command line, some questions are asked.
+#
+# Note that this script needs a helper program that finds out whether a
+# file is a CVF or not. If you have added the file(1) magics for CVFs to
+# /etc/magic (see the dmsdos installation instructions) you can use a
+# combination of file and grep (for example) for that purpose. I think this
+# is a standard way. If you still prefer the older method (by calling the
+# helper program cvftest) just compile cvftest ('make cvftest') and change
+# one lines below (I've commented out the cvftest call and placed a
+# file command in the next line).
+
+###########################################################################
+
+# where to find the different filesystem checker utilities
+DMSDOSFSCK="dmsdosfsck"
+DOSFSCK="dosfsck"
+CVFLIST="cvflist"
+
+ARGS="$@"
+FILE=""
+ASK=n
+
+while [ "$1" != "" ];
+do
+ case "$1" in
+ -u) shift
+ shift ;;
+ -d) shift
+ shift ;;
+ -r) ASK=y
+ shift ;;
+ -*) shift ;;
+ *) FILE="$1"
+ shift ;;
+ esac;
+done
+
+#echo "ARGS=$ARGS"
+#echo "FILE=$FILE"
+
+#if cvftest $FILE ;
+if [ ! -z "`file $FILE | grep CVF`" ];
+then
+ echo "CVF detected, calling dmsdosfsck ..."
+ $DMSDOSFSCK $ARGS
+ CODE="$?"
+else
+ echo "no CVF found, calling dosfsck ..."
+ $DOSFSCK $ARGS
+ CODE="$?"
+ if [ "$CODE" != "0" ];
+ then
+ exit $CODE
+ fi
+ if [ $ASK = y ];
+ then
+ echo -n "search $FILE for CVFs in it and check them, too?"
+ read ANS JUNK
+ if [ "$ANS" != "y" -a "$ANS" != "Y" ];
+ then
+ exit 0
+ fi
+ fi
+ CVFS=`$CVFLIST $FILE`
+ if [ -z "$CVFS" ];
+ then
+ echo "no CVFs found."
+ exit 0
+ fi
+
+ CODE="0"
+ for I in $CVFS
+ do
+ I=`echo $I | cut -f2 -d.`
+ echo "checking CVF with ext $I ..."
+ $DMSDOSFSCK $ARGS $FILE:$I
+ if [ "$?" != "0" ];
+ then
+ CODE="$?"
+ fi
+ done
+fi
+
+exit $CODE
diff --git a/src/my_break.h b/src/my_break.h
new file mode 100644
index 0000000..df2bac0
--- /dev/null
+++ b/src/my_break.h
@@ -0,0 +1,30 @@
+#ifndef _MY_BREAK_H
+#define _MY_BREAK_H
+
+#include <linux/sched.h>
+
+#define BREAK_NOW my_break_now(__PRETTY_FUNCTION__);
+
+#define get_esp() ({int a_sp; __asm__ ("mov %%esp,%0":"=r"(a_sp)); a_sp;})
+
+struct wait_queue * my_break_wait=NULL;
+
+static void my_break_cont(void)
+{ wake_up(&my_break_wait);
+}
+
+static void my_break_now(char * s)
+{ unsigned call_pc;
+ unsigned call_sp;
+ call_pc=(typeof(call_pc))__builtin_return_address(0);
+ call_sp=(typeof(call_sp))get_esp();
+ printk ("BREAK : procces %i (current=0x%x) stopped at 0x%x in %s\n",
+ current->pid,(unsigned)current,call_pc,s);
+ printk ("BREAK : sp = 0x%x\n",call_sp);
+ printk ("BREAK : for continue call *0x%x()\n",
+ (unsigned)&my_break_cont);
+ sleep_on(&my_break_wait);
+ printk ("BREAK : continuing\n");
+}
+
+#endif /* _MY_BREAK_H */
diff --git a/src/prepare_dos_8.3 b/src/prepare_dos_8.3
new file mode 100644
index 0000000..f63e3a3
--- /dev/null
+++ b/src/prepare_dos_8.3
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+TARGET_DIR=/tmp/dmsdos/dos8.3/src
+mkdir -p $TARGET_DIR
+
+if [ -f dmsdos-config.h ];
+then
+ sh putdos dmsdos-config.h $TARGET_DIR
+else
+ sh putdos dmsdos-config.h.default $TARGET_DIR
+fi
+sh putdos dmsdos.h $TARGET_DIR
+sh putdos lib_interface.h $TARGET_DIR
+sh putdos lib_interface.c $TARGET_DIR
+sh putdos dblspace_interface.c $TARGET_DIR
+sh putdos dblspace_dec.c $TARGET_DIR
+sh putdos dblspace_compr.c $TARGET_DIR
+sh putdos dblspace_methsq.c $TARGET_DIR
+sh putdos dblspace_alloc.c $TARGET_DIR
+sh putdos dblspace_chk.c $TARGET_DIR
+sh putdos dblspace_tables.c $TARGET_DIR
+sh putdos dstacker_compr.c $TARGET_DIR
+sh putdos dstacker_dec.c $TARGET_DIR
+sh putdos dstacker_alloc.c $TARGET_DIR
+sh putdos dcread.c $TARGET_DIR
+sh putdos mcdmsdos.c $TARGET_DIR
+sh putdos dmsdosfsck.c $TARGET_DIR
diff --git a/src/putdos b/src/putdos
new file mode 100644
index 0000000..2a8f2cb
--- /dev/null
+++ b/src/putdos
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+# create temporary command
+
+FILTER=`sh putdos_helper < putdos.cfg`
+
+#echo "FILTER=$FILTER"
+
+echo "#!/bin/sh" > filter.sh
+echo "$FILTER" >> filter.sh
+
+RFN=`echo $1 | sh filter.sh`
+
+echo "translating $1 to $2/$RFN ..."
+
+cat $1 | sh filter.sh > $2/$RFN
+
+rm filter.sh
diff --git a/src/putdos.cfg b/src/putdos.cfg
new file mode 100644
index 0000000..e35d8ef
--- /dev/null
+++ b/src/putdos.cfg
@@ -0,0 +1,19 @@
+dmsdos-config.h d-config.h
+dmsdos-config.h.default d-config.h
+dmsdos.h dmsdos.h
+lib_interface.h l_interf.h
+lib_interface.c l_interf.c
+dblspace_interface.c d_interf.c
+dblspace_dec.c d_dec.c
+dblspace_compr.c d_compr.c
+dblspace_methsq.c d_methsq.c
+dblspace_alloc.c d_alloc.c
+dblspace_chk.c d_chk.c
+dblspace_tables.c d_tables.c
+dstacker_compr.c s_compr.c
+dstacker_dec.c s_dec.c
+dstacker_alloc.c s_alloc.c
+dcread.c dcread.c
+mcdmsdos.c mcdmsdos.c
+dmsdosfsck.c dfsck.c
+Makefile.dos-8.3 makefile.dos
diff --git a/src/putdos_helper b/src/putdos_helper
new file mode 100644
index 0000000..52580ca
--- /dev/null
+++ b/src/putdos_helper
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+FIRST=y
+read A B
+while [ "$A" != "" -a "$B" != "" ];
+do
+if [ "$FIRST" = "n" ];
+then
+echo -n "|"
+fi
+echo -n "sed s/$A/$B/g"
+read A B
+FIRST=n
+done
diff --git a/src/remove_comments.awk b/src/remove_comments.awk
new file mode 100644
index 0000000..74d14e5
--- /dev/null
+++ b/src/remove_comments.awk
@@ -0,0 +1,36 @@
+#
+# This is an awk script which removes comments in c files.
+# it does not follow #include directives.
+#
+
+BEGIN{
+ incomment=0
+}
+
+# eliminate comments
+{
+ # remove all comments fully contained on a single line
+ gsub("\\/\\*.*\\*\\/", "")
+ if (incomment) {
+ if ($0 ~ /\*\//) {
+ incomment = 0;
+ gsub(".*\\*\\/", "")
+ } else {
+ next
+ }
+ } else {
+ # start of multi-line comment
+ if ($0 ~ /\/\*/)
+ {
+ incomment = 1;
+ sub("\\/\\*.*", "")
+ } else if ($0 ~ /\*\//) {
+ incomment = 0;
+ sub(".*\\*\\/", "")
+ }
+ }
+ print $0
+}
+
+END{
+}
diff --git a/src/win32_msc50.bat b/src/win32_msc50.bat
new file mode 100644
index 0000000..878ce57
--- /dev/null
+++ b/src/win32_msc50.bat
@@ -0,0 +1,65 @@
+@echo off
+rem this file compiles dmsdos under win32. sorry I did not manage to get a
+rem makefile work with nmake :(
+rem
+rem I had success with visual c++ 5.0 from microsoft
+
+rem please adapt the include and library paths here if necessary
+rem set CFLAGS=-I d:\programme\devstudio\vc\include -D__DMSDOS_LIB__ -c
+rem set LFLAGS=/libpath:d:\programme\devstudio\vc\lib
+set CFLAGS=-D__DMSDOS_LIB__ -DUSE_SOPEN -c
+set LFLAGS=
+
+rem check for existing configuration file
+if exist dmsdos-config.h goto isthere
+copy dmsdos-config.h.default dmsdos-config.h
+:isthere
+
+del *.obj
+
+cl %CFLAGS% lib_interface.c
+cl %CFLAGS% dblspace_interface.c
+cl %CFLAGS% dblspace_dec.c
+cl %CFLAGS% dblspace_compr.c
+cl %CFLAGS% dblspace_methsq.c
+cl %CFLAGS% dblspace_alloc.c
+cl %CFLAGS% dblspace_chk.c
+cl %CFLAGS% dblspace_tables.c
+cl %CFLAGS% dstacker_compr.c
+cl %CFLAGS% dstacker_dec.c
+cl %CFLAGS% dstacker_alloc.c
+
+rem dmsdos_library.lo: $(LIB_OBJS)
+rem ld -r -o dmsdos_library.lo $^
+rem we don't want this here
+
+rem libdmsdos.a: dmsdos_library.lo
+rem ar rcs libdmsdos.a dmsdos_library.lo
+
+del libdmsdos.lib
+lib /out:libdmsdos.lib lib_interface.obj dblspace_interface.obj dblspace_dec.obj dblspace_compr.obj dblspace_methsq.obj dblspace_alloc.obj dblspace_chk.obj dblspace_tables.obj dstacker_compr.obj dstacker_dec.obj dstacker_alloc.obj
+
+rem
+rem libdmsdos.so: dmsdos_library.lo
+rem ld -shared -o libdmsdos.so dmsdos_library.lo
+rem
+
+rem dcread: dcread.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -ggdb -o dcread dcread.c -L. -ldmsdos
+cl %CFLAGS% dcread.c
+link %LFLAGS% dcread.obj libdmsdos.lib
+
+rem
+rem mcdmsdos: mcdmsdos.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -ggdb -o mcdmsdos mcdmsdos.c -L. -ldmsdos
+
+cl %CFLAGS% mcdmsdos.c
+link %LFLAGS% mcdmsdos.obj libdmsdos.lib
+
+rem
+rem dmsdosfsck: dmsdosfsck.c libdmsdos.a dmsdos.h dmsdos-config.h
+rem $(CC) -Wall -o dmsdosfsck dmsdosfsck.c -L. -ldmsdos
+
+rem cl %CFLAGS% dmsdosfsck.c
+rem link %LFLAGS% dmsdosfsck.obj libdmsdos.lib
+rem this does not compile due to missing sleep ARGHH... use Unix, Win32 sucks.