From 4f96a4e32636e06dde13befb887e01b0a2ba9cd3 Mon Sep 17 00:00:00 2001 From: Vince Weaver Date: Fri, 11 Mar 2016 14:57:07 -0500 Subject: [PATCH] Add dos33fs code This was a dos33fs filesystem driver for Linux 2.4 It is included for historical reasons. --- dos33fs-linux2.4/BUGS | 3 + dos33fs-linux2.4/CHANGES | 11 + dos33fs-linux2.4/COPYING | 356 ++++++++++++++++++++++ dos33fs-linux2.4/CREDITS | 19 ++ dos33fs-linux2.4/Makefile | 21 ++ dos33fs-linux2.4/README | 134 ++++++++ dos33fs-linux2.4/TODO | 2 + dos33fs-linux2.4/dentry.c | 92 ++++++ dos33fs-linux2.4/dir.c | 593 ++++++++++++++++++++++++++++++++++++ dos33fs-linux2.4/dos33.h | 148 +++++++++ dos33fs-linux2.4/dos33_fs.h | 48 +++ dos33fs-linux2.4/file.c | 491 +++++++++++++++++++++++++++++ dos33fs-linux2.4/inode.c | 189 ++++++++++++ dos33fs-linux2.4/misc.c | 84 +++++ dos33fs-linux2.4/super.c | 550 +++++++++++++++++++++++++++++++++ 15 files changed, 2741 insertions(+) create mode 100644 dos33fs-linux2.4/BUGS create mode 100644 dos33fs-linux2.4/CHANGES create mode 100644 dos33fs-linux2.4/COPYING create mode 100644 dos33fs-linux2.4/CREDITS create mode 100644 dos33fs-linux2.4/Makefile create mode 100644 dos33fs-linux2.4/README create mode 100644 dos33fs-linux2.4/TODO create mode 100644 dos33fs-linux2.4/dentry.c create mode 100644 dos33fs-linux2.4/dir.c create mode 100644 dos33fs-linux2.4/dos33.h create mode 100644 dos33fs-linux2.4/dos33_fs.h create mode 100644 dos33fs-linux2.4/file.c create mode 100644 dos33fs-linux2.4/inode.c create mode 100644 dos33fs-linux2.4/misc.c create mode 100644 dos33fs-linux2.4/super.c diff --git a/dos33fs-linux2.4/BUGS b/dos33fs-linux2.4/BUGS new file mode 100644 index 00000000..285b3620 --- /dev/null +++ b/dos33fs-linux2.4/BUGS @@ -0,0 +1,3 @@ +* unsure if reading files > 30k works properly. + [no errors, but have no files that big to easily check that + all the data is being transferred properly] diff --git a/dos33fs-linux2.4/CHANGES b/dos33fs-linux2.4/CHANGES new file mode 100644 index 00000000..a65cf60c --- /dev/null +++ b/dos33fs-linux2.4/CHANGES @@ -0,0 +1,11 @@ +11 October 2001 ++ After about 3 weeks of work, read-only support 99% working! ++ version 0.0.3 + +12 October 2001 ++ Fix apple-detokenizer ++ Fix file permissions ++ In theory fix files with > 30k (multiple TSL lists) + +4 February 2002 ++ Fix an endian bug so it works on my iBook diff --git a/dos33fs-linux2.4/COPYING b/dos33fs-linux2.4/COPYING new file mode 100644 index 00000000..e84a6886 --- /dev/null +++ b/dos33fs-linux2.4/COPYING @@ -0,0 +1,356 @@ + + NOTE! This copyright does *not* cover user programs that use kernel + services by normal system calls - this is merely considered normal use + of the kernel, and does *not* fall under the heading of "derived work". + Also note that the GPL below is copyrighted by the Free Software + Foundation, but the instance of code that it refers to (the Linux + kernel) is copyrighted by me and others who actually wrote it. + + Also note that the only valid version of the GPL as far as the kernel + is concerned is _this_ license (ie v2), unless explicitly otherwise + stated. + + Linus Torvalds + +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/dos33fs-linux2.4/CREDITS b/dos33fs-linux2.4/CREDITS new file mode 100644 index 00000000..daaf3adb --- /dev/null +++ b/dos33fs-linux2.4/CREDITS @@ -0,0 +1,19 @@ + This file follows the same format as Linus Torvalds' CREDITS file. See + /usr/src/linux/CREDITS for more information. + +---------- + +N: Matt Jensen +E: mjensen@obvion.com +D: Creator and chief programmer +S: Wisconsin, United States of America + +N: Linus Torvalds et al +E: Various +D: Model works and documentation: Minix FS, VFS interface, etc. +S: Various + +N: David Wilson +E: +D: Interface and code suggestions +S: Australia diff --git a/dos33fs-linux2.4/Makefile b/dos33fs-linux2.4/Makefile new file mode 100644 index 00000000..e6a44d95 --- /dev/null +++ b/dos33fs-linux2.4/Makefile @@ -0,0 +1,21 @@ +CC=gcc +CFLAGS= -O2 -Wall -DDEBUG_LEVEL=0 -DCONFIG_DOS33_FS -D__KERNEL__ -DMODULE -I/usr/src/linux/include +OBJS=file.o dir.o dentry.o inode.o misc.o super.o + +dos33.o: $(OBJS) + $(LD) -r $^ -o $@ + +file.o: dos33.h dos33_fs.h file.c + +dir.o: dos33.h dos33_fs.h dir.c + +dentry.o: dos33.h dos33_fs.h dentry.c + +inode.o: dos33.h dos33_fs.h inode.c + +super.o: dos33.h dos33_fs.h super.c + +misc.o: dos33.h dos33_fs.h misc.c + +clean: + rm -rf *.o *~ diff --git a/dos33fs-linux2.4/README b/dos33fs-linux2.4/README new file mode 100644 index 00000000..c358c009 --- /dev/null +++ b/dos33fs-linux2.4/README @@ -0,0 +1,134 @@ +NOTE!!!! This driver only works with the ancient Linux-2.4 kernel + +It will not run on anything recent. + +Plans are to make a FUSE-capable driver, see the ../dos33fs-fuse directory + + + +Apple II DOS 3.3 Filesystem for Linux + +by Vince Weaver (vince@deater.net) + http://www.deater.net/weave/vmwprod/apple + +based on ProDos filesystem code + Copyright (c) 2001 Matt Jensen (mjensen@obvion.com) + http://www.obvion.com/matt/prodos/ + +[-------------------------------------------------------------------] + +Background: + +In January of 1978, Apple Computer demoed the DISK ][ drive, +arguably the first inexpensive 5 1/4" floppy drive for a personal +computer. It was a very clever hack by Steve Wozniak, and was +welcomed by Apple ][ users who until now had to make do with +a casette tape interface. + +Dos 3.1 was released in 1978. Dos 3.2 and 3.2.1 were released +in 1979. While both worked, neither were integrated very well +with the Apple ][ computers of the time and had their share of bugs. + +In August of 1980 Dos 3.3 was released. It required not only +an OS upgrade, but also a hardware one as well. Older DOS's +could read 113.75k (35 tracks*13 sectors*256bytes). Under +Dos 3.3 the disks could hold 140k (35tracks*16sectors*256bytes). + +Dos 3.3 was the standard disk OS on Apple ]['s for a long time, +but it was limited to only 5 1/4" disks. Eventually it was +replaced by ProDos, which was a weird combination of Dos 3.3 and HFS+. + +[------------------------------------------------------------------] + +Technical Info: + +* Partition size: 140k (in theory the filesystem can support more, + but I doubt it ever did it most cases). + +* sector [block] size: 256bytes. Which makes it fun trying to make + Linux deal with it properly. + +* Filenames: up to 30 characters in length, 7-bit ASCII. + First character had to be > 63. No commas or colons. + Padded on the right with spaces. + Besides that, anything goes [including control characters, + NULL's, and \, which make it interesting as a Unix filesystem] + +* 7bits of metadata: Indicate file type [binary, BASIC, text, etc] + +* "Lock": possible to "LOCK" files, that is make read-only + +* holes: filesystem supports holes in files [though Linux support of + this a bit troublesome because of 256 byte block issues]. + +* Timestamp: no timestamp possible. The driver assigns an arbitrary + date of 13 February 1978 to all files. [2 1/2 years + too early for DOS 3.3, but it is my birthday....] + + +[---------------------------------------------------------------] + +Usage: + + You need to be running a 2.4.x linux kernel. Older versions + not supported currently. + + The easiest way to do this is get a "disk image" of the type + used for Apple II emulators. + + Various web pages can help you make these from your old Apple Disks, + assuming you have a working Apple II, a modern PC, and a serial + connection between the two. + + First, be sure your Linux kernel has "loopback filesystem support" + + Then, set up the disk image you want as a loopback device as root: + + /sbin/losetup /dev/loop0 ./green.dsk + + Where "green.dsk" can be any image you might have. + + Next, compile the included driver [ "make"]. As root, install + the dos33.o module + + /sbin/insmod ./dos33.o + + Next, mount the filesystem + + mount -t dos33 /dev/loop0 /mnt + + And if all went well, you can now do an "ls /mnt" or wherever, + and get the file listing. + + You can use the "asoft_detoken" program in the ./util + directory to dump Applesoft basic programs into plain text + + asoft_detoken < /mnt/HELLO + +[----------------------------------------------------------] + +Future plans: + + Make the driver read/write. Right now it is read-only. + + Maybe add some more utilities. + + Handle the file-type metadata somehow. + + Far-out-there.... write a linux block-device for the DISK ][ + drive, so you can actually hook the old drives and disks up + directly to your linux box.... + +[-------------------------------------------------------------------] + +References: + http://apple2history.org/ -- great history site + http://ground.icaen.uiowa.edu/apple2/ -- treasure trove of apple][ info + +[-------------------------------------------------------------------] + +Added note: + + if anyone has a copy of "Inside Apple DOS" they'd be willing + to part with, please let me know.... + diff --git a/dos33fs-linux2.4/TODO b/dos33fs-linux2.4/TODO new file mode 100644 index 00000000..14826b9f --- /dev/null +++ b/dos33fs-linux2.4/TODO @@ -0,0 +1,2 @@ +* R/W support +* support for metadata diff --git a/dos33fs-linux2.4/dentry.c b/dos33fs-linux2.4/dentry.c new file mode 100644 index 00000000..35de5c07 --- /dev/null +++ b/dos33fs-linux2.4/dentry.c @@ -0,0 +1,92 @@ +/***************************************************************************** + * dentry.c + * dentry operations. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes ========================================================*/ + +#include +#include + +/*= DOS 3.3 Includes =======================================================*/ + +#include "dos33.h" + +/*= Forward Declarations ====================================================*/ + +/* For dos33_dentry_operations. */ +int dos33_hash_dentry(struct dentry *,struct qstr *); +int dos33_compare_dentry(struct dentry *,struct qstr *,struct qstr *); + +/*= Preprocessor Macros =====================================================*/ + +/*= VFS Interface Structures ================================================*/ + +struct dentry_operations dos33_dentry_operations = { + d_hash: dos33_hash_dentry, + d_compare: dos33_compare_dentry +}; + +/*= Private Functions =======================================================*/ + +/*= Interface Functions =====================================================*/ + +/***************************************************************************** + * dos33_hash_dentry() + * Check a name for validity under DOS33 naming rules; if it is valid, hash + * it. + *****************************************************************************/ +int dos33_hash_dentry(struct dentry *de,struct qstr *qstr) { + + unsigned long hash = 0; + int len = 0; + int i = 0; + + /* Ensure that we have a valid ProDOS name. */ + i = dos33_check_name(qstr); + if (i) return i; + + /* Hash the name. */ + if (len>DOS33_MAX_NAME_LEN) len=DOS33_MAX_NAME_LEN; + + hash = init_name_hash(); + for(i = 0;i < len;i++) hash = partial_name_hash(qstr->name[i],hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/***************************************************************************** + * dos33_compare_dentry() + * Compare two hashed DOS 3.3 names. + *****************************************************************************/ +int dos33_compare_dentry(struct dentry *de,struct qstr *q1,struct qstr *q2) { + + int len = 0; + int i = 0; + + /* q2 has not been validated yet. */ + i = dos33_check_name(q2); + if (i) return i; + + /* Ignore characters past the maximum DOS 3.3 name length. */ + len = q1->len; + if (q1->len >= DOS33_MAX_NAME_LEN) { + if (q2->len < DOS33_MAX_NAME_LEN) return 1; + len = DOS33_MAX_NAME_LEN; + } + else if (len != q2->len) return ~0; + + /* Compare the strings. */ + for (i = 0;i < len;i++) { + if (q1->name[i] != q2->name[i]) return ~0; + } + + return 0; +} + diff --git a/dos33fs-linux2.4/dir.c b/dos33fs-linux2.4/dir.c new file mode 100644 index 00000000..1afac027 --- /dev/null +++ b/dos33fs-linux2.4/dir.c @@ -0,0 +1,593 @@ +/***************************************************************************** + * dir.c + * File and inode operations for directories. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes =========================================================*/ + +#include + +/*= DOS 3.3 Includes =======================================================*/ + +#include "dos33.h" + +/*= Forward Declarations ===================================================*/ + +/* For dos33_dir_inode_operations. */ +struct dentry *dos33_lookup(struct inode *,struct dentry *); +int dos33_create(struct inode *,struct dentry *,int); +int dos33_unlink(struct inode *,struct dentry *); +int dos33_rename(struct inode *,struct dentry *,struct inode *,struct dentry *); + +/* For dos33_dir_operations. */ +int dos33_readdir(struct file *,void *,filldir_t); + +/*= VFS Interface Structures ================================================*/ + +struct inode_operations dos33_dir_inode_operations = { + lookup: dos33_lookup, + create: dos33_create, + unlink: dos33_unlink, + rename: dos33_rename +}; + +struct file_operations dos33_dir_operations = { + read: generic_read_dir, + readdir: dos33_readdir +/* fsync: file_fsync */ +}; + + +/***************************************************************************** + * dos33_new_dir_block() + * Allocate and initialize a new directory block. + *****************************************************************************/ +int dos33_expand_dir(struct super_block *sb,u16 block) { + int result = 0; +// u16 new_block = 0; +// struct buffer_head *dir_bh = NULL; +// struct dos33_dir_block *dir = NULL; + + DOS33_DEBUG("expand_dir\n"); + +#if 0 + /* Try to allocate a new block. */ + new_block = prodos_alloc_block(sb); + if (!new_block) { + result = -ENOSPC; + goto out_cleanup; + } + + /* Read the block. */ + dir_bh = prodos_bread(sb,new_block); + if (!dir_bh) { + result = -EIO; + goto out_cleanup; + } + dir = (void*)dir_bh->b_data; + + /* Initialize the block. */ + memset(dir,0,PRODOS_BLOCK_SIZE); + dir->header.prev = cpu_to_le16(block); + dir->header.next = 0; + mark_buffer_dirty(dir_bh); + result = new_block; + +out_cleanup: + if (dir_bh) prodos_brelse(dir_bh); +#endif + return result; +} + +/***************************************************************************** + * dos33_find_entry() + * Search directory @inode for an entry. If @name is non-NULL, the directory + * is searched for an entry with matching name; if @name is NULL, the + * directory is searched (and possibly expanded) for an unused entry. Zero is + * returned on failure; the found entry's inode number is returned on success. + *****************************************************************************/ +unsigned long dos33_find_entry(struct inode *inode,struct qstr *name) { + + unsigned long result = 0; + u32 catalog_block = (DOS33_INO_TRACK(inode->i_ino)<<4)+ + DOS33_INO_SECTOR(inode->i_ino); + u32 odd=0,entry_num=0; + struct buffer_head *bh = NULL; + struct dos33_catalog_sector *catalog = NULL; + + DOS33_DEBUG("find entry: %i %x\n",(int)inode->i_ino,(int)catalog_block); + + /* Scan the directory for a matching entry. */ + while (catalog_block) { + + /* Load next directory block when necessary. */ + if (!bh) { + odd=catalog_block%2; + /* Load the block. */ + DOS33_DEBUG("==Loading block %x\n",catalog_block); + bh = dos33_bread(inode->i_sb,catalog_block); + if (!bh) { + DOS33_DEBUG("dos33_bread() failed for block %d\n",catalog_block); + goto out_cleanup; + } + /* deal with 256byte sector size */ + if (odd) { + catalog = (struct dos33_catalog_sector*)(bh->b_data+256); + } + else { + catalog = (struct dos33_catalog_sector*)(bh->b_data); + } + } + /* Searching for an empty entry. FIX ME */ + if (!name) { + DOS33_DEBUG("== no name dir.c FIXME\n"); + // result = DOS33_MAKE_INO(0,(cur_block>>4)&0xffff,cur_block&0xf); + goto out_cleanup; + } + else { + int qlen=name->len; + int mismatch=0; + int i; + struct dos33_file_t *file_ent=&catalog->files[entry_num]; + + /* Do the names match? */ + for(i=0;iname[i] != ((file_ent->file_name[i])&0x7f)) mismatch++; + } + /* If they match, return the inode */ + if (!mismatch) { + DOS33_DEBUG("==MATCH %s\n",name->name); + result = DOS33_MAKE_INO(0,entry_num, + (catalog_block>>4)&0x7f, + (catalog_block)&0xf); + goto out_cleanup; + } + } + + + /* Advance to next entry. */ + /* For now only handle first 7 files */ + if (++entry_num >= 7) { + DOS33_DEBUG("ENDDDDDDDDDDDING\n"); + entry_num=0; + + catalog_block = ((catalog->next.track)<<4)+catalog->next.sector; + DOS33_DEBUG("== next block %x\n",catalog_block); + + /* Release previous block. */ + dos33_brelse(bh); + bh = NULL; + } + } + +out_cleanup: + if (bh) { + dos33_brelse(bh); + bh = NULL; + } + return result; +} + +/***************************************************************************** + * prodos_unlink_all() + * Unlink (delete) all forks of the ProDOS file represented by an @inode. + *****************************************************************************/ +#if 0 +int dos33_unlink_all(struct inode *inode,struct dos33_dir_entry *ent) { + int result = 0; + const u16 dblk = PRODOS_INO_DBLK(inode->i_ino); + const u8 dent = PRODOS_INO_DENT(inode->i_ino); + u8 dfrk = PRODOS_INO_DFORK(inode->i_ino); + struct buffer_head *ext_bh = NULL; + + /* We may have to delete TWO forks. */ + if (PRODOS_ISEXTENDED(*ent)) { + struct prodos_ext_key_block *ext_block = NULL; + + /* Load the extended directory block. */ + ext_bh = prodos_bread(inode->i_sb,PRODOS_I(inode)->i_key); + if (!ext_bh) { + result = -EIO; + goto out_cleanup; + } + ext_block = (void*)ext_bh->b_data; + + /* Free the resource fork. */ + prodos_free_tree_blocks(inode->i_sb,ext_block->res.stype << 4, + le16_to_cpu(ext_block->res.key_block), + le16_to_cpu(ext_block->res.blocks_used)); + + /* Free the data fork. */ + prodos_free_tree_blocks(inode->i_sb,ext_block->data.stype << 4, + le16_to_cpu(ext_block->data.key_block), + le16_to_cpu(ext_block->data.blocks_used)); + + /* Release the extended directory block. */ + prodos_brelse(ext_bh); + ext_bh = NULL; + + /* Free the extended directory block. */ + prodos_free_block(inode->i_sb,PRODOS_I(inode)->i_key); + } + + /* ...or we may only have to delete one fork. */ + else { + prodos_free_tree_blocks(inode->i_sb,PRODOS_I(inode)->i_stype, + le16_to_cpu(ent->key_block), + le16_to_cpu(ent->blocks_used)); + } + + /* Update this inode. */ + inode->i_nlink--; + mark_inode_dirty(inode); + + /* Unlink the resource fork inode. */ + if (PRODOS_I(inode)->i_stype == PRODOS_STYPE_EXTENDED) { + inode = iget(inode->i_sb,PRODOS_MAKE_INO(dblk,dent,PRODOS_DFORK_RES)); + if (inode) { + down(&inode->i_sem); + inode->i_nlink--; + mark_inode_dirty(inode); + up(&inode->i_sem); + iput(inode); + } + } + + /* We may have been called on either a data fork inode OR a meta file + inode. Unlink the one upon which we were NOT called (the one we were + called on has, obviously, already been unlinked.) */ + dfrk = dfrk == PRODOS_DFORK_META ? PRODOS_DFORK_DATA : PRODOS_DFORK_META; + inode = iget(inode->i_sb,PRODOS_MAKE_INO(dblk,dent,dfrk)); + if (inode) { + down(&inode->i_sem); + inode->i_nlink--; + mark_inode_dirty(inode); + up(&inode->i_sem); + iput(inode); + } + + /* Mark the directory entry as deleted. */ + ent->name[0] &= 0x0f; + result = 1; + +out_cleanup: + if (ext_bh) prodos_brelse(ext_bh); + + return result; +} +#endif +/***************************************************************************** + * prodos_unlink_res() + * Unlink (delete) the resource fork of the ProDOS file represented by an + * @inode. This is the most involved of the unlink operations. The resource + * fork is deleted and its blocks freed, then the file is collapsed into a + * a regular (non-extended) file by copying the data fork metainformation from + * the extended directory block into the base directory entry itself. + *****************************************************************************/ +#if 0 +int dos33_unlink_res(struct inode *inode,struct prodos_dir_entry *ent) { + int result = 0; + struct buffer_head *key_bh = NULL; + struct prodos_ext_key_block *key_block = NULL; + + /* Need the key block so we can copy data fork info into the dir entry. */ + key_bh = prodos_bread(inode->i_sb,PRODOS_I(inode)->i_key); + if (!key_bh) { + result = -EIO; + goto out_cleanup; + } + key_block = (void*)key_bh->b_data; + + /* Update the directory entry. Note that we don't have to worry about byte + order since we're copying from one on-disk entry to another. */ + ent->name[0] &= 0x0f; + ent->name[0] |= key_block->data.stype << 4; + ent->key_block = key_block->data.key_block; + ent->blocks_used = key_block->data.blocks_used; + memcpy(&ent->eof,&key_block->data.eof,3); + + /* TODO: Update the data and meta inodes here (stype, key block, size, blocks used) */ + + /* Free affected disk blocks. */ + prodos_free_tree_blocks(inode->i_sb,key_block->res.stype << 4, + le16_to_cpu(key_block->res.key_block), + le16_to_cpu(key_block->res.blocks_used)); + prodos_free_block(inode->i_sb,PRODOS_I(inode)->i_key); + + /* The inode is now dirty; also return 1 to cause the directory buffer + to be marked dirty. */ + mark_inode_dirty(inode); + result = 1; + +out_cleanup: + if (key_bh) prodos_brelse(key_bh); + + return result; +} +#endif +/*= Interface Functions =====================================================*/ + +/***************************************************************************** + * dos33_lookup() + * Search a directory inode for an entry. + *****************************************************************************/ +struct dentry *dos33_lookup(struct inode *inode,struct dentry *dentry) { + + SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(inode->i_sb); + struct dentry *result = NULL; + unsigned long ino = 0; + + DOS33_DEBUG("Lookup\n"); + + /* Grab directory lock. */ + down(&psb->s_dir_lock); + + /* Attempt to find matching entry and get its inode number. */ + ino = dos33_find_entry(inode,&dentry->d_name); + if (!ino) { + result = ERR_PTR(-ENOENT); + goto out_cleanup; + } + + /* Get the inode and set up a dentry. */ + inode = iget(inode->i_sb,ino); + if (!inode) { + DOS33_DEBUG("iget() failed for ino 0x%08x",(int)ino); + result = ERR_PTR(-EACCES); + goto out_cleanup; + } + dentry->d_op = &dos33_dentry_operations; + d_add(dentry,inode); + +out_cleanup: + up(&psb->s_dir_lock); + + return result; +} + +/***************************************************************************** + * prodos_create() + * Create a new entry in directory inode @dir. Note that the VFS has already + * checked that the filename is unique within the directory. + *****************************************************************************/ +int dos33_create(struct inode *dir,struct dentry *dentry,int mode) { +// SHORTCUT struct prodos_sb_info * psb = PRODOS_SB(dir->i_sb); + int result = 0; +// unsigned long ino = 0; +// struct buffer_head *dir_bh = NULL; + DOS33_DEBUG("create\n"); + +#if 0 + PRODOS_ERROR_0(dir->i_sb,"called"); + + /* Grab directory lock. */ + down(&psb->s_dir_lock); + + /* Get an unused "inode" in the directory. */ + ino = prodos_find_entry(dir,NULL); + if (!ino) { + result = -ENOSPC; + goto out_cleanup; + } + + /* Read the directory block. */ +/* dir_bh = prodos_bread(dir->i_sb,PRODOS_INO_DBLK(ino)); + if (!dir_bh) { + result = -EIO; + goto out_cleanup; + }*/ + + result = -EPERM; + +out_cleanup: + if (dir_bh) prodos_brelse(dir_bh); + up(&psb->s_dir_lock); +#endif + return result; +} + +/***************************************************************************** + * prodos_unlink() + *****************************************************************************/ +/* NOTE: The VFS holds the inode->i_zombie semaphore during this call. */ +/* The big kernel lock is also held for exactly the duration of this call. */ +int dos33_unlink(struct inode *inode,struct dentry *dentry) { +// SHORTCUT struct prodos_sb_info * const psb = PRODOS_SB(inode->i_sb); +// SHORTCUT struct inode * const victim = dentry->d_inode; + int result = 0; +// struct buffer_head *dir_bh = NULL; +// struct prodos_dir_entry *ent = NULL; + + DOS33_DEBUG("unlink\n"); + +#if 0 + /* Grab directory lock. */ + down(&psb->s_dir_lock); + + /* Load the directory block. */ + dir_bh = prodos_bread(inode->i_sb,PRODOS_INO_DBLK(victim->i_ino)); + if (!dir_bh) { + result = -EIO; + goto out_cleanup; + } + ent = &((struct prodos_dir_block*)dir_bh->b_data)->entries[PRODOS_INO_DENT(victim->i_ino)]; + + /* Dispatch appropriate unlink function. */ + switch (PRODOS_INO_DFORK(victim->i_ino)) { + case PRODOS_DFORK_DATA: + result = prodos_unlink_all(victim,ent); + break; + case PRODOS_DFORK_META: + result = prodos_unlink_all(victim,ent); + break; + case PRODOS_DFORK_RES: + if (!PRODOS_ISEXTENDED(*ent)) { + result = -ENOENT; + goto out_cleanup; + } + result = prodos_unlink_res(victim,ent); + break; + } + + /* The prodos_unlink_*() helper returned 1 if it dirtied the dir block. */ + if (result == 1) { + mark_buffer_dirty(dir_bh); + result = 0; + } + +out_cleanup: + if (dir_bh) prodos_brelse(dir_bh); + up(&psb->s_dir_lock); +#endif + return result; +} + +/***************************************************************************** + * prodos_rename() + *****************************************************************************/ +int dos33_rename(struct inode *oi,struct dentry *od,struct inode *ni,struct dentry *nd) { + int result = 0; + + DOS33_DEBUG("rename\n"); + +#if 0 + /* Our inode number usage prevents all but in-directory renames. */ + if (oi != ni) { + result = -EPERM; + goto out_cleanup; + } + + result = -EPERM; + +out_cleanup: +#endif + return result; +} + +/***************************************************************************** + * dos33_readdir() + * Standard file operation; reads a directory and returns its entries via + * the filldir() function supplied by the caller. + *****************************************************************************/ +int dos33_readdir(struct file *filp,void *dirent,filldir_t filldir) { + + SHORTCUT struct super_block * const sb = filp->f_dentry->d_sb; + SHORTCUT struct inode * const inode = filp->f_dentry->d_inode; + SHORTCUT struct inode * const parent = filp->f_dentry->d_parent->d_inode; + int result = 0; + struct buffer_head *bh = NULL; + struct dos33_catalog_sector *catalog = NULL; + u32 dent=0; + + u32 catalog_track = DOS33_INO_TRACK(filp->f_pos); + u32 catalog_sector = DOS33_INO_SECTOR(filp->f_pos); + u32 catalog_block; + u32 odd; + struct dos33_file_t *ent = NULL; + + char name[DOS33_MAX_NAME_LEN + 3] = ""; + int nlen; + unsigned int ino = 0; + + DOS33_DEBUG("readdir\n"); + + /* Handle the special '.' and '..' entries. */ + switch ( (long int)filp->f_pos) { + + case 0: /* Add the '.' entry. */ + if (filldir(dirent,".",1,0,inode->i_ino,DT_DIR) < 0) + goto out_cleanup; + + /* fpos is simply incremented at this point. */ + filp->f_pos++; + /* Fall through. */ + + case 1: /* Add the '..' entry. */ + if (filldir(dirent,"..",2,1,parent->i_ino,DT_DIR) < 0) + goto out_cleanup; + + /* fpos takes on special format after second entry. */ + filp->f_pos = DOS33_MAKE_INO(0,0, + DOS33_INO_TRACK(inode->i_ino), + DOS33_INO_SECTOR(inode->i_ino)); + } + + /* Grab directory lock. */ + down(&DOS33_SB(sb)->s_dir_lock); + + /* After the two special entries, filp->f_pos is the "inode number" of the + next file (or fork) in the directory. See the PRODOS_INO_*() and + PRODOS_MAKE_INO() macros for the format of this value. When all entries + have been returned filp->f_pos is set to 3 to signal end-of-directory. */ + while (filp->f_pos != 3) { + + catalog_block=((catalog_track<<4)+catalog_sector); + odd=catalog_block%2; + + DOS33_DEBUG("filp: %x dblk: %x %i %i\n",(int)filp->f_pos,(int)catalog_block,(int)catalog_track,(int)catalog_sector); + + /* Load the block if necessary. */ + if (!bh) { + bh = dos33_bread(sb,catalog_block); + if (!bh) { + result = -EIO; + goto out_cleanup; + } + /* handle 256 byte sectors */ + if (odd) { + catalog = (struct dos33_catalog_sector*)(bh->b_data+256); + } + else { + catalog = (struct dos33_catalog_sector*)bh->b_data; + } + } + + + ent = &catalog->files[dent]; + + /* Copy and modify the name as necessary. */ + if (ent->file_name[0]!=0) { + memcpy(name,&(ent->file_name),DOS33_MAX_NAME_LEN+1); + nlen=dos33_convert_filename(name); + + /* Build inode number. */ + ino = DOS33_MAKE_INO(0,dent,catalog_track, + catalog_sector); + + /* Finally, attempt to return the entry via filldir(). */ + if (filldir(dirent,&name[0],nlen,filp->f_pos,ino,DT_UNKNOWN) < 0) + goto out_cleanup; + } + dent++; + + if (dent>6) { + dent=0; + dos33_brelse(bh); + bh=NULL; + + catalog_track=(catalog->next.track); + catalog_sector=(catalog->next.sector); + + if ((!catalog_track) && (!catalog_sector)) { + filp->f_pos=3; + } + + } + } + +/* Release remaining resources and return. */ +out_cleanup: + if (bh) { + dos33_brelse(bh); + bh = NULL; + } + up(&DOS33_SB(sb)->s_dir_lock); + + return result; +} + diff --git a/dos33fs-linux2.4/dos33.h b/dos33fs-linux2.4/dos33.h new file mode 100644 index 00000000..fc8ec811 --- /dev/null +++ b/dos33fs-linux2.4/dos33.h @@ -0,0 +1,148 @@ +/***************************************************************************** + * dos33.h + * Includes, definitions, and helpers pertaining to the Linux driver + * implementation. + * + * Apple II Apple DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * based on code Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ +#ifndef __DOS33_DOS33_H +#define __DOS33_DOS33_H + +/*= ProDOS Includes =========================================================*/ + +#include "dos33_fs.h" + +/*= Code Readability Tags ===================================================*/ + +/* SHORTCUT identifies shortcuts to buried struct members, etc. */ +#define SHORTCUT /* */ + + +/* Simplify access to custom superblock info. */ +#define DOS33_SB(s) ((struct dos33_sb_info *)((s)->u.generic_sbp)) +#define DOS33_I(i) ((struct dos33_inode_info *)&(i)->u.minix_i) + +#define DOS33_INO_DENT(ino) ((ino)>>31) +#define DOS33_INO_ENTRY(ino) (((ino)>>16)&0xff) +#define DOS33_INO_TRACK(ino) (((ino)>>8)&0xff) +#define DOS33_INO_SECTOR(ino) ((ino)&0xff) +#define DOS33_MAKE_INO(__d,__n,__t,__s) ( ((__d)<<31) | (((__n)&0xff)<<16) | (((__t)&0xff)<<8) | (__s &0xff)) + +/*= Structure Definitions ===================================================*/ + +/* super_block information specific to ProDOS filesystems. */ +struct dos33_sb_info { + u32 s_flags; + + /* Items relating to partition location. */ + u32 s_part_start; + u32 s_part_size; + u8 s_part[32]; + + /* Items relating to the volume bitmap. */ + struct semaphore s_bm_lock; + u32 bitmaps[0x64]; /* one extra to act as NULL terminator */ + u16 s_bm_start; + int s_bm_free; + + /* Items relating to miscellaneous locking and serialization. */ + struct semaphore s_dir_lock; +}; + +/* inode information specific to ProDOS "inodes." Note that this structure is + never actually instantiated. It is simply overlaid upon inode.u.minix_i to + avoid the need to modify fs.h in the standard Linux source. Obviously, this + limits the prodos_inode_info structure to a size <= the size of + struct minix_inode_info. */ +struct dos33_inode_info { + u8 i_flags; + u8 i_filetype; + u8 i_stype; + u16 i_key; + u16 i_auxtype; + u8 i_access; +}; + +struct dos33_track_sector { + u8 track; + u8 sector; +} __attribute__((packed)); + +struct dos33_vtol { + u8 four; + struct dos33_track_sector catalog; + u8 dos_version; + u8 unused1[2]; + u8 volume; + u8 unused2[0x20]; + u8 ts_pairs; + u8 unused3[8]; + u8 last_track; + u8 allocation_direction; + u8 unused4[2]; + u8 tracks_per_disk; + u8 sectors_per_track; + u16 bytes_per_sector; + u32 bitmaps[0x23]; + u8 unused5[0x3b]; +} __attribute__((packed)); + +struct dos33_file_t { + struct dos33_track_sector first_tsl; + u8 file_type; + u8 file_name[0x1e]; /* high bit set, padded with spaces */ + u16 num_sectors; +} __attribute__((packed)); + +struct dos33_catalog_sector { + u8 unused1; + struct dos33_track_sector next; + u8 unused[8]; + struct dos33_file_t files[7]; +} __attribute__((packed)); + +struct dos33_ts_list { + u8 unused1; + struct dos33_track_sector next; + u8 unused2[2]; + u16 current_sectors_deep; + u8 unused3[5]; + struct dos33_track_sector data_location[122]; +} __attribute__((packed)); + + +/*= Externals ===============================================================*/ + +/* dir.c */ +extern struct inode_operations dos33_dir_inode_operations; +extern struct file_operations dos33_dir_operations; + +/* file.c */ +extern struct inode_operations dos33_file_inode_operations; +extern struct file_operations dos33_file_operations; +extern struct address_space_operations dos33_address_space_operations; + +/* dentry.c */ +extern struct dentry_operations dos33_dentry_operations; + +/* inode.c */ +extern void dos33_read_inode(struct inode *); +extern void dos33_write_inode(struct inode *,int); +extern void dos33_put_inode(struct inode *); + +/* super.c */ +extern int dos33_count_free_blocks(struct super_block *sb); +extern int dos33_alloc_block(struct super_block *sb); +extern int dos33_free_block(struct super_block *sb,u16 block); +extern int dos33_free_tree_blocks(struct super_block *sb,u8,u16,u16); + +/* misc.c */ +extern int dos33_convert_filename(char *filename); +extern int dos33_check_name(struct qstr *); +extern struct buffer_head *dos33_bread(struct super_block *,int); +extern void dos33_brelse(struct buffer_head *); + +#endif diff --git a/dos33fs-linux2.4/dos33_fs.h b/dos33fs-linux2.4/dos33_fs.h new file mode 100644 index 00000000..a96acd3b --- /dev/null +++ b/dos33fs-linux2.4/dos33_fs.h @@ -0,0 +1,48 @@ +/***************************************************************************** + * prodos/prodos_fs.h + * Includes, definitions, and helpers pertaining to the ProDOS filesystem + * implementation. + * + * Apple II ProDOS Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + * + * 18-May-2001: Created + *****************************************************************************/ +#ifndef __DOS33_DOS33_FS_H +#define __DOS33_DOS33_FS_H + +/*= Preprocessor Constants ==================================================*/ + +/* Block size. */ +#define DOS33_BLOCK_SIZE 0x200 +#define DOS33_BLOCK_SIZE_BITS 9 /* log base-2 of PRODOS_BLOCK_SIZE */ + +/* Number of entries per directory block. */ +#define DOS33_DIR_ENTS_PER_BLOCK 7 + +/* Various limits and maximums. */ +#define DOS33_MAX_NAME_LEN 0x1e +#define DOS33_MAX_FILE_SIZE 0x00ffffff + +#define DOS33_SUPER_MAGIC 0x02131978 + +#define DOS33_VOLUME_DIR_BLOCK (0x11*0x10) +#define DOS33_VOLUME_DIR_TRACK 0x11 +#define DOS33_VOLUME_DIR_SECTOR 0x00 + + +#define TWO_BYTES_TO_SHORT(__x,__y) ((((int)__y)<<8)+__x) + +#if (DEBUG_LEVEL==1) + +#define DOS33_DEBUG printk + +#else + +#define DOS33_DEBUG if (0) printk + +#endif +#define DOS33_ERROR printk + +#endif diff --git a/dos33fs-linux2.4/file.c b/dos33fs-linux2.4/file.c new file mode 100644 index 00000000..ca945a00 --- /dev/null +++ b/dos33fs-linux2.4/file.c @@ -0,0 +1,491 @@ +/***************************************************************************** + * file.c + * File and inode operations for regular files. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes =========================================================*/ + + +#include +#include + +/*= DOS 3.3 Includes =======================================================*/ + +#include "dos33.h" + +/*= Forward Declarations ====================================================*/ + +/* For dos33_address_space_operations. */ +int dos33_readpage(struct file *,struct page *); +int dos33_writepage(struct page *); +int dos33_prepare_write(struct file *,struct page *,unsigned int, unsigned int); +int dos33_commit_write(struct file*,struct page *,unsigned int,unsigned int); +int dos33_bmap(struct address_space *,long); + +/*= VFS Interface Structures ================================================*/ + +struct inode_operations dos33_file_inode_operations = { + +}; + +struct file_operations dos33_file_operations = { + read: generic_file_read, + write: generic_file_write, + mmap: generic_file_mmap +}; + +struct address_space_operations dos33_address_space_operations = { + readpage: dos33_readpage, + writepage: dos33_writepage, + sync_page: block_sync_page, + prepare_write: dos33_prepare_write, + commit_write: dos33_commit_write, + bmap: dos33_bmap +}; + +/*= Interface Functions =====================================================*/ + +/***************************************************************************** + * dos33_get_block() + * Translate a block number within an @inode into a logical disk block number. + *****************************************************************************/ +int dos33_get_block(struct inode *inode,long block,struct buffer_head *bh_res,int create) { + + int result = 0; + + struct dos33_catalog_sector *catalog = NULL; + struct dos33_file_t *file_ent; + + struct dos33_ts_list *tsl; + int i; + u32 catalog_block=(DOS33_INO_TRACK(inode->i_ino)<<4)+ + DOS33_INO_SECTOR(inode->i_ino); + + u32 data_block; + u32 odd,half_block; + + u32 file_entry=DOS33_INO_ENTRY(inode->i_ino); + u32 tsl_block; + + struct buffer_head *bh = NULL; + struct buffer_head *bh2 = NULL; + + struct buffer_head *bh_temp=NULL; + + + + DOS33_DEBUG("** get_block 0x%x from file 0x%x\n",(int)block,(int)inode->i_ino); + #if 0 + /* Verify that the block number is valid. */ + if ((block < 0) || (block >= inode->i_blocks)) { + printk("** bad block number: 0< 0x%x < 0x%x\n",(int)block,(int)inode->i_blocks); + result = -EIO; + goto out_cleanup; + } +#endif + + if (!bh) { + odd=catalog_block%2; + /* Load the block. */ + DOS33_DEBUG("** Loading block %x\n",catalog_block); + bh = dos33_bread(inode->i_sb,catalog_block); + if (!bh) { + DOS33_ERROR("** dos33_bread() failed for block %d\n",catalog_block); + goto out_cleanup; + } + /* deal with 256byte sector size */ + if (odd) { + catalog = (struct dos33_catalog_sector*)(bh->b_data+256); + } + else { + catalog = (struct dos33_catalog_sector*)(bh->b_data); + } + } + file_ent=&catalog->files[file_entry]; + + tsl_block= (file_ent->first_tsl.track<<4)+file_ent->first_tsl.sector; + + DOS33_DEBUG("** going to try reading tsl from 0x%x\n",tsl_block); + + odd=tsl_block%2; + /* Load the block. */ + + bh2 = dos33_bread(inode->i_sb,tsl_block); + if (!bh2) { + DOS33_ERROR("** dos33_bread() failed for block %d\n",tsl_block); + goto out_cleanup; + } + /* deal with 256byte sector size */ + if (odd) { + tsl = (struct dos33_ts_list*)(bh2->b_data+256); + } + else { + tsl = (struct dos33_ts_list*)(bh2->b_data); + } + + /* each tsl list only holds 122 sectors */ + /* so if bigger, we must follow the chain to the proper tsl */ + if ((block*2) > 122) { + DOS33_DEBUG("!! need tsl %i\n",(int)((block*2)/122)); + + for (i=0;i< ((block*2)/122);i++) { + tsl_block=((tsl->next.track)<<4)+tsl->next.sector; + if (tsl_block==0) { + DOS33_ERROR("dos33.o: NULL TSL BLOCK!\n"); + goto out_cleanup; + } + DOS33_DEBUG("!! going to tsl in block 0x%x\n",tsl_block); + + dos33_brelse(bh2); + bh2=NULL; + + odd=tsl_block%2; + /* Load the block. */ + + bh2 = dos33_bread(inode->i_sb,tsl_block); + if (!bh2) { + DOS33_ERROR("dos33_bread() failed for block %d\n",tsl_block); + goto out_cleanup; + } + /* deal with 256byte sector size */ + if (odd) { + tsl = (struct dos33_ts_list*)(bh2->b_data+256); + } + else { + tsl = (struct dos33_ts_list*)(bh2->b_data); + } + } + /* move block to the proper value */ + block=block%61; + + } + + + + /* OK, we know where the data is, not let's trick the VFS */ + /* into thinking we can read 256 byte blocks */ + + for (half_block=0;half_block<2;half_block++) { + + + data_block=(u32)( ((tsl->data_location[(block*2)+half_block].track)<<4)+ + tsl->data_location[(block*2)+half_block].sector); + DOS33_DEBUG("!!** found block: %x [%x %x] (pass %i request block %i)\n", + data_block,tsl->data_location[(block*2)+half_block].track, + tsl->data_location[(block*2)+half_block].sector,half_block,(int)block); + + + if (data_block==0) { + /* a hole.. but since we have to fake block size */ + /* we fill it with zeros ourselves */ + DOS33_DEBUG("** HOLE\n"); + + lock_buffer(bh_res); + memset(bh_res->b_data+(half_block*256),0,256); + bh_res->b_state |= (1 << BH_Mapped); + mark_buffer_uptodate(bh_res,1); + unlock_buffer(bh_res); + + } + else { + bh_temp = dos33_bread(inode->i_sb,data_block); + if (!bh_temp) { + DOS33_ERROR("dos33_bread() failed for block %d\n",data_block); + goto out_cleanup; + } + + if (data_block%2) { + lock_buffer(bh_res); + memcpy(bh_res->b_data+(half_block*256),bh_temp->b_data+256,256); + bh_res->b_state |= (1 << BH_Mapped); + mark_buffer_uptodate(bh_res,1); + unlock_buffer(bh_res); + } + else { + lock_buffer(bh_res); + memcpy(bh_res->b_data+(half_block*256),bh_temp->b_data,256); + bh_res->b_state |= (1 << BH_Mapped); + mark_buffer_uptodate(bh_res,1); + unlock_buffer(bh_res); + } + DOS33_DEBUG("** copying 256bytes from sector 0x%x starting with %0x %0x\n", + data_block, + (char)*(bh_temp->b_data),(char)*(bh_temp->b_data+1)); + + } + } +out_cleanup: + + if (bh) dos33_brelse(bh); + if (bh2) dos33_brelse(bh2); + if (bh_temp) dos33_brelse(bh_temp); + return result; +} + +/***************************************************************************** + * prodos_get_block_write() + * get_block_t function for write operations. This function is ugly and + * probably temporary. Ultimately it should be merged with prodos_get_block() + * and thoroughly cleaned. What you see here is basically a quick and (very) + * dirty hack. + *****************************************************************************/ +int dos33_get_block_write(struct inode *inode,long block,struct buffer_head *bh_res,int create) { +// SHORTCUT struct prodos_inode_info * const pi = PRODOS_I(inode); + int result = 0; + + DOS33_DEBUG("get_block_write\n"); + +#if 0 + /* Croak on non-regular files for now. */ + if ((pi->i_stype < PRODOS_STYPE_SEEDLING) || (pi->i_stype > PRODOS_STYPE_TREE)) { + PRODOS_ERROR_0(inode->i_sb,"extended files not supported for writing"); + result = -EPERM; + goto out_cleanup; + } + + /* Can't translate block numbers that are out of range. */ + if (block >= PRODOS_MAX_FILE_SIZE >> PRODOS_BLOCK_SIZE_BITS) { + result = -EFBIG; + goto out_cleanup; + } + + /* Expand the file if necessary. */ + if (block >= inode->i_blocks) { + u8 new_stype = pi->i_stype; + u16 new_key = pi->i_key; + u16 new_bused = inode->i_blocks; + + /* May have to convert a seedling into a sapling. */ + if ((block > 0) && (new_stype == PRODOS_STYPE_SEEDLING)) { + u16 key_ptr = 0; + struct buffer_head *bh = NULL; + + /* Allocate the new indirect block. */ + key_ptr = prodos_alloc_block(inode->i_sb); + if (!key_ptr) { + result = -ENOSPC; + goto out_cleanup; + } + + /* Load and initialize the indirect block. */ + bh = prodos_bread(inode->i_sb,key_ptr); + if (!bh) { + result = -EIO; + goto out_cleanup; + } + memset(bh->b_data,0,PRODOS_BLOCK_SIZE); + PRODOS_SET_INDIRECT_ENTRY(bh->b_data,0,new_key); + mark_buffer_dirty(bh); + prodos_brelse(bh); + + new_stype = PRODOS_STYPE_SAPLING; + new_key = key_ptr; + new_bused++; + } + + /* May have to convert a sapling into a tree. */ + if ((block > 256) && (new_stype == PRODOS_STYPE_SAPLING)) { + u16 key_ptr = 0; + struct buffer_head *bh = NULL; + + /* Allocate the new double indirect block. */ + key_ptr = prodos_alloc_block(inode->i_sb); + if (!key_ptr) { + result = -ENOSPC; + goto out_cleanup; + } + + /* Load and initialize the double indirect block. */ + bh = prodos_bread(inode->i_sb,key_ptr); + if (!bh) { + result = -EIO; + goto out_cleanup; + } + memset(bh->b_data,0,PRODOS_BLOCK_SIZE); + PRODOS_SET_INDIRECT_ENTRY(bh->b_data,0,new_key); + mark_buffer_dirty(bh); + prodos_brelse(bh); + + new_stype = PRODOS_STYPE_TREE; + new_key = key_ptr; + new_bused++; + } + + /* Update the inode if storage type changed. */ + if (new_stype != pi->i_stype) { + pi->i_stype = new_stype; + pi->i_key = new_key; + inode->i_blocks = new_bused; + mark_inode_dirty(inode); + } + + /* May need to expand indexes for a tree file. */ + if (new_stype == PRODOS_STYPE_TREE) { + u16 ind_existing = ((inode->i_blocks - 1) >> 8) + 1; + u16 ind_needed = block >> 8; + u16 ind_ptr = 0; + struct buffer_head *dind_bh = NULL; + struct buffer_head *ind_bh = NULL; + + dind_bh = prodos_bread(inode->i_sb,pi->i_key); + if (!dind_bh) { + result = -EIO; + goto out_cleanup; + } + for (;ind_existing < ind_needed;ind_existing++) { + ind_ptr = prodos_alloc_block(inode->i_sb); + if (!ind_ptr) { + result = -ENOSPC; + goto out_cleanup; + } + ind_bh = prodos_bread(inode->i_sb,ind_ptr); + if (!ind_bh) { + result = -EIO; + goto out_cleanup; + } + memset(ind_bh,0,PRODOS_BLOCK_SIZE); + PRODOS_SET_INDIRECT_ENTRY(dind_bh->b_data,ind_existing,ind_ptr); + prodos_brelse(ind_bh); + inode->i_blocks = ++new_bused; + } + prodos_brelse(dind_bh); + } + + } + + { + u16 blk = pi->i_key; + struct buffer_head *bh = NULL; + /* Appropriate action depends upon the file's storage type. */ + switch (pi->i_stype) { + case PRODOS_STYPE_TREE: + /* Load double indirect block. */ + bh = prodos_bread(inode->i_sb,blk); + if (!bh) { + result = -EIO; + goto out_cleanup; + } + + /* Get indirect block and release double indirect block. */ + blk = PRODOS_GET_INDIRECT_ENTRY(bh->b_data,block >> 8); + prodos_brelse(bh); + bh = NULL; + /* Fall through. */ + case PRODOS_STYPE_SAPLING: + /* Load indirect block. */ + bh = prodos_bread(inode->i_sb,blk); + if (!bh) { + result = -EIO; + goto out_cleanup; + } + + /* Get data block and release indirect block. */ + blk = PRODOS_GET_INDIRECT_ENTRY(bh->b_data,block & 0xff); + if (!blk) { + bh_res->b_state |= (1UL << BH_New); + blk = prodos_alloc_block(inode->i_sb); + if (!blk) { + result = -ENOSPC; + goto out_cleanup; + } + PRODOS_SET_INDIRECT_ENTRY(bh->b_data,block & 0xff,blk); + inode->i_blocks++; + } + prodos_brelse(bh); + bh = NULL; + + /* Fall through. */ + case PRODOS_STYPE_SEEDLING: + /* Set bh_res fields to cause the block to be read from disk. */ + bh_res->b_blocknr = PRODOS_SB(inode->i_sb)->s_part_start + blk; + bh_res->b_dev = inode->i_dev; + bh_res->b_state |= (1 << BH_Mapped); + } + } + + mark_inode_dirty(inode); + +out_cleanup: +#endif + return result; +} + +/***************************************************************************** + * dos33_readpage() + *****************************************************************************/ +int dos33_readpage(struct file *filp,struct page *page) { + int result = 0; + + DOS33_DEBUG("readpage\n"); + + /* Let the generic "read page" function do most of the work. */ + result = block_read_full_page(page,dos33_get_block); + + /* Propagate block_read_full_page() return value. */ + return result; +} + +/***************************************************************************** + * prodos_writepage() + *****************************************************************************/ +int dos33_writepage(struct page *page) { + DOS33_DEBUG("writepage\n"); + + /* Generic function does the work with the help of a get_block function. */ +// PRODOS_ERROR_0(page->mapping->host->i_sb,"called"); +// return block_write_full_page(page,prodos_get_block_write); + return 0; +} + + +/***************************************************************************** + * prodos_prepare_write() + *****************************************************************************/ +int dos33_prepare_write(struct file *file,struct page *page,unsigned int from,unsigned int to) { + DOS33_DEBUG("prepare_write\n"); + +//// return block_prepare_write(page,from,to,prodos_get_block_write); + return 0; +} + +/***************************************************************************** + * prodos_commit_write() + *****************************************************************************/ +int dos33_commit_write(struct file *file,struct page *page,unsigned int from,unsigned int to) { + DOS33_DEBUG("commit write\n"); + +#if 0 + /* Convert text files to ProDOS format. */ + if (PRODOS_I(page->mapping->host)->i_flags & PRODOS_I_CRCONV) { + int i = 0; + char *pdata = kmap(page); + for (i = 0;i < PAGE_CACHE_SIZE;i++,pdata++) + if (*pdata == '\n') *pdata = '\r'; + kunmap(page); + } + + /* Let generic function do the real work. */ +#endif + return generic_commit_write(file,page,from,to); +} + +/***************************************************************************** + * prodos_bmap() + *****************************************************************************/ +int dos33_bmap(struct address_space *mapping,long block) { + DOS33_DEBUG("bmap\n"); + + /* Generic function does the work with the help of a get_block function. */ +// PRODOS_ERROR_0(mapping->host->i_sb,"called"); +// return generic_block_bmap(mapping,block,prodos_get_block_write); + return 0; +} + + + + diff --git a/dos33fs-linux2.4/inode.c b/dos33fs-linux2.4/inode.c new file mode 100644 index 00000000..0d9aad90 --- /dev/null +++ b/dos33fs-linux2.4/inode.c @@ -0,0 +1,189 @@ +/***************************************************************************** + * inode.c + * Inode operations. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes =========================================================*/ + +#include + +/*= DOS 3.3 Includes ========================================================*/ + +#include "dos33.h" + +/***************************************************************************** + * dos33_read_inode() + *****************************************************************************/ +void dos33_read_inode(struct inode *inode) { + + SHORTCUT struct dos33_inode_info * const pi = DOS33_I(inode); + SHORTCUT struct super_block * const sb = inode->i_sb; + SHORTCUT const int dblk = (DOS33_INO_TRACK(inode->i_ino)*0x10)+ + DOS33_INO_SECTOR(inode->i_ino); + int dent=DOS33_INO_ENTRY(inode->i_ino); + struct buffer_head *bh = NULL; + struct dos33_catalog_sector *dir = NULL; + + DOS33_DEBUG("read_inodes: inode 0x%08x\n",(int)inode->i_ino); + + /* Initialize the inode structure. */ + memset(pi,0,sizeof(struct dos33_inode_info)); + + DOS33_DEBUG("DBLK %x ENTRY %x\n",dblk,dent); + + /* Read the directory block containing this entry. */ + bh = dos33_bread(sb,dblk); + if (!bh) { + DOS33_ERROR("dos33_bread() failed"); + make_bad_inode(inode); + goto out_cleanup; + } + /* Get a pointer to the directory block. */ + if (dblk%2) { + dir = (struct dos33_catalog_sector*) (bh->b_data+256); + } + else { + dir = (struct dos33_catalog_sector*)bh->b_data; + } + + /* Read a directory inode. */ + if (DOS33_INO_DENT(inode->i_ino)) { + + DOS33_DEBUG("DIRECTORY\n"); + + /* Initialize items that are common to all directories. */ + pi->i_key = inode->i_ino; + inode->i_op = &dos33_dir_inode_operations; + inode->i_fop = &dos33_dir_operations; + inode->i_mode = S_IFDIR; + + inode->i_mtime = inode->i_atime = inode->i_ctime =0; + + /* Convert the access flags. */ + inode->i_mode |=0777; + inode->i_blocks = 1; + + /* Calculate the "size" of the directory. */ + inode->i_size = inode->i_blocks << DOS33_BLOCK_SIZE_BITS; + } + + /* Read a file inode. */ + else { + + struct dos33_file_t *file_entry = NULL; + + file_entry=&dir->files[dent]; + + DOS33_DEBUG("FILE inode %x\n",(int)inode->i_ino); + + /* Set up stuff that is specific to files. */ + pi->i_filetype = file_entry->file_type; + inode->i_op = &dos33_file_inode_operations; + inode->i_fop = &dos33_file_operations; + inode->i_mapping->a_ops = &dos33_address_space_operations; + if (file_entry->file_type &0x80){ + inode->i_mode=(S_IFREG|S_IRUGO|S_IXUGO); + } + else { + inode->i_mode=(S_IFREG|S_IRWXUGO); + + } +// inode->i_mode = S_IFREG; | prodos_get_mode(ent->access,0); + /* My birthdate */ + /* a bit anachronistic, 2 years 6 months before the Disk ][ was available */ + inode->i_mtime = 256246800; + inode->i_atime = inode->i_ctime = 256246800; + + inode->i_size = le16_to_cpu(file_entry->num_sectors)*256; + inode->i_blocks = le16_to_cpu(file_entry->num_sectors)/2; + } + + /* Set up stuff that is common to every type of inode. */ + inode->i_nlink = 1; + inode->i_uid = 0; /* root uid (just for now.) */ + inode->i_gid = 0; /* root gid (just for now.) */ + +out_cleanup: + /* Release the directory block. */ + dos33_brelse(bh); + bh = NULL; + +} + +/***************************************************************************** + * prodos_write_inode() + * Write an @inode's metainformation back to disk. + *****************************************************************************/ +void dos33_write_inode(struct inode *inode,int unused) { + DOS33_DEBUG("write inodes\n"); + +#if 0 + SHORTCUT struct prodos_inode_info * const pi = PRODOS_I(inode); + struct buffer_head *dir_bh = NULL; + struct prodos_dir_entry *ent = NULL; + u32 eof = cpu_to_le32((u32)inode->i_size); + + PRODOS_INFO_1(inode->i_sb,"called for inode 0x%08x",(int)inode->i_ino); + + /* XXX: we'll eventually want to modify privileges and time stamps. */ + + /* Do nothing if the inode is not linked (free.) */ + if (!inode->i_nlink) return; + + /* Load the directory block and get pointer to this entry. */ + dir_bh = prodos_bread(inode->i_sb,PRODOS_INO_DBLK(inode->i_ino)); + if (!dir_bh) { + PRODOS_ERROR_0(inode->i_sb,"failed to read directory block"); + goto out_cleanup; + } + + /* Write a directory inode. */ + if (PRODOS_INO_DENT(inode->i_ino) == PRODOS_DENT_DIR) { + struct prodos_dir_block_first *dir = (void*)dir_bh->b_data; + struct buffer_head *parent_bh = NULL; + + /* Load and update the parent directory entry. */ + parent_bh = prodos_bread(inode->i_sb,le16_to_cpu(dir->u.dir_header.parent_block)); + if (!parent_bh) { + PRODOS_ERROR_0(inode->i_sb,"failed to read parent directory block"); + goto out_cleanup; + } + ent = &((struct prodos_dir_block*)parent_bh->b_data)->entries[dir->u.dir_header.parent_entry - 1]; + memcpy(&ent->eof,&eof,3); + ent->blocks_used = cpu_to_le16(inode->i_blocks); + mark_buffer_dirty(parent_bh); + prodos_brelse(parent_bh); + } + + /* Write a file inode. */ + else { + /* Update the directory entry. */ + ent = &((struct prodos_dir_block*)dir_bh->b_data)->entries[PRODOS_INO_DENT(inode->i_ino)]; + memcpy(&ent->eof,&eof,3); + ent->key_block = cpu_to_le16(pi->i_key); + ent->blocks_used = cpu_to_le16(inode->i_blocks); + ent->name[0] = (ent->name[0] & 0x0f) | pi->i_stype; + mark_buffer_dirty(dir_bh); + } + +out_cleanup: + if (dir_bh) prodos_brelse(dir_bh); + +#endif +} + +/***************************************************************************** + * dos33_put_inode() + *****************************************************************************/ +void dos33_put_inode(struct inode *inode) { + DOS33_DEBUG("put inodes\n"); + + DOS33_DEBUG("called on ino 0x%08x",(int)inode->i_ino); + +} + diff --git a/dos33fs-linux2.4/misc.c b/dos33fs-linux2.4/misc.c new file mode 100644 index 00000000..45f2dc73 --- /dev/null +++ b/dos33fs-linux2.4/misc.c @@ -0,0 +1,84 @@ +/***************************************************************************** + * misc.c + * Miscellaneous utility and helper functions. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes =========================================================*/ + +#include + +/*= DOS 3.3 Includes =========================================================*/ + +#include "dos33.h" + +extern int dos33_convert_filename(char *filename) { + + int i,last_char=0; + + for(i=0;i 63 + * no commas or colors allowed. Everything else goes + *****************************************************************************/ +int dos33_check_name(struct qstr *qstr) { + + int len = qstr->len; + int i; + + /* Check only up to ProDOS name length limit. */ + if (len>DOS33_MAX_NAME_LEN) len=DOS33_MAX_NAME_LEN; + + if (qstr->name[0]<64) return -EINVAL; + + for(i=0;iname[i]==',') || (qstr->name[i]==':')) return -EINVAL; + } + + return 0; +} + +/***************************************************************************** + * dos33_bread() + * Read a block from a device. + * do some magic for the 256byte->512byte transition + *****************************************************************************/ +struct buffer_head *dos33_bread(struct super_block *sb,int block_num) { + + int internal_block_num=block_num/2; + + SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); + + if (internal_block_num < 0 || internal_block_num >= psb->s_part_size) { + return NULL; + } + DOS33_DEBUG("Attempting to read block 0x%x %i\n",internal_block_num, + psb->s_part_start+internal_block_num); + return bread(sb->s_dev,psb->s_part_start + internal_block_num,DOS33_BLOCK_SIZE); +} + +/***************************************************************************** + * dos33_brelse() + * Release a block that was previously read via dos33_bread(). + *****************************************************************************/ +void dos33_brelse(struct buffer_head *bh) { + + brelse(bh); +} + + diff --git a/dos33fs-linux2.4/super.c b/dos33fs-linux2.4/super.c new file mode 100644 index 00000000..6b8d1f85 --- /dev/null +++ b/dos33fs-linux2.4/super.c @@ -0,0 +1,550 @@ +/***************************************************************************** + * super.c + * Superblock operations and basic interface to the Linux VFS. + * + * Apple II DOS 3.3 Filesystem Driver for Linux 2.4.x + * Copyright (c) 2001 Vince Weaver + * Copyright (c) 2001 Matt Jensen. + * This program is free software distributed under the terms of the GPL. + *****************************************************************************/ + +/*= Kernel Includes =========================================================*/ + +#include +#include +#include +#include +#include + +/*= DOS 3.3 Includes =======================================================*/ + +#include "dos33.h" + +/*= Forward Declarations ====================================================*/ + +/* For the Linux module interface. */ +int __init init_dos33_fs(void); +void __exit exit_dos33_fs(void); + +/* For dos33_fs_type. */ +struct super_block *dos33_read_super(struct super_block *,void *,int); + +/* For dos33_super_operations. */ +void dos33_put_super(struct super_block *); +void dos33_write_super(struct super_block *); +int dos33_statfs(struct super_block *,struct statfs *); + +/*= VFS Interface Structures ================================================*/ + +/* Linux module operations. */ +EXPORT_NO_SYMBOLS; +module_init(init_dos33_fs) +module_exit(exit_dos33_fs) + +/* DOS 3.3 driver metainformation. */ +DECLARE_FSTYPE_DEV(dos33_fs_type,"dos33",dos33_read_super); + +/* DOS 3.3 superblock operations. */ +struct super_operations dos33_super_operations = { + read_inode: dos33_read_inode, + write_inode: dos33_write_inode, + put_inode: dos33_put_inode, + put_super: dos33_put_super, +// write_super: dos33_write_super, + statfs: dos33_statfs +}; + +/* Number of one bits in a given 4-bit value. */ +static int onebits[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; + +/*= Private Functions =======================================================*/ + +/***************************************************************************** + * dos33_count_ones() + * Counts the one (set) bits in a block of @data, @size bytes long. This is + * basically the nifty algorithm that is also employed by (and was stolen + * from) the minix fs driver. + *****************************************************************************/ +int dos33_count_ones(void *data,size_t size) { + + const u32 *bytes = (u32*)data; + u32 ones = 0; + u32 tmp = 0; + + DOS33_DEBUG("Count Ones\n"); + + /* Count one bits, one nibble at a time. */ + for(size /= 4;size > 0;size--) { + tmp = *bytes++; + while (tmp) { + ones += onebits[tmp & 0x0f]; + tmp >>= 4; + } + } + + /* Return the number of ones found. */ + return ones; +} + +/***************************************************************************** + * dos33_find_first_one() + * Return the position of the first one bit in a block of @data which is @size + * bytes in length. Returns -1 if no one bits were found. + *****************************************************************************/ +int dos33_find_first_one(void *data,size_t size) { +// const u32 *bytes = (u32*)data; +// int pos = 0; +// u32 tmp = 0; + + DOS33_DEBUG("find_first_one\n"); +#if 0 + /* Search for a one bit; if found, return its position. */ + for (size /= 4;size > 0;size--,bytes++,pos += 32) { + if (*bytes) { + for (tmp = be32_to_cpu(*bytes);!(tmp & 0x80000000);tmp <<= 1,pos++); + return pos; + } + } + + /* No one bits were found. */ +#endif + return -1; +} + +/***************************************************************************** + * prodos_mark_block() + * Modify an entry in the volume bitmap. Note that the bitmap must be locked + * before calling this function. + *****************************************************************************/ +void dos33_mark_block(struct super_block *sb,int block,int free) { +// SHORTCUT struct prodos_sb_info *psb = PRODOS_SB(sb); +// SHORTCUT struct buffer_head *bh = psb->s_bm_bh[block / 4096]; +// u32 * const data = ((u32*)bh->b_data) + block % 4096 / 32; +// const int bit = cpu_to_be32(0x80000000 >> block % 4096 % 32); + DOS33_DEBUG("mark_block\n"); +#if 0 + /* Check whether we are to mark the block used or free. */ + if (free) { + /* We should only be freeing used blocks. */ + if (*data & bit) { + PRODOS_ERROR_1(sb,"block %i is already marked free",block); + return; + } + + /* Mark it free. */ + *data |= bit; + if (psb->s_bm_free >= 0) psb->s_bm_free++; + } + else { + /* We should only be allocating free blocks. */ + if (!(*data & bit)) { + PRODOS_ERROR_1(sb,"block %i is already marked used",block); + return; + } + + /* Mark it clear. */ + *data &= ~bit; + if (psb->s_bm_free >= 0) psb->s_bm_free--; + } +#endif + /* The bitmap block is now dirty. */ +// mark_buffer_dirty(bh); +} + +/***************************************************************************** + * prodos_parse_options() + * Parse the mount options string and fill prodos_sb_info fields accordingly. + *****************************************************************************/ +int dos33_parse_options(struct super_block *sb,char *opts) { +// SHORTCUT struct prodos_sb_info * const psb = PRODOS_SB(sb); +// char *arg_key = NULL; +// char *arg_value = NULL; + DOS33_DEBUG("parse_options\n"); +#if 0 + /* Step through "key[=value]" options, modifying members of psb */ + if (!opts) return 1; + for (arg_key = strtok(opts,",");arg_key;arg_key = strtok(NULL,",")) { + if ((arg_value = strchr(arg_key,'='))) *arg_value++ = 0; + if (!strcmp(arg_key,"verbose")) psb->s_flags |= PRODOS_FLAG_VERBOSE; + else if (!strcmp(arg_key,"crconv")) psb->s_flags |= PRODOS_FLAG_CONVERT_CR; + else if (!strcmp(arg_key,"case")) { + if (arg_value) { + if (!strcmp(arg_value,"lower")) + psb->s_flags |= PRODOS_FLAG_LOWERCASE; + else if (strcmp(arg_value,"asis")) goto out_invalid_value; + } + else goto out_omitted_value; + } + else if (!strcmp(arg_key,"forks")) { + if (arg_value) { + if (!strcmp(arg_value,"show")) + psb->s_flags |= PRODOS_FLAG_SHOW_FORKS; + else if (strcmp(arg_value,"hide")) goto out_invalid_value; + } + else goto out_omitted_value; + } + else if (!strcmp(arg_key,"partition") || !strcmp(arg_key,"part")) { + if (arg_value) strncpy(psb->s_part,arg_value,32); + else goto out_omitted_value; + } + else goto out_invalid_key; + } + + /* Success. */ + return ~0; + +out_omitted_value: + PRODOS_ERROR_1(sb,"option \"%s\" requires a value",arg_key); + goto out_error; +out_invalid_value: + PRODOS_ERROR_2(sb,"option \"%s\" given unrecognized value \"%s\"",arg_key,arg_value); + goto out_error; +out_invalid_key: + PRODOS_ERROR_1(sb,"unrecognized option \"%s\"",arg_key); + goto out_error; + +out_error: +#endif + return 0; +} + +/*= Exported Functions ======================================================*/ + +/***************************************************************************** + * dos33_count_free_blocks() + * Count free blocks on the volume by counting the one bits in the volume + * bitmap. + *****************************************************************************/ +int dos33_count_free_blocks(struct super_block *sb) { + + SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); + + DOS33_DEBUG("Count_free_blocks\n"); + + /* Grab the bitmap lock. */ + down(&psb->s_bm_lock); + + /* Count free blocks if they have not already been counted. */ + if (psb->s_bm_free < 0) { + psb->s_bm_free = 0; + psb->s_bm_free += (dos33_count_ones(&(psb->bitmaps[0]),0x23))/2; + } + + /* Release the bitmap lock and return free block count. */ + up(&psb->s_bm_lock); + return psb->s_bm_free; +} + +/***************************************************************************** + * prodos_alloc_block() + * Find the first free block on the volume, mark it used, and return its + * index. + *****************************************************************************/ +int dos33_alloc_block(struct super_block *sb) { +// SHORTCUT struct prodos_sb_info * const psb = PRODOS_SB(sb); + int result = -ENOSPC; +// int pos = -1; +// int i = 0; + DOS33_DEBUG("alloc_block\n"); +#if 0 + /* Grab the bitmap lock. */ + down(&psb->s_bm_lock); + + /* Fail early if we already know that all blocks are full. */ + if (!psb->s_bm_free) goto out_cleanup; + + /* Find first one bit in the bitmap, which signifies a free block. */ + for (i = 0;psb->s_bm_bh[i];i++) { + pos = prodos_find_first_one(psb->s_bm_bh[i]->b_data,PRODOS_BLOCK_SIZE); + if (pos != -1) { + pos += (i * 4096); + break; + } + } + + /* XXX: should probably cache first free block index. */ + /* If a bit was found, clear it. */ + if (pos >= 0 && pos < psb->s_part_size) { + prodos_mark_block(sb,pos,0); + result = pos; + } + +out_cleanup: + up(&psb->s_bm_lock); +#endif + return result; +} + +/***************************************************************************** + * prodos_free_block() + * Mark a block as free by setting its bit in the volume bitmap. + *****************************************************************************/ +int dos33_free_block(struct super_block *sb,u16 block) { + DOS33_DEBUG("free_blocks\n"); +#if 0 + /* Grab the bitmap lock. */ + down(&PRODOS_SB(sb)->s_bm_lock); + + /* Set the bit. */ + prodos_mark_block(sb,block,1); + + /* Release the lock and return. */ + up(&PRODOS_SB(sb)->s_bm_lock); +#endif + return 0; +} + +/***************************************************************************** + * prodos_free_tree_blocks() + * Mark all blocks in a tree (file or fork; seedling, sapling, or full tree) + * as free. + *****************************************************************************/ +int prodos_free_tree_blocks(struct super_block *sb,u8 stype,u16 key_block,u16 blocks_used) { + int result = 0; +// struct buffer_head *dind_bh = NULL; +// u16 ind_block = 0; +// struct buffer_head *ind_bh = NULL; + DOS33_DEBUG("free_tree_blocks\n"); +#if 0 + /* Grab the bitmap lock. */ + down(&PRODOS_SB(sb)->s_bm_lock); + + /* Free blocks in sapling and tree files. */ + if (stype != PRODOS_STYPE_SEEDLING) { + int i = 0; + u16 block = 0; + + /* Load the double indirect block for tree files. */ + if (stype == PRODOS_STYPE_TREE) { + dind_bh = prodos_bread(sb,key_block); + if (!dind_bh) { + result = -EIO; + goto out_cleanup; + } + + /* Allow for double indirect to be freed outside the loop. */ + blocks_used--; + } + + /* Release all data blocks and indirect blocks. */ + while (blocks_used) { + /* Get next indirect block when necessary. */ + if (!(i % 256)) { + ind_block = dind_bh ? + PRODOS_GET_INDIRECT_ENTRY(dind_bh->b_data,i / 256) : + key_block; + ind_bh = prodos_bread(sb,ind_block); + if (!ind_bh) { + result = -EIO; + goto out_cleanup; + } + } + + /* Free next data block (if it isn't sparse.) */ + block = PRODOS_GET_INDIRECT_ENTRY(ind_bh->b_data,i % 256); + if (block) { + prodos_mark_block(sb,block,1); + blocks_used--; + } + + /* Free indirect block when necessary. */ + if (!(++i % 256) || (blocks_used == 1)) { + prodos_brelse(ind_bh); + ind_bh = NULL; + prodos_mark_block(sb,ind_block,1); + blocks_used--; + } + } + + /* Free the double indirect block for tree files. */ + if (stype == PRODOS_STYPE_TREE) { + prodos_brelse(dind_bh); + dind_bh = NULL; + prodos_mark_block(sb,key_block,1); + } + } + + /* Free blocks in seedling files. */ + else prodos_mark_block(sb,key_block,1); + +out_cleanup: + if (ind_bh) prodos_brelse(ind_bh); + if (dind_bh) prodos_brelse(dind_bh); + up(&PRODOS_SB(sb)->s_bm_lock); +#endif + return result; +} + +/*= Interface Functions =====================================================*/ + +/***************************************************************************** + * dos33_read_super() + *****************************************************************************/ +struct super_block *dos33_read_super(struct super_block *sb,void *opts,int silent) { + + SHORTCUT kdev_t const dev = sb->s_dev; + struct dos33_sb_info *psb = NULL; + struct buffer_head *bh = NULL; + struct dos33_vtol *pdbf = NULL; + struct inode *root_inode = NULL; + u16 i = 0; + + DOS33_DEBUG("read_super\n"); + + /* Initialize the device and the super_block struct. */ + set_blocksize(dev,DOS33_BLOCK_SIZE); + sb->s_magic = DOS33_SUPER_MAGIC; + sb->s_op = &dos33_super_operations; + sb->s_blocksize = DOS33_BLOCK_SIZE; + sb->s_blocksize_bits = DOS33_BLOCK_SIZE_BITS; + sb->s_maxbytes = DOS33_MAX_FILE_SIZE; + sb->s_flags = MS_RDONLY | MS_NOSUID; + + /* Allocate the DOS 3.3 superblock metainformation struct. */ + psb = kmalloc(sizeof(struct dos33_sb_info),GFP_KERNEL); + if (!psb) { + DOS33_ERROR("failed to allocate memory for prodos_sb_info"); + goto out_error; + } + DOS33_SB(sb) = psb; + + /* Fill the superblock metainformation struct with default values. */ + memset(DOS33_SB(sb),0,sizeof(struct dos33_sb_info)); + psb->s_part_size = blk_size[MAJOR(dev)] ? blk_size[MAJOR(dev)][MINOR(dev)] * 2 : 0; + init_MUTEX(&psb->s_bm_lock); + init_MUTEX(&psb->s_dir_lock); + + /* Parse mount options and, if necessary, find desired partition. */ +// if (!prodos_parse_options(sb,(char*)opts)) goto out_error; + + /* Load DOS 3.3 volume directory block. */ + bh = dos33_bread(sb,DOS33_VOLUME_DIR_BLOCK); + if (!bh) { + DOS33_ERROR("failed to read volume directory block"); + goto out_error; + } + pdbf = (struct dos33_vtol*)bh->b_data; + + /* Check for DOS 3.3 footprint in the block. */ + /* this is a possibly-bogus check, replace */ +/* if (pdbf->four != 0x4) { + DOS33_ERROR("failed to find a DOS 3.3 filesystem"); + goto out_error; + } +*/ + /* Use size from the volume directory as partition size. */ + psb->s_part_size = ((pdbf->tracks_per_disk*pdbf->sectors_per_track*le16_to_cpu(pdbf->bytes_per_sector))/512); + + /* Initialize the volume bitmap stuff. */ + psb->s_bm_free = -1; + + for(i=0;itracks_per_disk;i++) { + psb->bitmaps[i]=cpu_to_le32(pdbf->bitmaps[i]); + } + + printk("dos33.o: found %ik DOS 3.%i filesystem, Volume %i\n", + psb->s_part_size/2,pdbf->dos_version,pdbf->volume); + + /* Get root inode. */ + root_inode = iget(sb,DOS33_MAKE_INO(1,0,DOS33_VOLUME_DIR_TRACK,0xf)); + sb->s_root = d_alloc_root(root_inode); + if (!sb->s_root) { + DOS33_ERROR("failed to get root inode"); + goto out_error; + } + sb->s_root->d_op = &dos33_dentry_operations; + + /* Return a copy of the 'sb' pointer on success. */ + return sb; + +out_error: + if (sb->s_root) { + iput(root_inode); + root_inode = NULL; + } + if (bh) { + dos33_brelse(bh); + bh = NULL; + } + if (psb) { + kfree(DOS33_SB(sb)); + DOS33_SB(sb) = NULL; + } + + return NULL; +} + +/***************************************************************************** + * prodos_put_super() + *****************************************************************************/ +void dos33_put_super(struct super_block *sb) { + SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); +// int i = 0; + + DOS33_DEBUG("put_super\n"); + +#if 0 /* Release volume bitmap blocks. */ + for (i = 0;psb->s_bm_bh[i];i++) { + prodos_brelse(psb->s_bm_bh[i]); + psb->s_bm_bh[i] = NULL; + } +#endif + /* Release the ProDOS super block metainformation structure. */ + kfree(psb); + +} + +/***************************************************************************** + * prodos_write_super() + ***************************************************************************** +void prodos_write_super(struct super_block *sb) { + +}*/ + +/***************************************************************************** + * prodos_statfs() + *****************************************************************************/ +int dos33_statfs(struct super_block *sb,struct statfs *statfs) { + SHORTCUT struct dos33_sb_info * const psb = DOS33_SB(sb); + DOS33_DEBUG("statfs\n"); + + /* Copy applicable information. */ + statfs->f_type = DOS33_SUPER_MAGIC; + statfs->f_bsize = sb->s_blocksize; + statfs->f_blocks = psb->s_part_size; + statfs->f_bfree = dos33_count_free_blocks(sb); + statfs->f_bavail = statfs->f_bfree; + + return 0; +} + +/*= Module Functions ========================================================*/ + +/***************************************************************************** + * init_dos33_filesystem() + *****************************************************************************/ +int __init init_dos33_fs(void) { + /* Since we are overlaying our prodos_inode_info structure over a + minix_inode_info structure, we should verify that the former is no larger + than the latter. */ + + DOS33_DEBUG("Insmodding\n"); + + if (sizeof(struct dos33_inode_info) > sizeof(struct minix_inode_info)) { + DOS33_ERROR("aborting; struct prodos_inode_info too big!"); + return -EFAULT; + } + + /* Register the filesystem. */ + return register_filesystem(&dos33_fs_type); +} + +/***************************************************************************** + * exit_dos33_filesystem() + *****************************************************************************/ +void __exit exit_dos33_fs(void) { + /* Unregister the filesystem. */ + + DOS33_DEBUG("Rmmodding\n"); + unregister_filesystem(&dos33_fs_type); +}