diff options
author | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-05-11 14:38:47 +0000 |
---|---|---|
committer | cc4b05f61e2d8f77114750386c9f9a60 <cc4b05f61e2d8f7@7114750386c9f9a60> | 2023-05-11 14:38:47 +0000 |
commit | f5450bfd35a6410528d124f534c2b1a958cafe51 (patch) | |
tree | a808b12d6ad5343fabdec7b8918df6b4d844e03f | |
parent | 5ad2bb7a6ac7e97c031908d2439808a00fff6214 (diff) | |
download | dmsdosnow-dmsdos-0.9.2.2.tar.gz |
dmsdos-0.9.2.2 addeddmsdos-0.9.2.2
108 files changed, 31746 insertions, 1 deletions
@@ -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. + @@ -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. @@ -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 :) @@ -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 @@ -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 :) @@ -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 @@ -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 @@ -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&§ornr<=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&§or<=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(§or,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(§or,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(§or,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(§or,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.
|