summaryrefslogtreecommitdiffstats
path: root/ext2ed
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:49:25 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 15:49:25 +0000
commit464df1d5e5ab1322e2dd0a7796939fff1aeefa9a (patch)
tree6a403684e0978f0287d7f0ec0e5aab1fd31a59e1 /ext2ed
parentInitial commit. (diff)
downloade2fsprogs-db58a52ab489b66cea7224323c4c6171ccc2a9dd.tar.xz
e2fsprogs-db58a52ab489b66cea7224323c4c6171ccc2a9dd.zip
Adding upstream version 1.47.0.upstream/1.47.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ext2ed')
-rw-r--r--ext2ed/COPYRIGHT346
-rw-r--r--ext2ed/Makefile.in97
-rw-r--r--ext2ed/README160
-rw-r--r--ext2ed/blockbitmap_com.c266
-rw-r--r--ext2ed/dir_com.c678
-rw-r--r--ext2ed/disk.c239
-rw-r--r--ext2ed/doc/ext2ed-design.sgml3459
-rw-r--r--ext2ed/doc/ext2fs-overview.sgml1569
-rw-r--r--ext2ed/doc/user-guide.sgml2258
-rw-r--r--ext2ed/ext2.descriptors976
-rw-r--r--ext2ed/ext2_com.c98
-rw-r--r--ext2ed/ext2ed.8.in72
-rw-r--r--ext2ed/ext2ed.conf.in79
-rw-r--r--ext2ed/ext2ed.h435
-rw-r--r--ext2ed/file_com.c565
-rw-r--r--ext2ed/general_com.c907
-rw-r--r--ext2ed/group_com.c183
-rw-r--r--ext2ed/init.c633
-rw-r--r--ext2ed/inode_com.c437
-rw-r--r--ext2ed/inodebitmap_com.c215
-rw-r--r--ext2ed/main.c548
-rw-r--r--ext2ed/super_com.c156
-rw-r--r--ext2ed/win.c232
23 files changed, 14608 insertions, 0 deletions
diff --git a/ext2ed/COPYRIGHT b/ext2ed/COPYRIGHT
new file mode 100644
index 0000000..5585eb6
--- /dev/null
+++ b/ext2ed/COPYRIGHT
@@ -0,0 +1,346 @@
+EXT2ED is hereby placed under the terms of the GNU General Public License.
+Follows the GNU license.
+
+Gadi Oxman, August 1995
+
+---------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ext2ed/Makefile.in b/ext2ed/Makefile.in
new file mode 100644
index 0000000..e4724ca
--- /dev/null
+++ b/ext2ed/Makefile.in
@@ -0,0 +1,97 @@
+#
+# Standard e2fsprogs prologue....
+#
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+top_builddir = ..
+my_dir = ext2ed
+INSTALL = @INSTALL@
+MKDIR_P = @MKDIR_P@
+
+@MCONFIG@
+
+PROGS= ext2ed
+MANPAGES= ext2ed.8
+
+DOC_DIR = $datadir/doc/ext2ed
+
+LIBS = -lncurses $(LIBEXT2FS)
+
+SRCS= $(srcdir)/main.c $(srcdir)/init.c $(srcdir)/general_com.c \
+ $(srcdir)/inode_com.c $(srcdir)/dir_com.c $(srcdir)/super_com.c \
+ $(srcdir)/disk.c $(srcdir)/win.c $(srcdir)/group_com.c \
+ $(srcdir)/file_com.c $(srcdir)/blockbitmap_com.c \
+ $(srcdir)/ext2_com.c $(srcdir)/inodebitmap_com.c
+
+OBJS= main.o init.o general_com.o inode_com.o dir_com.o super_com.o \
+ disk.o win.o group_com.o file_com.o blockbitmap_com.o ext2_com.o \
+ inodebitmap_com.o
+
+DOCS= doc/ext2ed-design.pdf doc/user-guide.pdf doc/ext2fs-overview.pdf \
+ doc/ext2ed-design.html doc/user-guide.html doc/ext2fs-overview.html
+
+.c.o:
+ $(CC) -c $(ALL_CFLAGS) $< -o $@
+ $(CHECK_CMD) $(ALL_CFLAGS) $<
+ $(CPPCHECK_CMD) $(CPPFLAGS) $<
+
+.SUFFIXES: .sgml .ps .pdf .html
+
+.sgml.ps:
+ mkdir -p doc
+ sgmltools -b ps $<
+ -mv `echo $< | sed -e 's/.sgml$$/.ps/'` $@
+
+.sgml.pdf:
+ mkdir -p doc
+ sgmltools -b pdf $<
+ -mv `echo $< | sed -e 's/.sgml$$/.pdf/'` $@
+
+.sgml.html:
+ mkdir -p doc
+ sgmltools -b onehtml $<
+ -mv `echo $< | sed -e 's/.sgml$$/.html/'` $@
+
+all:: $(PROGS) $(MANPAGES) ext2ed.conf
+
+docs: $(DOCS)
+
+ext2ed: $(OBJS)
+ $(CC) $(ALL_LDFLAGS) -o ext2ed $(OBJS) $(LIBS)
+
+ext2ed.8: $(DEP_SUBSTITUTE) $(srcdir)/ext2ed.8.in
+ $(SUBSTITUTE_UPTIME) $(srcdir)/ext2ed.8.in ext2ed.8
+
+ext2ed.conf: $(DEP_SUBSTITUTE) $(srcdir)/ext2ed.conf.in
+ $(SUBSTITUTE_UPTIME) $(srcdir)/ext2ed.conf.in ext2ed.conf
+
+clean::
+ $(RM) -f ext2ed $(OBJS) $(DOCS) ext2ed.conf ext2ed.8
+ -rmdir doc
+
+install: ext2ed
+ install -d $(root_sysconfdir)
+ install -m 755 ext2ed $(sbindir)
+ install -m 644 $(srcdir)/ext2.descriptors $(datadir)
+ install -m 644 ext2ed.conf $(root_sysconfdir)
+ install -m 644 ext2ed.8 $(man8dir)
+
+# +++ Dependency line eater +++
+#
+# Makefile dependencies follow. This must be the last section in
+# the Makefile.in file
+#
+main.o: $(srcdir)/main.c $(srcdir)/ext2ed.h
+general_com.o: $(srcdir)/general_com.c $(srcdir)/ext2ed.h
+inode_com.o: $(srcdir)/inode_com.c $(srcdir)/ext2ed.h
+dir_com.o: $(srcdir)/dir_com.c $(srcdir)/ext2ed.h
+super_com.o: $(srcdir)/super_com.c $(srcdir)/ext2ed.h
+disk.o: $(srcdir)/disk.c $(srcdir)/ext2ed.h
+win.o: $(srcdir)/win.c $(srcdir)/ext2ed.h
+group_com.o: $(srcdir)/group_com.c $(srcdir)/ext2ed.h
+file_com.o: $(srcdir)/file_com.c $(srcdir)/ext2ed.h
+blockbitmap_com.o: $(srcdir)/blockbitmap_com.c $(srcdir)/ext2ed.h
+ext2_com.o: $(srcdir)/ext2_com.c $(srcdir)/ext2ed.h
+inodebitmap_com.o: $(srcdir)/inodebitmap_com.c $(srcdir)/ext2ed.h
diff --git a/ext2ed/README b/ext2ed/README
new file mode 100644
index 0000000..1f473cf
--- /dev/null
+++ b/ext2ed/README
@@ -0,0 +1,160 @@
+ext2ed - The extended-2 filesystem editor, version e2fsprogs
+
+
+This version of ext2ed has been modified and updated by Theodore Ts'o
+for inclusion with the e2fsprogs package. It now builds with modern
+ncurses and I've fixed various small bugs, particular in the redrawing
+and resizing functions.
+
+It is currently not built by default -- the top-level e2fsprogs
+Makefile doesn't recurse into this directory. This is because ext2ed
+has a number of VERY SERIOUS shortcomings:
+
+1) It doesn't work on filesystems bigger than 2GB
+
+2) It assumes that all's the world an Intel, and assumes that
+ everything is in Intel (little-endian) byte order.
+
+Fixing either of these problems would be non-trivial, and so I at this
+point DO NOT RECOMMEND that distributions try to include this program,
+even as modified and updated in e2fsprogs. This especially goes for
+Debian, where the Debian maintainer for e2fsprogs has a history of
+making authorized changes to e2fsprogs to include programs that aren't
+supported, and then starts bugging me with bug reports. YES I KNOW.
+THIS PROGRAM HAS HORRIBLE SHORTCOMINGS. IT IS INCLUDED HERE ONLY
+BECAUSE IT'S A CONVENIENT WAY FOR (FOR E2FSPROGS DEVELOPERS ON INTEL
+MACHINES) TO GENERATE TEST CASES BY SELECTIVELY CORRUPTING
+FILESYSTEMS. I WILL NOT SUPPORT THIS PROGRAM FOR USE BY GENERAL USERS
+SO PLEASE DO NOT INCLUDE IT IN A DISTRIBUTION.
+
+I have currently disabled the use of GNU Readline in this program,
+because readline and ncurses really don't play well together at all.
+Readline assumes that it can write arbitrary text to the screen
+(especially when it tries to print out a completion list), which just
+doesn't work with ncurses. Fixing this so they would work together
+would require wholesale changes to readline.
+
+ Theodore Ts'o
+ tytso@MIT.EDU
+ May 12, 2001
+
+----------------------------------------------------------------------------
+
+ext2ed - The extended-2 filesystem editor, version 0.2
+------------------------------------------------------
+
+This is version 0.2 of ext2ed - The extended-2 filesystem editor.
+
+Modifications on Apr 5 2001
+This is minor bug fixes to ext2ed, as of April 2001.
+It supports modern ext2 version that has file type in directory structure
+and fixes the misunderstanding between ncurses and
+readline.
+
+I first applied patches from Red Hat except the one for readline that
+rewrote a readline behavior.
+
+Globals diffs between version 0.1 and 0.2 is stored in file :
+ diff-ext2ed.1.2
+
+Christian Bac <Christian.Bac@nt-evry.fr>
+
+----------------------------------------------------------------------------
+
+Documentation
+-------------
+
+ext2ed's documentation consists of three documents:
+
+1. The user's guide.
+2. Technical overview of the ext2 filesystem.
+3. The EXT2ED design and implementation document.
+
+Those documents are available in the doc directory, in linuxdoc-sgml and
+postscript formats.
+
+The documentation is also available online at:
+
+http://tochnapc2.technion.ac.il
+
+under the ext2ed section.
+
+Installation
+------------
+
+ext2ed requires the kernel sources and the readline and ncurses packages.
+Please edit the makefile if you are using an "old" version of ncurses (See the
+details below) or if gcc can't find the various header files and libraries.
+
+To install, simply issue a 'make' command to compile and a 'make install'
+command to install. I have also included an already compiled linux a.out
+binary.
+
+ext2ed and ncurses
+------------------
+
+ext2ed uses the ncurses library for terminal output. It is very important
+that ncurses will be properly installed on your system:
+
+1. Old versions of ncurses (around 1.8.5) need the OLD_NCURSES compile
+ time option in EXT2ED.
+
+ At least from 1.9.2c, this flag should not be used. I would recommend
+ upgrading the ncurses library to the newer versions.
+
+2. ncurses uses its own terminfo database rather then the termcap file.
+ It is important that the terminfo database will be found by ncurses.
+ If this is not the case, you will see on startup some lines which
+ refer to /etc/termcap. This will mean that there is a problem with
+ the terminfo database.
+
+3. Newer versions of ncurses (and the 1.3 series of the kernel) describe
+ the linux console in the entry 'linux' and not 'console', as it was
+ before. If you run ext2ed in the linux console, you should either
+ set your TERM environment variable to 'linux' or link
+ l/linux to c/console in the terminfo database.
+
+4. The precompiled binary was linked with ncurses 1.9.4 and will search
+ for the terminfo database on /usr/local/lib/terminfo. If you are
+ using it, and your 1.9.4 compatible terminfo database is not on the
+ directory above, use the TERMINFO environment variable to specify
+ an alternate location.
+
+Running ext2ed
+--------------
+
+For those of you who don't like reading lengthy articles, here are a few
+basic guidelines:
+
+1. Don't use ext2ed to change a mounted filesystem !
+
+ Using ext2ed in read-only mode on a mounted filesystem can be allowed
+ by using the configuration file option 'AllowMountedRead on'. However,
+ note that the displayed data will be unreliable.
+
+2. ext2ed currently can't handle filesystems bigger than 2 GB. I am
+ sorry for the inconvenience. This will hopefully be fixed in future
+ releases.
+
+3. Before running ext2ed, edit the configuration file
+ /var/lib/ext2ed/ext2ed.conf to suit your needs. The various
+ configuration options are documented there.
+
+4. Use the 'setdevice' command to open an ext2 filesystem.
+ e.g. 'setdevice /dev/hda1'.
+
+5. If the filesystem is an ext2 filesystem and ext2ed fails to
+ autodetect this, use the 'ForceExt2 on' configuration file option.
+
+6. The filesystem will always be opened in read-only mode. Feel free to
+ experiment, but take care with the 'enablewrite' command.
+
+-----------------------------------------------------------------------------
+
+Feel free to send me feedback with anything regarding to ext2ed.
+
+Enjoy,
+
+Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+Haifa, August 23 1995
+
diff --git a/ext2ed/blockbitmap_com.c b/ext2ed/blockbitmap_com.c
new file mode 100644
index 0000000..53e61dc
--- /dev/null
+++ b/ext2ed/blockbitmap_com.c
@@ -0,0 +1,266 @@
+/*
+
+/usr/src/ext2ed/blockbitmap_com.c
+
+A part of the extended file system 2 disk editor.
+
+-------------------------
+Handles the block bitmap.
+-------------------------
+
+This file implements the commands which are specific to the blockbitmap type.
+
+First written on: July 5 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+/*
+
+The functions in this file use the flobal structure block_bitmap_info. This structure contains the current
+position in the bitmap.
+
+*/
+
+void type_ext2_block_bitmap___entry (char *command_line)
+
+/*
+
+This function changes the current entry in the bitmap. It just changes the entry_num variable in block_bitmap_info
+and dispatches a show command to show the new entry.
+
+*/
+
+{
+ unsigned long entry_num;
+ char *ptr,buffer [80];
+
+
+
+ ptr=parse_word (command_line,buffer); /* Get the requested entry */
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");
+ refresh_command_win (); return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ entry_num=atol (buffer);
+
+
+ if (entry_num >= file_system_info.super_block.s_blocks_per_group) { /* Check if it is a valid entry number */
+
+ wprintw (command_win,"Error - Entry number out of bounds\n");
+ refresh_command_win ();return;
+ }
+
+
+
+ block_bitmap_info.entry_num=entry_num; /* If it is, just change entry_num and */
+ strcpy (buffer,"show");dispatch (buffer); /* dispatch a show command */
+}
+
+void type_ext2_block_bitmap___next (char *command_line)
+
+/*
+
+This function passes to the next entry in the bitmap. We just call the above entry command.
+
+*/
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",block_bitmap_info.entry_num+entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_block_bitmap___prev (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",block_bitmap_info.entry_num-entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_block_bitmap___allocate (char *command_line)
+
+/*
+
+This function starts allocating block from the current position. Allocating involves setting the correct bits
+in the bitmap. This function is a vector version of allocate_block below - We just run on the blocks that
+we need to allocate, and call allocate_block for each one.
+
+*/
+
+{
+ long entry_num,num=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer); /* Get the number of blocks to allocate */
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ num=atol (buffer);
+ }
+
+ entry_num=block_bitmap_info.entry_num;
+ /* Check for limits */
+ if (num > file_system_info.super_block.s_blocks_per_group-entry_num) {
+ wprintw (command_win,"Error - There aren't that much blocks in the group\n");
+ refresh_command_win ();return;
+ }
+
+ while (num) { /* And call allocate_block */
+ allocate_block (entry_num); /* for each block */
+ num--;entry_num++;
+ }
+
+ dispatch ("show"); /* Show the result */
+}
+
+void type_ext2_block_bitmap___deallocate (char *command_line)
+
+/* This is the opposite of the above function - We call deallocate_block instead of allocate_block */
+
+{
+ long entry_num,num=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ num=atol (buffer);
+ }
+
+ entry_num=block_bitmap_info.entry_num;
+ if (num > file_system_info.super_block.s_blocks_per_group-entry_num) {
+ wprintw (command_win,"Error - There aren't that much blocks in the group\n");
+ refresh_command_win ();return;
+ }
+
+ while (num) {
+ deallocate_block (entry_num);
+ num--;entry_num++;
+ }
+
+ dispatch ("show");
+}
+
+
+void allocate_block (long entry_num)
+
+/* In this function we convert the bit number into the right byte and inner bit positions. */
+
+{
+ unsigned char bit_mask=1;
+ int byte_offset,j;
+
+ byte_offset=entry_num/8; /* Find the correct byte - entry_num/8 */
+ /* The position inside the byte is entry_num %8 */
+ for (j=0;j<entry_num%8;j++)
+ bit_mask*=2; /* Generate the or mask - 1 at the right place */
+ type_data.u.buffer [byte_offset] |= bit_mask; /* And apply it */
+}
+
+void deallocate_block (long entry_num)
+
+/* This is the opposite of allocate_block above. We use an and mask instead of an or mask. */
+
+{
+ unsigned char bit_mask=1;
+ int byte_offset,j;
+
+ byte_offset=entry_num/8;
+ for (j=0;j<entry_num%8;j++)
+ bit_mask*=2;
+ bit_mask^=0xff;
+
+ type_data.u.buffer [byte_offset] &= bit_mask;
+}
+
+void type_ext2_block_bitmap___show (char *command_line)
+
+/*
+
+We show the bitmap as a series of bits, grouped at 8-bit intervals. We display 8 such groups on each line.
+The current position (as known from block_bitmap_info.entry_num) is highlighted.
+
+*/
+
+{
+ int i,j;
+ unsigned char *ptr;
+ unsigned long block_num,entry_num;
+
+ ptr=type_data.u.buffer;
+ show_pad_info.line=0;show_pad_info.max_line=-1;
+
+ wmove (show_pad,0,0);
+ for (i=0,entry_num=0;i<file_system_info.super_block.s_blocks_per_group/8;i++,ptr++) {
+ for (j=1;j<=128;j*=2) { /* j contains the and bit mask */
+ if (entry_num==block_bitmap_info.entry_num) { /* Highlight the current entry */
+ wattrset (show_pad,A_REVERSE);
+ show_pad_info.line=show_pad_info.max_line-show_pad_info.display_lines/2;
+ }
+
+ if ((*ptr) & j) /* Apply the mask */
+ wprintw (show_pad,"1");
+ else
+ wprintw (show_pad,"0");
+
+ if (entry_num==block_bitmap_info.entry_num)
+ wattrset (show_pad,A_NORMAL);
+
+ entry_num++; /* Pass to the next entry */
+ }
+ wprintw (show_pad," ");
+ if (i%8==7) { /* Display 8 groups in a row */
+ wprintw (show_pad,"\n");
+ show_pad_info.max_line++;
+ }
+ }
+
+ refresh_show_pad ();
+ show_info (); /* Show the usual information */
+
+ /* Show the group number */
+ wmove (show_win,1,0);
+ wprintw (show_win,"Block bitmap of block group %ld\n",block_bitmap_info.group_num);
+ /* Show the block number */
+
+ block_num=block_bitmap_info.entry_num+block_bitmap_info.group_num*file_system_info.super_block.s_blocks_per_group;
+ block_num+=file_system_info.super_block.s_first_data_block;
+
+ wprintw (show_win,"Status of block %ld - ",block_num); /* and the allocation status */
+ ptr=type_data.u.buffer+block_bitmap_info.entry_num/8;
+ j=1;
+ for (i=block_bitmap_info.entry_num % 8;i>0;i--)
+ j*=2;
+ if ((*ptr) & j)
+ wprintw (show_win,"Allocated\n");
+ else
+ wprintw (show_win,"Free\n");
+ refresh_show_win ();
+}
diff --git a/ext2ed/dir_com.c b/ext2ed/dir_com.c
new file mode 100644
index 0000000..67bb4b5
--- /dev/null
+++ b/ext2ed/dir_com.c
@@ -0,0 +1,678 @@
+/*
+
+/usr/src/ext2ed/dir_com.c
+
+A part of the extended file system 2 disk editor.
+
+--------------------
+Handles directories.
+--------------------
+
+This file contains the codes which allows the user to handle directories.
+
+Most of the functions use the global variable file_info (along with the special directory fields there) to save
+information and pass it between them.
+
+Since a directory is just a big file which is composed of directory entries, you will find that
+the functions here are a superset of those in the file_com.c source.
+
+We assume that the user reached here using the dir command of the inode type and not by using settype dir, so
+that init_dir_info is indeed called to gather the required information.
+
+type_data is not changed! It still contains the inode of the file - We handle the directory in our own
+variables, so that settype ext2_inode will "go back" to the inode of this directory.
+
+First written on: April 28 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+char name_search [80];
+long entry_num_search;
+
+int init_dir_info (struct struct_file_info *info_ptr)
+
+/*
+
+This function is called by the inode of the directory when the user issues the dir command from the inode.
+It is used to gather information about the inode and to reset some variables which we need in order to handle
+directories.
+
+*/
+
+{
+ struct ext2_inode *ptr;
+
+ ptr=&type_data.u.t_ext2_inode; /* type_data contains the inode */
+
+ info_ptr->inode_ptr=ptr;
+ info_ptr->inode_offset=device_offset; /* device offset contains the inode's offset */
+
+ /* Reset the current position to the start */
+
+ info_ptr->global_block_num=ptr->i_block [0];
+ info_ptr->global_block_offset=ptr->i_block [0]*file_system_info.block_size;
+ info_ptr->block_num=0;
+ info_ptr->file_offset=0;
+ /* Set the size of the directory */
+
+ info_ptr->blocks_count=(ptr->i_size+file_system_info.block_size-1)/file_system_info.block_size;
+ info_ptr->file_length=ptr->i_size;
+
+ info_ptr->level=0; /* We start using direct blocks */
+ info_ptr->display=HEX; /* This is not actually used */
+
+ info_ptr->dir_entry_num=0;info_ptr->dir_entries_count=0; /* We'll start at the first directory entry */
+ info_ptr->dir_entry_offset=0;
+
+ /* Find dir_entries_count */
+
+ info_ptr->dir_entries_count=count_dir_entries (); /* Set the total number of entries */
+
+ return (1);
+}
+
+struct struct_file_info search_dir_entries (int (*action) (struct struct_file_info *info),int *status)
+
+/*
+ This is the main function in this source file. Various actions are implemented using this basic function.
+
+ This routine runs on all directory entries in the current directory.
+ For each entry, action is called. We'll act according to the return code of action:
+
+ ABORT - Current dir entry is returned.
+ CONTINUE - Continue searching.
+ FOUND - Current dir entry is returned.
+
+ If the last entry is reached, it is returned, along with an ABORT status.
+
+ status is updated to the returned code of action.
+*/
+
+{
+ struct struct_file_info info; /* Temporary variables used to */
+ struct ext2_dir_entry_2 *dir_entry_ptr; /* contain the current search entries */
+ int return_code, next;
+
+ info=first_file_info; /* Start from the first entry - Read it */
+ low_read (info.buffer,file_system_info.block_size,info.global_block_offset);
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);
+
+ while (info.file_offset < info.file_length) { /* While we haven't reached the end */
+
+ *status=return_code=action (&info); /* Call the client function to test */
+ /* the current entry */
+ if (return_code==ABORT || return_code==FOUND)
+ return (info); /* Stop, if so asked */
+
+ /* Pass to the next entry */
+
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);
+
+ info.dir_entry_num++;
+ next = dir_entry_ptr->rec_len;
+ if (!next)
+ next = file_system_info.block_size - info.dir_entry_offset;
+ info.dir_entry_offset += next;
+ info.file_offset += next;
+
+ if (info.file_offset >= info.file_length) break;
+
+ if (info.dir_entry_offset >= file_system_info.block_size) { /* We crossed a block boundary */
+ /* Find the next block, */
+ info.block_num++;
+ info.global_block_num=file_block_to_global_block (info.block_num,&info);
+ info.global_block_offset=info.global_block_num*file_system_info.block_size;
+ info.file_offset=info.block_num*file_system_info.block_size;
+ info.dir_entry_offset=0;
+ /* read it and update the pointer */
+
+ low_read (info.buffer,file_system_info.block_size,info.global_block_offset);
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (info.buffer+info.dir_entry_offset);
+
+ }
+
+ }
+
+ *status=ABORT;return (info); /* There was no match */
+}
+
+long count_dir_entries (void)
+
+/*
+
+This function counts the number of entries in the directory. We just call search_dir_entries till the end.
+The client function is action_count, which just tell search_dir_entries to continue.
+
+*/
+
+{
+ int status;
+
+ return (search_dir_entries (&action_count,&status).dir_entry_num);
+}
+
+int action_count (struct struct_file_info *info)
+
+/*
+
+Used by count_dir_entries above - This function is called by search_dir_entries, and it tells it to continue
+searching, until we get to the last entry.
+
+*/
+
+{
+ return (CONTINUE); /* Just continue searching */
+}
+
+void type_dir___cd (char *command_line)
+
+/*
+ Changes to a directory, relative to the current directory.
+
+ This is a complicated operation, so I would repeat here the explanation from the design and
+ implementation document.
+
+1. The path is checked that it is not an absolute path (from /). If it is, we let the general cd to do the job by
+ calling directly type_ext2___cd.
+
+2. The path is divided into the nearest path and the rest of the path. For example, cd 1/2/3/4 is divided into
+ 1 and into 2/3/4.
+
+3. It is the first part of the path that we need to search for in the current directory. We search for it using
+ search_dir_entries, which accepts the action_name function as the client function.
+
+4. search_dir_entries will scan the entire entries and will call our action_name function for each entry.
+ In action_name, the required name will be checked against the name of the current entry, and FOUND will be
+ returned when a match occurs.
+
+5. If the required entry is found, we dispatch a remember command to insert the current inode (remember that
+ type_data is still intact and contains the inode of the current directory) into the object memory.
+ This is required to easily support symbolic links - If we find later that the inode pointed by the entry is
+ actually a symbolic link, we'll need to return to this point, and the above inode doesn't have (and can't have,
+ because of hard links) the information necessary to "move back".
+
+6. We then dispatch a followinode command to reach the inode pointed by the required entry. This command will
+ automatically change the type to ext2_inode - We are now at an inode, and all the inode commands are available.
+
+7. We check the inode's type to see if it is a directory. If it is, we dispatch a dir command to "enter the directory",
+ and recursively call ourself (The type is dir again) by dispatching a cd command, with the rest of the path
+ as an argument.
+
+8. If the inode's type is a symbolic link (only fast symbolic link were meanwhile implemented. I guess this is
+ typically the case.), we note the path it is pointing at, the saved inode is recalled, we dispatch dir to
+ get back to the original directory, and we call ourself again with the link path/rest of the path argument.
+
+9. In any other case, we just stop at the resulting inode.
+
+*/
+
+{
+ int status;
+ char *ptr,full_dir_name [500],dir_name [500],temp [500],temp2 [500];
+ struct struct_file_info info;
+ struct ext2_dir_entry_2 *dir_entry_ptr;
+
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);
+
+ ptr=parse_word (command_line,dir_name);
+
+ if (*ptr==0) { /* cd alone will enter the highlighted directory */
+ strncpy (full_dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len);
+ full_dir_name [dir_entry_ptr->name_len]=0;
+ }
+ else
+ ptr=parse_word (ptr,full_dir_name);
+
+ ptr=strchr (full_dir_name,'/');
+
+ if (ptr==full_dir_name) { /* Pathname is from root - Let the general cd do the job */
+ sprintf (temp,"cd %s",full_dir_name);type_ext2___cd (temp);return;
+ }
+
+ if (ptr==NULL) {
+ strcpy (dir_name,full_dir_name);
+ full_dir_name [0]=0;
+ }
+
+ else {
+ strncpy (dir_name,full_dir_name,ptr-full_dir_name);
+ dir_name [ptr-full_dir_name]=0;
+ strcpy (full_dir_name,++ptr);
+ }
+ /* dir_name contains the current entry, while */
+ /* full_dir_name contains the rest */
+
+ strcpy (name_search,dir_name); /* name_search is used to hold the required entry name */
+
+ if (dir_entry_ptr->name_len != strlen (dir_name) ||
+ strncmp (dir_name,dir_entry_ptr->name,dir_entry_ptr->name_len)!=0)
+ info=search_dir_entries (&action_name,&status); /* Search for the entry. Answer in info. */
+ else {
+ status=FOUND;info=file_info;
+ }
+
+ if (status==FOUND) { /* If found */
+ file_info=info; /* Switch to it, by setting the global file_info */
+ dispatch ("remember internal_variable"); /* Move the inode into the objects memory */
+
+ dispatch ("followinode"); /* Go to the inode pointed by this directory entry */
+
+ if (S_ISLNK (type_data.u.t_ext2_inode.i_mode)) {/* Symbolic link ? */
+
+ if (type_data.u.t_ext2_inode.i_size > 60) { /* I'm lazy, I guess :-) */
+ wprintw (command_win,"Error - Sorry, Only fast symbolic link following is currently supported\n");
+ refresh_command_win ();
+ return;
+ }
+ /* Get the pointed name and append the previous path */
+
+ strcpy (temp2,(unsigned char *) &type_data.u.t_ext2_inode.i_block);
+ strcat (temp2,"/");
+ strcat (temp2,full_dir_name);
+
+ dispatch ("recall internal_variable"); /* Return to the original inode */
+ dispatch ("dir"); /* and to the directory */
+
+ sprintf (temp,"cd %s",temp2); /* And continue from there by dispatching a cd command */
+ dispatch (temp); /* (which can call ourself or the general cd) */
+
+ return;
+ }
+
+ if (S_ISDIR (type_data.u.t_ext2_inode.i_mode)) { /* Is it an inode of a directory ? */
+
+ dispatch ("dir"); /* Yes - Pass to the pointed directory */
+
+ if (full_dir_name [0] != 0) { /* And call ourself with the rest of the pathname */
+ sprintf (temp,"cd %s",full_dir_name);
+ dispatch (temp);
+ }
+
+ return;
+ }
+
+ else { /* If we can't continue from here, we'll just stop */
+ wprintw (command_win,"Can\'t continue - Stopping at last inode\n");refresh_command_win ();
+ return;
+ }
+ }
+
+ wprintw (command_win,"Error - Directory entry %s not found.\n",dir_name); /* Hmm, an invalid path somewhere */
+ refresh_command_win ();
+}
+
+int action_name (struct struct_file_info *info)
+
+/*
+
+Compares the current search entry name (somewhere inside info) with the required name (in name_search).
+Returns FOUND if found, or CONTINUE if not found.
+
+*/
+
+{
+ struct ext2_dir_entry_2 *dir_entry_ptr;
+
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset);
+
+ if (dir_entry_ptr->name_len != strlen (name_search))
+ return (CONTINUE);
+
+ if (strncmp (dir_entry_ptr->name,name_search,dir_entry_ptr->name_len)==0)
+ return (FOUND);
+
+ return (CONTINUE);
+}
+
+void type_dir___entry (char *command_line)
+
+/*
+
+Selects a directory entry according to its number.
+search_dir_entries is used along with action_entry_num, in the same fashion as the previous usage of search_dir_entries.
+
+*/
+
+{
+ int status;
+ struct struct_file_info info;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument_not_specified\n");wrefresh (command_win);
+ return;
+ }
+ ptr=parse_word (ptr,buffer);
+ entry_num_search=atol (buffer);
+
+ if (entry_num_search < 0 || entry_num_search >= file_info.dir_entries_count) {
+ wprintw (command_win,"Error - Entry number out of range\n");wrefresh (command_win);
+ return;
+ }
+
+ info=search_dir_entries (&action_entry_num,&status);
+ if (status==FOUND) {
+ file_info=info;
+ dispatch ("show");
+ return;
+ }
+#ifdef DEBUG
+ internal_error ("dir_com","type_dir___entry","According to our gathered data, we should have found this entry");
+#endif
+}
+
+int action_entry_num (struct struct_file_info *info)
+
+/*
+
+Used by the above function. Just compares the current number (in info) with the required one.
+
+*/
+
+{
+ if (info->dir_entry_num == entry_num_search)
+ return (FOUND);
+
+ return (CONTINUE);
+}
+
+void type_dir___followinode (char *command_line)
+
+/*
+
+Here we pass to the inode pointed by the current entry.
+It involves computing the device offset of the inode and using directly the setoffset and settype commands.
+
+*/
+{
+ long inode_offset;
+ char buffer [80];
+
+ struct ext2_dir_entry_2 *dir_entry_ptr;
+
+ low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);
+
+ inode_offset=inode_num_to_inode_offset (dir_entry_ptr->inode); /* Compute the inode's offset */
+ sprintf (buffer,"setoffset %ld",inode_offset);dispatch (buffer); /* Move to it */
+ sprintf (buffer,"settype ext2_inode");dispatch (buffer); /* and set the type to an inode */
+}
+
+void type_dir___inode (char *command_line)
+
+/*
+
+Returns to the parent inode of the current directory.
+This is trivial, as we type_data is still intact and contains the parent inode !
+
+*/
+
+{
+ dispatch ("settype ext2_inode");
+}
+
+
+void type_dir___show (char *command_line)
+
+/*
+
+We use search_dir_entries to run on all the entries. Each time, action_show will be called to show one entry.
+
+*/
+
+{
+ int status;
+
+ wmove (show_pad,0,0);
+ show_pad_info.max_line=-1;
+
+ search_dir_entries (&action_show,&status);
+ show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2;
+ refresh_show_pad ();
+ show_dir_status ();
+}
+
+int action_show (struct struct_file_info *info)
+
+/*
+
+Show the current search entry (info) in one line. If the entry happens to be the current edited entry, it is highlighted.
+
+*/
+
+{
+ unsigned char temp [80];
+ struct ext2_dir_entry_2 *dir_entry_ptr;
+
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (info->buffer+info->dir_entry_offset);
+
+ if (info->dir_entry_num == file_info.dir_entry_num) /* Highlight the current entry */
+ wattrset (show_pad,A_REVERSE);
+
+ strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len); /* The name is not terminated */
+ temp [dir_entry_ptr->name_len]=0;
+ if (dir_entry_ptr->name_len > (COLS - 55) && COLS > 55)
+ temp [COLS-55]=0;
+ wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n", /* Display the various fields */
+ dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);
+
+ show_pad_info.max_line++;
+
+ if (info->dir_entry_num == file_info.dir_entry_num)
+ wattrset (show_pad,A_NORMAL);
+
+ return (CONTINUE); /* And pass to the next */
+}
+
+void type_dir___next (char *command_line)
+
+/*
+
+This function moves to the next directory entry. It just uses the current information and the entry command.
+
+*/
+
+{
+ int offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",file_info.dir_entry_num+offset);dispatch (buffer);
+
+}
+
+void type_dir___prev (char *command_line)
+
+{
+ int offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",file_info.dir_entry_num-offset);dispatch (buffer);
+}
+
+void show_dir_status (void)
+
+/*
+
+Various statistics about the directory.
+
+*/
+
+{
+ long inode_num;
+
+ wmove (show_win,0,0);
+ wprintw (show_win,"Directory listing. Block %ld. ",file_info.global_block_num);
+ wprintw (show_win,"Directory entry %ld of %ld.\n",file_info.dir_entry_num,file_info.dir_entries_count-1);
+ wprintw (show_win,"Directory Offset %ld of %ld. ",file_info.file_offset,file_info.file_length-1);
+
+ inode_num=inode_offset_to_inode_num (file_info.inode_offset);
+ wprintw (show_win,"File inode %ld. Indirection level %ld.\n",inode_num,file_info.level);
+
+ refresh_show_win ();
+}
+
+void type_dir___remember (char *command_line)
+
+/*
+
+This is overridden here because we don't remember a directory - It is too complicated. Instead, we remember the
+inode of the current directory.
+
+*/
+
+{
+ int found=0;
+ long entry_num;
+ char *ptr,buffer [80];
+ struct struct_descriptor *descriptor_ptr;
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");wrefresh (command_win);
+ return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+ entry_num=remember_lifo.entries_count++;
+ if (entry_num>REMEMBER_COUNT-1) {
+ entry_num=0;
+ remember_lifo.entries_count--;
+ }
+
+ descriptor_ptr=first_type;
+ while (descriptor_ptr!=NULL && !found) {
+ if (strcmp (descriptor_ptr->name,"ext2_inode")==0)
+ found=1;
+ else
+ descriptor_ptr=descriptor_ptr->next;
+ }
+
+
+ remember_lifo.offset [entry_num]=device_offset;
+ remember_lifo.type [entry_num]=descriptor_ptr;
+ strcpy (remember_lifo.name [entry_num],buffer);
+
+ wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",descriptor_ptr->name,device_offset,buffer);
+ wrefresh (command_win);
+}
+
+void type_dir___set (char *command_line)
+
+/*
+
+Since the dir object doesn't have variables, we provide the impression that it has here. ext2_dir_entry was not used
+because it is of variable length.
+
+*/
+
+{
+ int found=0;
+ unsigned char *ptr,buffer [80],variable [80],value [80],temp [80];
+ struct ext2_dir_entry_2 *dir_entry_ptr;
+
+ dir_entry_ptr=(struct ext2_dir_entry_2 *) (file_info.buffer+file_info.dir_entry_offset);
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Missing arguments\n");refresh_command_win ();
+ return;
+ }
+ parse_word (ptr,buffer);
+ ptr=strchr (buffer,'=');
+ if (ptr==NULL) {
+ wprintw (command_win,"Error - Bad syntax\n");refresh_command_win ();return;
+ }
+ strncpy (variable,buffer,ptr-buffer);variable [ptr-buffer]=0;
+ strcpy (value,++ptr);
+
+ if (strcasecmp ("inode",variable)==0) {
+ found=1;
+ dir_entry_ptr->inode=atol (value);
+ wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->inode);refresh_command_win ();
+
+ }
+
+ if (strcasecmp ("rec_len",variable)==0) {
+ found=1;
+ dir_entry_ptr->rec_len=(unsigned int) atol (value);
+ wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->rec_len);refresh_command_win ();
+
+ }
+
+ if (strcasecmp ("name_len",variable)==0) {
+ found=1;
+ dir_entry_ptr->name_len=(unsigned int) atol (value);
+ wprintw (command_win,"Variable %s set to %lu\n",variable,dir_entry_ptr->name_len);refresh_command_win ();
+
+ }
+
+ if (strcasecmp ("name",variable)==0) {
+ found=1;
+ if (strlen (value) > dir_entry_ptr->name_len) {
+ wprintw (command_win,"Error - Length of name greater then name_len\n");
+ refresh_command_win ();return;
+ }
+ strncpy (dir_entry_ptr->name,value,strlen (value));
+ wprintw (command_win,"Variable %s set to %s\n",variable,value);refresh_command_win ();
+
+ }
+
+ if (found) {
+ wattrset (show_pad,A_REVERSE);
+ strncpy (temp,dir_entry_ptr->name,dir_entry_ptr->name_len);
+ temp [dir_entry_ptr->name_len]=0;
+ wmove (show_pad,file_info.dir_entry_num,0);
+ wprintw (show_pad,"inode = %-8lu rec_len = %-4lu name_len = %-3lu name = %s\n",
+ dir_entry_ptr->inode,dir_entry_ptr->rec_len,dir_entry_ptr->name_len,temp);
+ wattrset (show_pad,A_NORMAL);
+ show_pad_info.line=file_info.dir_entry_num-show_pad_info.display_lines/2;
+ refresh_show_pad ();
+ show_dir_status ();
+ }
+
+ else {
+ wprintw (command_win,"Error - Variable %s not found\n",variable);
+ refresh_command_win ();
+ }
+
+}
+
+void type_dir___writedata (char *command_line)
+
+/*
+
+We need to override this since the data is not in type_data. Instead, we have to write the buffer which corresponds
+to the current block.
+
+*/
+
+{
+ low_write (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+ return;
+}
diff --git a/ext2ed/disk.c b/ext2ed/disk.c
new file mode 100644
index 0000000..4612d00
--- /dev/null
+++ b/ext2ed/disk.c
@@ -0,0 +1,239 @@
+/*
+
+/usr/src/ext2ed/disk.c
+
+A part of the extended file system 2 disk editor.
+
+-------------------------------------------------
+The filesystem's disk activity pass through here.
+-------------------------------------------------
+
+This file is acting as a filter - Before we pass an actual read or write request to the operating system, we
+double check the various permissions and possible errors here.
+
+The major update which needs to be done here is switching to the use of the llseek system call, so that we will
+be able to support ext2 filesystems up to 4 TB. Currently, due to the standard fseek usage, we can't handle
+filesystems bigger than 4 GB. The limit is actually 2 GB because I used long rather than unsigned long long at too
+many places in the program. To conclude - This upgrade needs to be done carefully; There are many places to change.
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2ed.h"
+
+int write_access;
+
+int low_read (unsigned char *buffer,unsigned long length,unsigned long offset)
+
+/*
+
+This function is used when we need to read something from the filesystem.
+
+*/
+
+{
+
+#ifdef DEBUG
+
+ char temp [80];
+
+ if (device_handle==NULL) { /* Check that a device is indeed open */
+ internal_error ("No device opened yet read requested","disk","low_read");
+ return (0);
+ }
+ if (offset > file_system_info.file_system_size) { /* Check that the offset is within limits */
+ sprintf (temp,"Seek offset %ld is out of range",offset);
+ internal_error (temp,"disk","low_read");
+ return (0);
+ }
+
+#endif
+
+ if ( (fseek (device_handle,offset,SEEK_SET))==-1) { /* Seek to the required offset */
+ wprintw (command_win,"Error - Failed to seek to offset %ld in device %s\n",offset,device_name);
+ refresh_command_win ();
+ return (0);
+ };
+
+ if ( (fread (buffer,1,length,device_handle))==-1) { /* And do the actual reading */
+ wprintw (command_win,"Error - Failed to read from offset %ld in device %s\n",offset,device_name);
+ refresh_command_win ();return (0);
+ };
+
+ return (1);
+}
+
+int low_write (unsigned char *buffer,unsigned long length,unsigned long offset)
+
+/*
+
+This is used to change something in the filesystem.
+write_access is checked to see if we are allowed to do the actual writing.
+As a double safety measure, AllowChanges is rechecked here.
+If logging is enabled, we log the change before writing it to the device.
+
+*/
+{
+ char temp [80];
+
+ if (!write_access) {
+ wprintw (command_win,"Error - Write access not available (use enablewrite)\n");
+ return (0);
+ }
+
+#ifdef DEBUG
+
+ if (!AllowChanges) {
+ internal_error ("AllowChanges=0 yet enablewrite succeeded","disk","low_write");
+ return (0);
+ }
+
+ if (device_handle==NULL) {
+ internal_error ("No device opened yet read requested","disk","low_write");
+ return (0);
+ }
+
+ if (offset > file_system_info.file_system_size) {
+ sprintf (temp,"Seek offset %ld is out of range",offset);
+ internal_error (temp,"disk","low_write");
+ return (0);
+ }
+
+#endif
+
+ if (LogChanges)
+ if (!log_changes (buffer,length,offset))
+ return (0);
+
+ if ( (fseek (device_handle,offset,SEEK_SET))==-1) {
+ wprintw (command_win,"Error - Failed to seek to offset %ld in device %s\n",offset,device_name);
+ refresh_command_win ();return (0);
+ };
+
+
+ if ( (fwrite (buffer,1,length,device_handle))==-1) {
+ wprintw (command_win,"Error - Failed to write to offset %ld in device %s\n",offset,device_name);
+ refresh_command_win ();return (0);
+ };
+
+ wprintw (command_win,"Data written");refresh_command_win ();
+ return (1);
+}
+
+int log_changes (unsigned char *buffer,unsigned long length,unsigned long offset)
+
+/*
+
+Log the change in a primitive form - An hex dump of the data before the change and after the change.
+The hex bytes are converted to text, so that they will be readable with a standard text editor.
+
+*/
+
+{
+ unsigned char *original;
+
+ int i;
+ time_t current_time;
+ FILE *fp;
+
+ if ((fp=fopen (LogFile,"a+"))==NULL) {
+ wprintw (command_win,"Error - Unable to open log file %s\n",LogFile);
+ refresh_command_win ();return (0);
+ };
+
+ current_time=time (NULL);
+
+ fprintf (fp,"\n----- EXT2ED log begin -----\n\n");
+ fprintf (fp,"Time: %s\nDevice: %s\n",ctime ((time_t *) &current_time),device_name);
+ fprintf (fp,"Offset: %lu\nLength: %lu\n",offset,length);
+
+ original=(unsigned char *) malloc (length*sizeof (unsigned char));
+
+ if (original==NULL) {
+ wprintw (command_win,"Fatal error - Can\'t allocate %lu bytes!");
+ refresh_command_win ();fclose (fp);return (0);
+ }
+
+ if (!low_read (original,length,offset)) {
+ fclose (fp);return (0);
+ }
+
+ fprintf (fp,"\nOriginal data:\n\n");
+
+ for (i=0;i<length;i++) {
+ if (i%16==0 && i!=0) fprintf (fp,"\n");
+ fprintf (fp,"%02x ",original [i]);
+ }
+
+ fprintf (fp,"\n\nNew data:\n\n");
+
+ for (i=0;i<length;i++) {
+ if (i%16==0 && i!=0) fprintf (fp,"\n");
+ fprintf (fp,"%02x ",buffer [i]);
+ }
+
+ fprintf (fp,"\n----- EXT2ED log end -----\n");
+
+ fclose (fp);
+ return (1);
+}
+
+int load_type_data (void)
+
+/*
+
+Just read from the current position into type data.
+
+*/
+
+{
+ if (device_handle==NULL) {
+ printf ("Error - No device opened\n");
+ return (0);
+ }
+
+ if (device_offset==-1) {
+ printf ("Error - No offset set\n");
+ return (0);
+ }
+
+ if (low_read (type_data.u.buffer,EXT2_MAX_BLOCK_SIZE,device_offset)==0)
+ return (0);
+
+ if (current_type!=NULL)
+ if (strcmp (current_type->name,"ext2_dir_entry")==0)
+ current_type->length=type_data.u.t_ext2_dir_entry.rec_len;
+
+ return (1);
+}
+
+int write_type_data (void)
+
+{
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");
+ refresh_command_win ();
+ return (0);
+ }
+
+ if (device_offset==-1) {
+ wprintw (command_win,"Error - No offset set\n");
+ refresh_command_win ();
+ return (0);
+ }
+
+ if (low_write (type_data.u.buffer,file_system_info.block_size,device_offset)==0)
+ return (0);
+
+ return (1);
+}
+
diff --git a/ext2ed/doc/ext2ed-design.sgml b/ext2ed/doc/ext2ed-design.sgml
new file mode 100644
index 0000000..b2cab37
--- /dev/null
+++ b/ext2ed/doc/ext2ed-design.sgml
@@ -0,0 +1,3459 @@
+<!DOCTYPE Article PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
+
+<Article>
+
+<ArtHeader>
+
+<Title>EXT2ED - The Extended-2 filesystem editor - Design and implementation</Title>
+<AUTHOR
+>
+<FirstName>Programmed by Gadi Oxman, with the guide of Avner Lottem</FirstName>
+</AUTHOR
+>
+<PubDate>v0.1, August 3 1995</PubDate>
+
+</ArtHeader>
+
+<Sect1>
+<Title>About EXT2ED documentation</Title>
+
+<Para>
+The EXT2ED documentation consists of three parts:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The ext2 filesystem overview.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The EXT2ED user's guide.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The EXT2ED design and implementation.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+This document is not the user's guide. If you just intend to use EXT2ED, you
+may not want to read it.
+</Para>
+
+<Para>
+However, if you intend to browse and modify the source code, this document is
+for you.
+</Para>
+
+<Para>
+In any case, If you intend to read this article, I strongly suggest that you
+will be familiar with the material presented in the other two articles as well.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Preface</Title>
+
+<Para>
+In this document I will try to explain how EXT2ED is constructed.
+At this time of writing, the initial version is finished and ready
+for distribution; It is fully functional. However, this was not always the
+case.
+</Para>
+
+<Para>
+At first, I didn't know much about Unix, much less about Unix filesystems,
+and even less about Linux and the extended-2 filesystem. While working
+on this project, I gradually acquired knowledge about all of the above
+subjects. I can think of two ways in which I could have made my project:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ The "Engineer" way
+
+Learn the subject thoroughly before I get to the programming itself.
+Then, I could easily see the entire picture and select the best
+course of action, taking all the factors into account.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The "Explorer - Progressive" way.
+
+Jump immediately into the cold water - Start programming and
+learning the material in parallel.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+<Para>
+I guess that the above dilemma is typical and appears all through science and
+technology.
+</Para>
+
+<Para>
+However, I didn't have the luxury of choice when I started my project -
+Linux is a relatively new (and great!) operating system. The extended-2
+filesystem is even newer - Its first release lies somewhere in 1993 - Only
+passed two years until I started working on my project.
+</Para>
+
+<Para>
+The situation I found myself at the beginning was that I didn't have a fully
+detailed document which describes the ext2 filesystem. In fact, I didn't
+have any ext2 document at all. When I asked Avner about documentation, he
+suggested two references:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ A general Unix book - THE DESIGN OF THE UNIX OPERATING SYSTEM, by
+Maurice J. Bach.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The kernel sources.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+I read the relevant parts of the book before I started my project - It is a
+bit old now, but the principles are still the same. However, I needed
+more than just the principles.
+</Para>
+
+<Para>
+The kernel sources are a rare bonus! You don't get everyday the full
+sources of the operating system. There is so much that can be learned from
+them, and it is the ultimate source - The exact answer how the kernel
+works is there, with all the fine details. At the first week I started to
+look at random at the relevant parts of the sources. However, it is difficult
+to understand the global picture from direct reading of over one hundred
+page sources. Then, I started to do some programming. I didn't know
+yet what I was looking for, and I started to work on the project like a kid
+who starts to build a large puzzle.
+</Para>
+
+<Para>
+However, this was exactly the interesting part! It is frustrating to know
+it all from advance - I think that the discovery itself, bit by bit, is the
+key to a true learning and understanding.
+</Para>
+
+<Para>
+Now, in this document, I am trying to present the subject. Even though I
+developed EXT2ED progressively, I now can see the entire subject much
+brighter than I did before, and though I do have the option of presenting it
+only in the "engineer" way. However, I will not do that.
+</Para>
+
+<Para>
+My presentation will be mixed - Sometimes I will present a subject with an
+incremental perspective, and sometimes from a "top down" view. I'll leave
+you to decide if my presentation choice was wise :-)
+</Para>
+
+<Para>
+In addition, you'll notice that the sections tend to get shorter as we get
+closer to the end. The reason is simply that I started to feel that I was
+repeating myself so I decided to present only the new ideas.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Getting started ...</Title>
+
+<Para>
+Getting started is almost always the most difficult task. Once you get
+started, things start "running" ...
+</Para>
+
+<Sect2>
+<Title>Before the actual programming</Title>
+
+<Para>
+From mine talking with Avner, I understood that Linux, like any other Unix
+system, provides accesses to the entire disk as though it were a general
+file - Accessing the device. It is surely a nice idea. Avner suggested two
+ways of action:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Opening the device like a regular file in the user space.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Constructing a device driver which will run in the kernel space and
+provide hooks for the user space program. The advantage is that it
+will be a part of the kernel, and would be able to use the ext2
+kernel functions to do some of the work.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+I chose the first way. I think that the basic reason was simplicity - Learning
+the ext2 filesystem was complicated enough, and adding to it the task of
+learning how to program in the kernel space was too much. I still don't know
+how to program a device driver, and this is perhaps the bad part, but
+concerning the project in a back-perspective, I think that the first way is
+superior to the second; Ironically, because of the very reason I chose it -
+Simplicity. EXT2ED can now run entirely in the user space (which I think is
+a point in favor, because it doesn't require the user to recompile its
+kernel), and the entire hard work is mine, which fitted nicely into the
+learning experience - I didn't use other code to do the job (aside from
+looking at the sources, of-course).
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Jumping into the cold water</Title>
+
+<Para>
+I didn't know almost anything of the structure of the ext2 filesystem.
+Reading the sources was not enough - I needed to experiment. However, a tool
+for experiments in the ext2 filesystem was exactly my project! - Kind of a
+paradox.
+</Para>
+
+<Para>
+I started immediately with constructing a simple <Literal remap="tt">hex editor</Literal> - It would
+open the device as a regular file, provide means of moving inside the
+filesystem with a simple <Literal remap="tt">offset</Literal> method, and just show a
+<Literal remap="tt"> hex dump</Literal> of the contents at this point. Programming this was trivially
+simple of-course. At this point, the user-interface didn't matter to me - I
+wanted a fast way to interact. As a result, I chose a simple command line
+parser. Of course, there where no windows at this point.
+</Para>
+
+<Para>
+A hex editor is nice, but is not enough. It indeed enabled me to see each part
+of the filesystem, but the format of the viewed data was difficult to
+analyze. I wanted to see the data in a more intuitive way.
+</Para>
+
+<Para>
+At this point of time, the most helpful file in the sources was the ext2
+main include file - <Literal remap="tt">/usr/include/linux/ext2&lowbar;fs.h</Literal>. Among its contents
+there were various structures which I assumed they are disk images - Appear
+exactly like that on the disk.
+</Para>
+
+<Para>
+I wanted a <Literal remap="tt">quick</Literal> way to get going. I didn't have the patience to learn
+each of the structures use in the code. Rather, I wanted to see them in action,
+so that I could explore the connections between them - Test my assumptions,
+and reach other assumptions.
+</Para>
+
+<Para>
+So after the <Literal remap="tt">hex editor</Literal>, EXT2ED progressed into a tool which has some
+elements of a compiler. I programmed EXT2ED to <Literal remap="tt">dynamically read the kernel
+ext2 main include file in run time</Literal>, and process the information. The goal
+was to <Literal remap="tt">imply a structure-definition on the current offset at the
+filesystem</Literal>. EXT2ED would then display the structure as a list of its
+variables names and contents, instead of a meaningless hex dump.
+</Para>
+
+<Para>
+The format of the include file is not very complicated - The structures
+are mostly <Literal remap="tt">flat</Literal> - Didn't contain a lot of recursive structure; Only a
+global structure definition, and some variables. There were cases of
+structures inside structures, I treated them in a somewhat non-elegant way - I
+made all the structures flat, and expanded the arrays. As a result, the parser
+was very simple. After all, this was not an exercise in compiling, and I
+wanted to quickly get some results.
+</Para>
+
+<Para>
+To handle the task, I constructed the <Literal remap="tt">struct&lowbar;descriptor</Literal> structure.
+Each <Literal remap="tt">struct&lowbar;descriptor instance</Literal> contained information which is needed
+in order to format a block of data according to the C structure contained in
+the kernel source. The information contained:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The descriptor name, used to reference to the structure in EXT2ED.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The name of each variable.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The relative offset of the each variable in the data block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The length, in bytes, of each variable.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+Since I didn't want to limit the number of structures, I chose a simple
+double linked list to store the information. One variable contained the
+<Literal remap="tt">current structure type</Literal> - A pointer to the relevant
+<Literal remap="tt">struct&lowbar;descriptor</Literal>.
+</Para>
+
+<Para>
+Now EXT2ED contained basically three command line operations:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ setdevice
+
+Used to open a device for reading only. Write access was postponed
+to a very advanced state in the project, simply because I didn't
+know a thing of the filesystem structure, and I believed that
+making actual changes would do nothing but damage :-)
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ setoffset
+
+Used to move in the device.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ settype
+
+Used to imply a structure definition on the current place.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ show
+
+Used to display the data. It displayed the data in a simple hex dump
+if there was no type set, or in a nice formatted way - As a list of
+the variable contents, if there was.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Command line analyzing was primitive back then - A simple switch, as far as
+I can remember - Nothing alike the current flow control, but it was enough
+at the time.
+</Para>
+
+<Para>
+At the end, I had something to start working with. It knew to format many
+structures - None of which I understood - and provided me, without too much
+work, something to start with.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Starting to explore</Title>
+
+<Para>
+With the above tool in my pocket, I started to explore the ext2 filesystem
+structure. From the brief reading in Bach's book, I got familiar to some
+basic concepts - The <Literal remap="tt">superblock</Literal>, for example. It seems that the
+superblock is an important part of the filesystem. I decided to start
+exploring with that.
+</Para>
+
+<Para>
+I realized that the superblock should be at a fixed location in the
+filesystem - Probably near the beginning. There can be no other way -
+The kernel should start at some place to find it. A brief looking in
+the kernel sources revealed that the superblock is signed by a special
+signature - A <Literal remap="tt">magic number</Literal> - EXT2&lowbar;SUPER&lowbar;MAGIC (0xEF53 - EF probably
+stands for Extended Filesystem). I quickly found the superblock at the
+fixed offset 1024 in the filesystem - The <Literal remap="tt">s&lowbar;magic</Literal> variable in the
+superblock was set exactly to the above value.
+</Para>
+
+<Para>
+It seems that starting with the <Literal remap="tt">superblock</Literal> was a good bet - Just from
+the list of variables, one can learn a lot. I didn't understand all of them
+at the time, but it seemed that the following keywords were repeating themselves
+in various variables:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ block
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ inode
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ group
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+At this point, I started to explore the block groups. I will not detail here
+the technical design of the ext2 filesystem. I have written a special
+article which explains just that, in the "engineering" way. Please refer to it
+if you feel that you are lacking knowledge in the structure of the ext2
+filesystem.
+</Para>
+
+<Para>
+I was exploring the filesystem in this way for some time, along with reading
+the sources. This lead naturally to the next step.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Object specific commands</Title>
+
+<Para>
+What has become clear is that the above way of exploring is not powerful
+enough - I found myself doing various calculations manually in order to pass
+between related structures. I needed to replace some tasks with an automated
+procedure.
+</Para>
+
+<Para>
+In addition, it also became clear that (of-course) each key object in the
+filesystem has its special place in regard to the overall ext2 filesystem
+design, and needs a <Literal remap="tt">fine tuned handling</Literal>. It is at this point that the
+structure definitions <Literal remap="tt">came to life</Literal> - They became <Literal remap="tt">object
+definitions</Literal>, making EXT2ED <Literal remap="tt">object oriented</Literal>.
+</Para>
+
+<Para>
+The actual meaning of the breathtaking words above, is that each structure
+now had a list of <Literal remap="tt">private commands</Literal>, which ended up in
+<Literal remap="tt">calling special fine-tuned C functions</Literal>. This approach was
+found to be very powerful and is <Literal remap="tt">the heart of EXT2ED even now</Literal>.
+</Para>
+
+<Para>
+In order to implement the above concepts, I added the structure
+<Literal remap="tt">struct&lowbar;commands</Literal>. The role of this structure is to group together a
+group of commands, which can be later assigned to a specific type. Each
+structure had:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ A list of command names.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ A list of pointers to functions, which binds each command to its
+special fine-tuned C function.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+In order to relate a list of commands to a type definition, each
+<Literal remap="tt">struct&lowbar;descriptor</Literal> structure (explained earlier) was added a private
+<Literal remap="tt">struct&lowbar;commands</Literal> structure.
+</Para>
+
+<Para>
+Follows the current definitions of <Literal remap="tt">struct&lowbar;descriptor</Literal> and of
+<Literal remap="tt">struct&lowbar;command</Literal>:
+
+<ProgramListing>
+struct struct_descriptor {
+ unsigned long length;
+ unsigned char name [60];
+ unsigned short fields_num;
+ unsigned char field_names [MAX_FIELDS][80];
+ unsigned short field_lengths [MAX_FIELDS];
+ unsigned short field_positions [MAX_FIELDS];
+ struct struct_commands type_commands;
+ struct struct_descriptor *prev,*next;
+};
+
+typedef void (*PF) (char *);
+
+struct struct_commands {
+ int last_command;
+ char *names [MAX_COMMANDS_NUM];
+ char *descriptions [MAX_COMMANDS_NUM];
+ PF callback [MAX_COMMANDS_NUM];
+};
+</ProgramListing>
+
+
+</Para>
+
+</Sect1>
+
+<Sect1 id="flow-control">
+<Title>Program flow control</Title>
+
+<Para>
+Obviously the above approach lead to a major redesign of EXT2ED. The
+main engine of the resulting design is basically the same even now.
+</Para>
+
+<Para>
+I redesigned the program flow control. Up to now, I analyzed the user command
+line with the simple switch method. Now I used the far superior callback
+method.
+</Para>
+
+<Para>
+I divided the available user commands into two groups:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ General commands.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Type specific commands.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+As a result, at each point in time, the user was able to enter a
+<Literal remap="tt">general command</Literal>, selectable from a list of general commands which was
+always available, or a <Literal remap="tt">type specific command</Literal>, selectable from a list of
+commands which <Literal remap="tt">changed in time</Literal> according to the current type that the
+user was editing. The special <Literal remap="tt">type specific command</Literal> "knew" how to
+handle the object in the best possible way - It was "fine tuned" for the
+object's place in the ext2 filesystem design.
+</Para>
+
+<Para>
+In order to implement the above idea, I constructed a global variable of
+type <Literal remap="tt">struct&lowbar;commands</Literal>, which contained the <Literal remap="tt">general commands</Literal>.
+The <Literal remap="tt">type specific commands</Literal> were accessible through the <Literal remap="tt">struct
+descriptors</Literal>, as explained earlier.
+</Para>
+
+<Para>
+The program flow was now done according to the following algorithm:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Ask the user for a command line.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Analyze the user command - Separate it into <Literal remap="tt">command</Literal> and
+<Literal remap="tt">arguments</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Trace the list of known objects to match the command name to a type.
+If the type is found, call the callback function, with the arguments
+as a parameter. Then go back to step (1).
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ If the command is not type specific, try to find it in the general
+commands, and call it if found. Go back to step (1).
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ If the command is not found, issue a short error message, and return
+to step (1).
+</Para>
+</ListItem>
+
+</OrderedList>
+
+Note the <Literal remap="tt">order</Literal> of the above steps. In particular, note that a command
+is first assumed to be a type-specific command and only if this fails, a
+general command is searched. The "<Literal remap="tt">side-effect</Literal>" (main effect, actually)
+is that when we have two commands with the <Literal remap="tt">same name</Literal> - One that is a
+type specific command, and one that is a general command, the dispatching
+algorithm will call the <Literal remap="tt">type specific command</Literal>. This allows
+<Literal remap="tt">overriding</Literal> of a command to provide <Literal remap="tt">fine-tuned</Literal> operation.
+For example, the <Literal remap="tt">show</Literal> command is overridden nearly everywhere,
+to accommodate for the different ways in which different objects are displayed,
+in order to provide an intuitive fine-tuned display.
+</Para>
+
+<Para>
+The above is done in the <Literal remap="tt">dispatch</Literal> function, in <Literal remap="tt">main.c</Literal>. Since
+it is a very important function in EXT2ED, and it is relatively short, I will
+list it entirely here. Note that a redesign was made since then - Another
+level was added between the two described, but I'll elaborate more on this
+later. However, the basic structure follows the explanation described above.
+
+<ProgramListing>
+int dispatch (char *command_line)
+
+{
+ int i,found=0;
+ char command [80];
+
+ parse_word (command_line,command);
+
+ if (strcmp (command,"quit")==0) return (1);
+
+ /* 1. Search for type specific commands FIRST - Allows overriding of a general command */
+
+ if (current_type != NULL)
+ for (i=0;i&#60;=current_type-&#62;type_commands.last_command &#38;&#38; !found;i++) {
+ if (strcmp (command,current_type-&#62;type_commands.names [i])==0) {
+ (*current_type-&#62;type_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+ /* 2. Now search for ext2 filesystem general commands */
+
+ if (!found)
+ for (i=0;i&#60;=ext2_commands.last_command &#38;&#38; !found;i++) {
+ if (strcmp (command,ext2_commands.names [i])==0) {
+ (*ext2_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+
+ /* 3. If not found, search the general commands */
+
+ if (!found)
+ for (i=0;i&#60;=general_commands.last_command &#38;&#38; !found;i++) {
+ if (strcmp (command,general_commands.names [i])==0) {
+ (*general_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+ if (!found) {
+ wprintw (command_win,"Error: Unknown command\n");
+ refresh_command_win ();
+ }
+
+ return (0);
+}
+</ProgramListing>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Source files in EXT2ED</Title>
+
+<Para>
+The project was getting large enough to be split into several source
+files. I split the source as much as I could into self-contained
+source files. The source files consist of the following blocks:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Main include file - ext2ed.h</Literal>
+
+This file contains the definitions of the various structures,
+variables and functions used in EXT2ED. It is included by all source
+files in EXT2ED.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Main block - main.c</Literal>
+
+<Literal remap="tt">main.c</Literal> handles the upper level of the program flow control.
+It contains the <Literal remap="tt">parser</Literal> and the <Literal remap="tt">dispatcher</Literal>. Its task is
+to ask the user for a required action, and to pass control to other
+lower level functions in order to do the actual job.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Initialization - init.c</Literal>
+
+The init source is responsible for the various initialization
+actions which need to be done through the program. For example,
+auto detection of an ext2 filesystem when selecting a device and
+initialization of the filesystem-specific structures described
+earlier.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Disk activity - disk.c</Literal>
+
+<Literal remap="tt">disk.c</Literal> is handles the lower level interaction with the
+device. All disk activity is passed through this file - The various
+functions through the source code request disk actions from the
+functions in this file. In this way, for example, we can easily block
+the write access to the device.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Display output activity - win.c</Literal>
+
+In a similar way to <Literal remap="tt">disk.c</Literal>, the user-interface functions and
+most of the interaction with the <Literal remap="tt">ncurses library</Literal> are done
+here. Nothing will be actually written to a specific window without
+calling a function from this file.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Commands available through dispatching - *&lowbar;com.c </Literal>
+
+The above file name is generic - Each file which ends with
+<Literal remap="tt">&lowbar;com.c</Literal> contains a group of related commands which can be
+called through <Literal remap="tt">the dispatching function</Literal>.
+
+Each object typically has its own file. A separate file is also
+available for the general commands.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+The entire list of source files available at this time is:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ blockbitmap&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ dir&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ disk.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ ext2&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ file&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ general&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ group&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ init.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ inode&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ inodebitmap&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ main.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ super&lowbar;com.c
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ win.c
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>User interface</Title>
+
+<Para>
+The user interface is text-based only and is based on the following
+libraries:
+</Para>
+
+<Para>
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The <Literal remap="tt">ncurses</Literal> library, developed by <Literal remap="tt">Zeyd Ben-Halim</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The <Literal remap="tt">GNU readline</Literal> library.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The user interaction is command line based - The user enters a command
+line, which consists of a <Literal remap="tt">command</Literal> and of <Literal remap="tt">arguments</Literal>. This fits
+nicely with the program flow control described earlier - The <Literal remap="tt">command</Literal>
+is used by <Literal remap="tt">dispatch</Literal> to select the right function, and the
+<Literal remap="tt">arguments</Literal> are interpreted by the function itself.
+</Para>
+
+<Sect2>
+<Title>The ncurses library</Title>
+
+<Para>
+The <Literal remap="tt">ncurses</Literal> library enables me to divide the screen into "windows".
+The main advantage is that I treat the "window" in a virtual way, asking
+the ncurses library to "write to a window". However, the ncurses
+library internally buffers the requests, and nothing is actually passed to the
+terminal until an explicit refresh is requested. When the refresh request is
+made, ncurses compares the current terminal state (as known in the last time
+that a refresh was done) with the new to be shown state, and passes to the
+terminal the minimal information required to update the display. As a
+result, the display output is optimized behind the scenes by the
+<Literal remap="tt">ncurses</Literal> library, while I can still treat it in a virtual way.
+</Para>
+
+<Para>
+There are two basic concepts in the <Literal remap="tt">ncurses</Literal> library:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ A window.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ A pad.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+A window can be no bigger than the actual terminal size. A pad, however, is
+not limited in its size.
+</Para>
+
+<Para>
+The user screen is divided by EXT2ED into three windows and one pad:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Title window.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Status window.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Main display pad.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Command window.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The <Literal remap="tt">title window</Literal> is static - It just displays the current version
+of EXT2ED.
+</Para>
+
+<Para>
+The user interaction is done in the <Literal remap="tt">command window</Literal>. The user enters a
+<Literal remap="tt">command line</Literal>, feedback is usually displayed there, and then relevant
+data is usually displayed in the main display and in the status window.
+</Para>
+
+<Para>
+The <Literal remap="tt">main display</Literal> is using a <Literal remap="tt">pad</Literal> instead of a window because
+the amount of information which is written to it is not known in advance.
+Therefor, the user treats the main display as a "window" into a bigger
+display and can <Literal remap="tt">scroll vertically</Literal> using the <Literal remap="tt">pgdn</Literal> and <Literal remap="tt">pgup</Literal>
+commands. Although the <Literal remap="tt">pad</Literal> mechanism enables me to use horizontal
+scrolling, I have not utilized this.
+</Para>
+
+<Para>
+When I need to show something to the user, I use the ncurses <Literal remap="tt">wprintw</Literal>
+command. Then an explicit refresh command is required. As explained before,
+the refresh commands is piped through <Literal remap="tt">win.c</Literal>. For example, to update
+the command window, <Literal remap="tt">refresh&lowbar;command&lowbar;win ()</Literal> is used.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The readline library</Title>
+
+<Para>
+Avner suggested me to integrate the GNU <Literal remap="tt">readline</Literal> library in my project.
+The <Literal remap="tt">readline</Literal> library is designed specifically for programs which use
+command line interface. It provides a nice package of <Literal remap="tt">command line editing
+tools</Literal> - Inserting, deleting words, and the whole package of editing tools
+which are normally available in the <Literal remap="tt">bash</Literal> shell (Refer to the readline
+documentation for details). In addition, I utilized the <Literal remap="tt">history</Literal>
+feature of the readline library - The entered commands are saved in a
+<Literal remap="tt">command history</Literal>, and can be called later by whatever means that the
+readline package provides. Command completion is also supported - When the
+user enters a partial command name, EXT2ED will provide the readline library
+with the possible completions.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Possible support of other filesystems</Title>
+
+<Para>
+The entire ext2 layer is provided through specific objects. Given another
+set of objects, support of other filesystem can be provided using the same
+dispatching mechanism. In order to prepare the surface for this option, I
+added yet another layer to the two-layer structure presented earlier. EXT2ED
+commands now consist of three layers:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The general commands.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The ext2 general commands.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The ext2 object specific commands.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+The general commands are provided by the <Literal remap="tt">general&lowbar;com.c</Literal> source file,
+and are always available. The two other levels are not present when EXT2ED
+loads - They are dynamically added by <Literal remap="tt">init.c</Literal> when EXT2ED detects an
+ext2 filesystem on the device.
+</Para>
+
+<Para>
+The abstraction levels presented above helps to extend EXT2ED to fully
+support a new filesystem, with its own specific type commands.
+</Para>
+
+<Para>
+Even without any source code modification, the user is free to add structure
+definitions in a separate file (specified in the configuration file),
+which will be added to the list of available objects. The added objects will
+consist only of variables, of-course, and will be used through the more
+primitive <Literal remap="tt">setoffset</Literal> and <Literal remap="tt">settype</Literal> commands.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>On the implementation of the various commands</Title>
+
+<Para>
+This section points out some typical programming style that I used in many
+places at the code.
+</Para>
+
+<Sect2>
+<Title>The explicit use of the dispatch function</Title>
+
+<Para>
+The various commands are reached by the user through the <Literal remap="tt">dispatch</Literal>
+function. This is not surprising. The fact that can be surprising, at least in
+a first look, is that <Literal remap="tt">you'll find the dispatch call in many of my
+own functions!</Literal>.
+</Para>
+
+<Para>
+I am in fact using my own implemented functions to construct higher
+level operations. I am heavily using the fact that the dispatching mechanism
+is object oriented ant that the <Literal remap="tt">overriding</Literal> principle takes place and
+selects the proper function to call when several commands with the same name
+are accessible.
+</Para>
+
+<Para>
+Sometimes, however, I call the explicit command directly, without passing
+through <Literal remap="tt">dispatch</Literal>. This is typically done when I want to bypass the
+<Literal remap="tt">overriding</Literal> effect.
+</Para>
+
+<Para>
+
+This is used, for example, in the interaction between the global cd command
+and the dir object specific cd command. You will see there that in order
+to implement the "entire" cd command, the type specific cd command uses both
+a dispatching mechanism to call itself recursively if a relative path is
+used, or a direct call of the general cd handling function if an explicit path
+is used.
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Passing information between handling functions</Title>
+
+<Para>
+Typically, every source code file which handles one object type has a global
+structure specifically designed for it which is used by most of the
+functions in that file. This is used to pass information between the various
+functions there, and to physically provide the link to other related
+objects, typically for initialization use.
+</Para>
+
+<Para>
+
+For example, in order to edit a file, information about the
+inode is needed - The file command is available only when editing an
+inode. When the file command is issued, the handling function (found,
+according to the source division outlined above, in inode_com.c) will
+store the necessary information about the inode in a specific structure
+of type struct_file_info which will be available for use by the file_com.c
+functions. Only then it will set the type to file. This is also the reason
+that a direct asynchronous set of the object type to a file through a settype
+command will fail - The above data structure will not be initialized
+properly because the user never was at the inode of the file.
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>A very simplified overview of a typical command handling function</Title>
+
+<Para>
+This is a very simplified overview. Detailed information will follow
+where appropriate.
+</Para>
+
+<Sect3>
+<Title>The prototype of a typical handling function</Title>
+
+<Para>
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ I chose a unified <Literal remap="tt">naming convention</Literal> for the various object
+specific commands. It is perhaps best showed with an example:
+
+The prototype of the handling function of the command <Literal remap="tt">next</Literal> of
+the type <Literal remap="tt">file</Literal> is:
+
+<Screen>
+ extern void type_file___next (char *command_line);
+
+</Screen>
+
+
+For other types and commands, the words <Literal remap="tt">file</Literal> and <Literal remap="tt">next</Literal>
+should be replaced accordingly.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The ext2 general commands syntax is similar. For example, the ext2
+general command <Literal remap="tt">super</Literal> results in calling:
+
+<Screen>
+ extern void type_ext2___super (char *command_line);
+
+</Screen>
+
+Those functions are available in <Literal remap="tt">ext2&lowbar;com.c</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The general commands syntax is even simpler - The name of the
+handling function is exactly the name of the commands. Those
+functions are available in <Literal remap="tt">general&lowbar;com.c</Literal>.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>"Typical" algorithm</Title>
+
+<Para>
+This section can't of-course provide meaningful information - Each
+command is handled differently, but the following frame is typical:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Parse command line arguments and analyze them. Return with an error
+message if the syntax is wrong.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ "Act accordingly", perhaps making use of the global variable available
+to this type.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Use some <Literal remap="tt">dispatch / direct </Literal> calls in order to pass control to
+other lower-level user commands.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Sometimes <Literal remap="tt">dispatch</Literal> to the object's <Literal remap="tt">show</Literal> command to
+display the resulting data to the user.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+I told you it is meaningless :-)
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Initialization overview</Title>
+
+<Para>
+In this section I will discuss some aspects of the various initialization
+routines available in the source file <Literal remap="tt">init.c</Literal>.
+</Para>
+
+<Sect2>
+<Title>Upon startup</Title>
+
+<Para>
+Follows the function <Literal remap="tt">main</Literal>, appearing of-course in <Literal remap="tt">main.c</Literal>:
+
+
+<ProgramListing>
+int main (void)
+
+{
+ if (!init ()) return (0); /* Perform some initial initialization */
+ /* Quit if failed */
+
+ parser (); /* Get and parse user commands */
+
+ prepare_to_close (); /* Do some cleanup */
+ printf ("Quitting ...\n");
+ return (1); /* And quit */
+}
+</ProgramListing>
+
+</Para>
+
+<Para>
+The two initialization functions, which are called by <Literal remap="tt">main</Literal>, are:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ init
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ prepare&lowbar;to&lowbar;close
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Sect3>
+<Title>The init function</Title>
+
+<Para>
+<Literal remap="tt">init</Literal> is called from <Literal remap="tt">main</Literal> upon startup. It initializes the
+following tasks / subsystems:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Processing of the <Literal remap="tt">user configuration file</Literal>, by using the
+<Literal remap="tt">process&lowbar;configuration&lowbar;file</Literal> function. Failing to complete the
+configuration file processing is considered a <Literal remap="tt">fatal error</Literal>,
+and EXT2ED is aborted. I did it this way because the configuration
+file has some sensitive user options like write access behavior, and
+I wanted to be sure that the user is aware of them.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Registration of the <Literal remap="tt">general commands</Literal> through the use of
+the <Literal remap="tt">add&lowbar;general&lowbar;commands</Literal> function.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Reset of the object memory rotating lifo structure.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Reset of the device parameters and of the current type.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Initialization of the windows subsystem - The interface between the
+ncurses library and EXT2ED, through the use of the <Literal remap="tt">init&lowbar;windows</Literal>
+function, available in <Literal remap="tt">win.c</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Initialization of the interface between the readline library and
+EXT2ED, through <Literal remap="tt">init&lowbar;readline</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Initialization of the <Literal remap="tt">signals</Literal> subsystem, through
+<Literal remap="tt">init&lowbar;signals</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Disabling write access. Write access needs to be explicitly enabled
+using a user command, to prevent accidental user mistakes.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+When <Literal remap="tt">init</Literal> is finished, it dispatches the <Literal remap="tt">help</Literal> command in order
+to show the available commands to the user. Note that the ext2 layer is still
+not added; It will be added if and when EXT2ED will detect an ext2
+filesystem on a device.
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>The prepare&lowbar;to&lowbar;close function</Title>
+
+<Para>
+The <Literal remap="tt">prepare&lowbar;to&lowbar;close</Literal> function reverses some of the actions done
+earlier in EXT2ED and freeing the dynamically allocated memory.
+Specifically, it:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Closes the open device, if any.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Removes the first level - Removing the general commands, through
+the use of <Literal remap="tt">free&lowbar;user&lowbar;commands</Literal>, with a pointer to the
+general&lowbar;commands structure as a parameter.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Removes of the second level - Removing the ext2 ext2 general
+commands, in much the same way.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Removes of the third level - Removing the objects and the object
+specific commands, by using <Literal remap="tt">free&lowbar;struct&lowbar;descriptors</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Closes the window subsystem, and detaches EXT2ED from the ncurses
+library, through the use of the <Literal remap="tt">close&lowbar;windows</Literal> function,
+available in <Literal remap="tt">win.c</Literal>.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+<Sect2>
+<Title>Registration of commands</Title>
+
+<Para>
+Addition of a user command is done through the <Literal remap="tt">add&lowbar;user&lowbar;command</Literal>
+function. The prototype is:
+
+<Screen>
+void add_user_command (struct struct_commands *ptr,char *name,char
+*description,PF callback);
+</Screen>
+
+The function receives a pointer to a structure of type
+<Literal remap="tt">struct&lowbar;commands</Literal>, a desired name for the command which will be used by
+the user to identify the command, a short description which is utilized by the
+<Literal remap="tt">help</Literal> subsystem, and a pointer to a C function which will be called if
+<Literal remap="tt">dispatch</Literal> decides that this command was requested.
+</Para>
+
+<Para>
+The <Literal remap="tt">add&lowbar;user&lowbar;command</Literal> is a <Literal remap="tt">low level function</Literal> used in the three
+levels to add user commands. For example, addition of the <Literal remap="tt">ext2
+general commands is done by:</Literal>
+
+<ProgramListing>
+void add_ext2_general_commands (void)
+
+{
+ add_user_command (&amp;ext2_commands,"super","Moves to the superblock of the filesystem",type_ext2___super);
+ add_user_command (&amp;ext2_commands,"group","Moves to the first group descriptor",type_ext2___group);
+ add_user_command (&amp;ext2_commands,"cd","Moves to the directory specified",type_ext2___cd);
+}
+</ProgramListing>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Registration of objects</Title>
+
+<Para>
+Registration of objects is based, as explained earlier, on the "compilation"
+of an external user file, which has a syntax similar to the C language
+<Literal remap="tt">struct</Literal> keyword. The primitive parser I have implemented detects the
+definition of structures, and calls some lower level functions to actually
+register the new detected object. The parser's prototype is:
+
+<Screen>
+int set_struct_descriptors (char *file_name)
+</Screen>
+
+It opens the given file name, and calls, when appropriate:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ add&lowbar;new&lowbar;descriptor
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ add&lowbar;new&lowbar;variable
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+<Literal remap="tt">add&lowbar;new&lowbar;descriptor</Literal> is a low level function which adds a new descriptor
+to the doubly linked list of the available objects. It will then call
+<Literal remap="tt">fill&lowbar;type&lowbar;commands</Literal>, which will add specific commands to the object,
+if the object is known.
+</Para>
+
+<Para>
+<Literal remap="tt">add&lowbar;new&lowbar;variable</Literal> will add a new variable of the requested length to the
+specified descriptor.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Initialization upon specification of a device</Title>
+
+<Para>
+When the general command <Literal remap="tt">setdevice</Literal> is used to open a device, some
+initialization sequence takes place, which is intended to determine two
+factors:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Are we dealing with an ext2 filesystem ?
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ What are the basic filesystem parameters, such as its total size and
+its block size ?
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+This questions are answered by the <Literal remap="tt">set&lowbar;file&lowbar;system&lowbar;info</Literal>, possibly
+using some <Literal remap="tt">help from the user</Literal>, through the configuration file.
+The answers are placed in the <Literal remap="tt">file&lowbar;system&lowbar;info</Literal> structure, which is of
+type <Literal remap="tt">struct&lowbar;file&lowbar;system&lowbar;info</Literal>:
+
+<ProgramListing>
+struct struct_file_system_info {
+ unsigned long file_system_size;
+ unsigned long super_block_offset;
+ unsigned long first_group_desc_offset;
+ unsigned long groups_count;
+ unsigned long inodes_per_block;
+ unsigned long blocks_per_group; /* The name is misleading; beware */
+ unsigned long no_blocks_in_group;
+ unsigned short block_size;
+ struct ext2_super_block super_block;
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+Autodetection of an ext2 filesystem is usually recommended. However, on a damaged
+filesystem I can't assure a success. That's were the user comes in - He can
+<Literal remap="tt">override</Literal> the auto detection procedure and force an ext2 filesystem, by
+selecting the proper options in the configuration file.
+</Para>
+
+<Para>
+If auto detection succeeds, the second question above is automatically
+answered - I get all the information I need from the filesystem itself. In
+any case, default parameters can be supplied in the configuration file and
+the user can select the required behavior.
+</Para>
+
+<Para>
+If we decide to treat the filesystem as an ext2 filesystem, <Literal remap="tt">registration of
+the ext2 specific objects</Literal> is done at this point, by calling the
+<Literal remap="tt">set&lowbar;struct&lowbar;descriptors</Literal> outlined earlier, with the name of the file
+which describes the ext2 objects, and is basically based on the ext2 sources
+main include file. At this point, EXT2ED can be fully used by the user.
+</Para>
+
+<Para>
+If we do not register the ext2 specific objects, the user can still provide
+object definitions in a separate file, and will be able to use EXT2ED in a
+<Literal remap="tt">limited form</Literal>, but more sophisticated than a simple hex editor.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>main.c</Title>
+
+<Para>
+As described earlier, <Literal remap="tt">main.c</Literal> is used as a front-head to the entire
+program. <Literal remap="tt">main.c</Literal> contains the following elements:
+</Para>
+
+<Sect2>
+<Title>The main routine</Title>
+
+<Para>
+The <Literal remap="tt">main</Literal> routine was displayed above. Its task is to pass control to
+the initialization routines and to the parser.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The parser</Title>
+
+<Para>
+The parser consists of the following functions:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The <Literal remap="tt">parser</Literal> function, which reads the command line from the
+user and saves it in readline's history buffer and in the internal
+last-command buffer.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The <Literal remap="tt">parse&lowbar;word</Literal> function, which receives a string and parses
+the first word from it, ignoring whitespaces, and returns a pointer
+to the rest of the string.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The <Literal remap="tt">complete&lowbar;command</Literal> function, which is used by the readline
+library for command completion. It scans the available commands at
+this point and determines the possible completions.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The dispatcher</Title>
+
+<Para>
+The dispatcher was already explained in the flow control section - section
+<XRef LinkEnd="flow-control">. Its task is to pass control to the proper command
+handling function, based on the command line's command.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The self-sanity control</Title>
+
+<Para>
+This is not fully implemented.
+</Para>
+
+<Para>
+The general idea was to provide a control system which will supervise the
+internal work of EXT2ED. Since I am pretty sure that bugs exist, I have
+double checked myself in a few instances, and issued an <Literal remap="tt">internal
+error</Literal> warning if I reached the conclusion that something is not logical.
+The internal error is reported by the function <Literal remap="tt">internal&lowbar;error</Literal>,
+available in <Literal remap="tt">main.c</Literal>.
+</Para>
+
+<Para>
+The self sanity check is compiled only if the compile time option
+<Literal remap="tt">DEBUG</Literal> is selected.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The windows interface</Title>
+
+<Para>
+Screen handling and interfacing to the <Literal remap="tt">ncurses</Literal> library is done in
+<Literal remap="tt">win.c</Literal>.
+</Para>
+
+<Sect2>
+<Title>Initialization</Title>
+
+<Para>
+Opening of the windows is done in <Literal remap="tt">init&lowbar;windows</Literal>. In
+<Literal remap="tt">close&lowbar;windows</Literal>, we just close our windows. The various window lengths
+with an exception to the <Literal remap="tt">show pad</Literal> are defined in the main header file.
+The rest of the display will be used by the <Literal remap="tt">show pad</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Display output</Title>
+
+<Para>
+Each actual refreshing of the terminal monitor is done by using the
+appropriate refresh function from this file: <Literal remap="tt">refresh&lowbar;title&lowbar;win</Literal>,
+<Literal remap="tt">refresh&lowbar;show&lowbar;win</Literal>, <Literal remap="tt">refresh&lowbar;show&lowbar;pad</Literal> and
+<Literal remap="tt">refresh&lowbar;command&lowbar;win</Literal>.
+</Para>
+
+<Para>
+With the exception of the <Literal remap="tt">show pad</Literal>, each function simply calls the
+<Literal remap="tt">ncurses refresh command</Literal>. In order to provide to <Literal remap="tt">scrolling</Literal> in
+the <Literal remap="tt">show pad</Literal>, some information about its status is constantly updated
+by the various functions which display output in it. <Literal remap="tt">refresh&lowbar;show&lowbar;pad</Literal>
+passes this information to <Literal remap="tt">ncurses</Literal> so that the correct part of the pad
+is actually copied to the display.
+</Para>
+
+<Para>
+The above information is saved in a global variable of type <Literal remap="tt">struct
+struct&lowbar;pad&lowbar;info</Literal>:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct struct_pad_info {
+ int display_lines,display_cols;
+ int line,col;
+ int max_line,max_col;
+ int disable_output;
+};
+</ProgramListing>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Screen redraw</Title>
+
+<Para>
+The <Literal remap="tt">redraw&lowbar;all</Literal> function will just reopen the windows. This action is
+necessary if the display gets garbled from some reason.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The disk interface</Title>
+
+<Para>
+All the disk activity with regard to the filesystem passes through the file
+<Literal remap="tt">disk.c</Literal>. This is done that way to provide additional levels of safety
+concerning the disk access. This way, global decisions considering the disk
+can be easily accomplished. The benefits of this isolation will become even
+clearer in the next sections.
+</Para>
+
+<Sect2>
+<Title>Low level functions</Title>
+
+<Para>
+Read requests are ultimately handled by <Literal remap="tt">low&lowbar;read</Literal> and write requests
+are handled by <Literal remap="tt">low&lowbar;write</Literal>. They just receive the length of the data
+block, the offset in the filesystem and a pointer to the buffer and pass the
+request to the <Literal remap="tt">fread</Literal> or <Literal remap="tt">fwrite</Literal> standard library functions.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Mounted filesystems</Title>
+
+<Para>
+EXT2ED design assumes that the edited filesystem is not mounted. Even if
+a <Literal remap="tt">reasonably simple</Literal> way to handle mounted filesystems exists, it is
+probably <Literal remap="tt">too complicated</Literal> :-)
+</Para>
+
+<Para>
+Write access to a mounted filesystem will be denied. Read access can be
+allowed by using a configuration file option. The mount status is determined
+by reading the file /etc/mtab.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Write access</Title>
+
+<Para>
+Write access is the most sensitive part in the program. This program is
+intended for <Literal remap="tt">editing filesystems</Literal>. It is obvious that a small mistake
+in this regard can make the filesystem not usable anymore.
+</Para>
+
+<Para>
+The following safety measures are added, of-course, to the general Unix
+permission protection - The user can always disable write access on the
+device file itself.
+</Para>
+
+<Para>
+Considering the user, the following safety measures were taken:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ The filesystem is <Literal remap="tt">never</Literal> opened with write-access enables.
+Rather, the user must explicitly request to enable write-access.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The user can <Literal remap="tt">disable</Literal> write access entirely by using a
+<Literal remap="tt">configuration file option</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Changes are never done automatically - Whenever the user makes
+changes, they are done in memory. An explicit <Literal remap="tt">writedata</Literal>
+command should be issued to make the changes active in the disk.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+Considering myself, I tried to protect against my bugs by:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Opening the device in read-only mode until a write request is
+issued by the user.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Limiting <Literal remap="tt">actual</Literal> filesystem access to two functions only -
+<Literal remap="tt">low&lowbar;read</Literal> for reading, and <Literal remap="tt">low&lowbar;write</Literal> for writing. Those
+functions were programmed carefully, and I added the self
+sanity checks there. In addition, this is the only place in which I
+need to check the user options described above - There can be no
+place in which I can "forget" to check them.
+
+Note that The disabling of write-access through the configuration file
+is double checked here only as a <Literal remap="tt">self-sanity</Literal> check - If
+<Literal remap="tt">DEBUG</Literal> is selected, since write enable should have been refused
+and write-access is always disabled at startup, hence finding
+<Literal remap="tt">here</Literal> that the user has write access disabled through the
+configuration file clearly indicates that I have a bug somewhere.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The following safety measure can provide protection against <Literal remap="tt">both</Literal> user
+mistakes and my own bugs:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ I added a <Literal remap="tt">logging option</Literal>, which logs every actual write
+access to the disk in the lowest level - In <Literal remap="tt">low&lowbar;write</Literal> itself.
+
+The logging has nothing to do with the current type and the various
+other higher level operations of EXT2ED - It is simply a hex dump of
+the contents which will be overwritten; Both the original contents
+and the new written data.
+
+In that case, even if the user makes a mistake, the original data
+can be retrieved.
+
+Even If I have a bug somewhere which causes incorrect data to be
+written to the disk, the logging option will still log exactly the
+original contents at the place were data was incorrectly overwritten.
+(This assumes, of-course, that <Literal remap="tt">low-write</Literal> and the <Literal remap="tt">logging
+itself</Literal> work correctly. I have done my best to verify that this is
+indeed the case).
+
+The <Literal remap="tt">logging</Literal> option is implemented in the <Literal remap="tt">log&lowbar;changes</Literal>
+function.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Reading / Writing objects</Title>
+
+<Para>
+Usually <Literal remap="tt">(not always)</Literal>, the current object data is available in the
+global variable <Literal remap="tt">type&lowbar;data</Literal>, which is of the type:
+
+<ProgramListing>
+struct struct_type_data {
+ long offset_in_block;
+
+ union union_type_data {
+ char buffer [EXT2_MAX_BLOCK_SIZE];
+ struct ext2_acl_header t_ext2_acl_header;
+ struct ext2_acl_entry t_ext2_acl_entry;
+ struct ext2_old_group_desc t_ext2_old_group_desc;
+ struct ext2_group_desc t_ext2_group_desc;
+ struct ext2_inode t_ext2_inode;
+ struct ext2_super_block t_ext2_super_block;
+ struct ext2_dir_entry t_ext2_dir_entry;
+ } u;
+};
+</ProgramListing>
+
+The above union enables me, in the program, to treat the data as raw data or
+as a meaningful filesystem object.
+</Para>
+
+<Para>
+The reading and writing, if done to this global variable, are done through
+the functions <Literal remap="tt">load&lowbar;type&lowbar;data</Literal> and <Literal remap="tt">write&lowbar;type&lowbar;data</Literal>, available in
+<Literal remap="tt">disk.c</Literal>.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The general commands</Title>
+
+<Para>
+The <Literal remap="tt">general commands</Literal> are handled in the file <Literal remap="tt">general&lowbar;com.c</Literal>.
+</Para>
+
+<Sect2>
+<Title>The help system</Title>
+
+<Para>
+The help command is handled by the function <Literal remap="tt">help</Literal>. The algorithm is as
+follows:
+</Para>
+
+<Para>
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Check the command line arguments. If there is an argument, pass
+control to the <Literal remap="tt">detailed&lowbar;help</Literal> function, in order to provide
+help on the specific command.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ If general help was requested, display a list of the available
+commands at this point. The three levels are displayed in reverse
+order - First the commands which are specific to the current type
+(If a current type is defined), then the ext2 general commands (If
+we decided that the filesystem should be treated like an ext2
+filesystem), then the general commands.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Display information about EXT2ED - Current version, general
+information about the project, etc.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The setdevice command</Title>
+
+<Para>
+The <Literal remap="tt">setdevice</Literal> commands result in calling the <Literal remap="tt">set&lowbar;device</Literal>
+function. The algorithm is:
+</Para>
+
+<Para>
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Parse the command line argument. If it isn't available report the
+error and return.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Close the current open device, if there is one.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Open the new device in read-only mode. Update the global variables
+<Literal remap="tt">device&lowbar;name</Literal> and <Literal remap="tt">device&lowbar;handle</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Disable write access.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Empty the object memory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Unregister the ext2 general commands, using
+<Literal remap="tt">free&lowbar;user&lowbar;commands</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Unregister the current objects, using <Literal remap="tt">free&lowbar;struct&lowbar;descriptors</Literal>
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Call <Literal remap="tt">set&lowbar;file&lowbar;system&lowbar;info</Literal> to auto-detect an ext2 filesystem
+and set the basic filesystem values.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Add the <Literal remap="tt">alternate descriptors</Literal>, supplied by the user.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Set the device offset to the filesystem start by dispatching
+<Literal remap="tt">setoffset 0</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Show the new available commands by dispatching the <Literal remap="tt">help</Literal>
+command.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Basic maneuvering</Title>
+
+<Para>
+Basic maneuvering is done using the <Literal remap="tt">setoffset</Literal> and the <Literal remap="tt">settype</Literal>
+user commands.
+</Para>
+
+<Para>
+<Literal remap="tt">set&lowbar;offset</Literal> accepts some alternative forms of specifying the new
+offset. They all ultimately lead to changing the <Literal remap="tt">device&lowbar;offset</Literal>
+global variable and seeking to the new position. <Literal remap="tt">set&lowbar;offset</Literal> also
+calls <Literal remap="tt">load&lowbar;type&lowbar;data</Literal> to read a block ahead of the new position into
+the <Literal remap="tt">type&lowbar;data</Literal> global variable.
+</Para>
+
+<Para>
+<Literal remap="tt">set&lowbar;type</Literal> will point the global variable <Literal remap="tt">current&lowbar;type</Literal> to the
+correct entry in the double linked list of the known objects. If the
+requested type is <Literal remap="tt">hex</Literal> or <Literal remap="tt">none</Literal>, <Literal remap="tt">current&lowbar;type</Literal> will be
+initialized to <Literal remap="tt">NULL</Literal>. <Literal remap="tt">set&lowbar;type</Literal> will also dispatch <Literal remap="tt">show</Literal>,
+so that the object data will be re-formatted in the new format.
+</Para>
+
+<Para>
+When editing an ext2 filesystem, it is not intended that those commands will
+be used directly, and it is usually not required. My implementation of the
+ext2 layer, on the other hand, uses this lower level commands on countless
+occasions.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The display functions</Title>
+
+<Para>
+The general command version of <Literal remap="tt">show</Literal> is handled by the <Literal remap="tt">show</Literal>
+function. This command is overridden by various objects to provide a display
+which is better suited to the object.
+</Para>
+
+<Para>
+The general show command will format the data in <Literal remap="tt">type&lowbar;data</Literal> according
+to the structure definition of the current type and show it on the <Literal remap="tt">show
+pad</Literal>. If there is no current type, the data will be shown as a simple hex
+dump; Otherwise, the list of variables, along with their values will be shown.
+</Para>
+
+<Para>
+A call to <Literal remap="tt">show&lowbar;info</Literal> is also made - <Literal remap="tt">show&lowbar;info</Literal> will provide
+<Literal remap="tt">general statistics</Literal> on the <Literal remap="tt">show&lowbar;window</Literal>, such as the current
+block, current type, current offset and current page.
+</Para>
+
+<Para>
+The <Literal remap="tt">pgup</Literal> and <Literal remap="tt">pgdn</Literal> general commands just update the
+<Literal remap="tt">show&lowbar;pad&lowbar;info</Literal> global variable - We just increment
+<Literal remap="tt">show&lowbar;pad&lowbar;info.line</Literal> with the number of lines in the screen -
+<Literal remap="tt">show&lowbar;pad&lowbar;info.display&lowbar;lines</Literal>, which was initialized in
+<Literal remap="tt">init&lowbar;windows</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Changing data</Title>
+
+<Para>
+Data change is done in memory only. An update to the disk if followed by an
+explicit <Literal remap="tt">writedata</Literal> command to the disk. The <Literal remap="tt">write&lowbar;data</Literal>
+function simple calls the <Literal remap="tt">write&lowbar;type&lowbar;data</Literal> function, outlined earlier.
+</Para>
+
+<Para>
+The <Literal remap="tt">set</Literal> command is used for changing the data.
+</Para>
+
+<Para>
+If there is no current type, control is passed to the <Literal remap="tt">hex&lowbar;set</Literal> function,
+which treats the data as a block of bytes and uses the
+<Literal remap="tt">type&lowbar;data.offset&lowbar;in&lowbar;block</Literal> variable to write the new text or hex string
+to the correct place in the block.
+</Para>
+
+<Para>
+If a current type is defined, the requested variable is searched in the
+current object, and the desired new valued is entered.
+</Para>
+
+<Para>
+The <Literal remap="tt">enablewrite</Literal> commands just sets the global variable
+<Literal remap="tt">write&lowbar;access</Literal> to <Literal remap="tt">1</Literal> and re-opens the filesystem in read-write
+mode, if possible.
+</Para>
+
+<Para>
+If the current type is NULL, a hex-mode is assumed - The <Literal remap="tt">next</Literal> and
+<Literal remap="tt">prev</Literal> commands will just update <Literal remap="tt">type&lowbar;data.offset&lowbar;in&lowbar;block</Literal>.
+</Para>
+
+<Para>
+If the current type is not NULL, the The <Literal remap="tt">next</Literal> and <Literal remap="tt">prev</Literal> command
+are usually overridden anyway. If they are not overridden, it will be assumed
+that the user is editing an array of such objects, and they will just pass
+to the next / prev element by dispatching to <Literal remap="tt">setoffset</Literal> using the
+<Literal remap="tt">setoffset type + / - X</Literal> syntax.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The ext2 general commands</Title>
+
+<Para>
+The ext2 general commands are contained in the <Literal remap="tt">ext2&lowbar;general&lowbar;commands</Literal>
+global variable (which is of type <Literal remap="tt">struct struct&lowbar;commands</Literal>).
+</Para>
+
+<Para>
+The handling functions are implemented in the source file <Literal remap="tt">ext2&lowbar;com.c</Literal>.
+I will include the entire source code since it is relatively short.
+</Para>
+
+<Sect2>
+<Title>The super command</Title>
+
+<Para>
+The super command just "brings the user" to the main superblock and set the
+type to ext2&lowbar;super&lowbar;block. The implementation is trivial:
+</Para>
+
+<Para>
+
+<ProgramListing>
+void type_ext2___super (char *command_line)
+
+{
+ char buffer [80];
+
+ super_info.copy_num=0;
+ sprintf (buffer,"setoffset %ld",file_system_info.super_block_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_super_block");dispatch (buffer);
+}
+</ProgramListing>
+
+It involves only setting the <Literal remap="tt">copy&lowbar;num</Literal> variable to indicate the main
+copy, dispatching a <Literal remap="tt">setoffset</Literal> command to reach the superblock, and
+dispatching a <Literal remap="tt">settype</Literal> to enable the superblock specific commands.
+This last command will also call the <Literal remap="tt">show</Literal> command of the
+<Literal remap="tt">ext2&lowbar;super&lowbar;block</Literal> type, through dispatching at the general command
+<Literal remap="tt">settype</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The group command</Title>
+
+<Para>
+The group command will bring the user to the specified group descriptor in
+the main copy of the group descriptors. The type will be set to
+<Literal remap="tt">ext2&lowbar;group&lowbar;desc</Literal>:
+
+<ProgramListing>
+void type_ext2___group (char *command_line)
+
+{
+ long group_num=0;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ group_num=atol (buffer);
+ }
+
+ group_info.copy_num=0;group_info.group_num=0;
+ sprintf (buffer,"setoffset %ld",file_system_info.first_group_desc_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_group_desc");dispatch (buffer);
+ sprintf (buffer,"entry %ld",group_num);dispatch (buffer);
+}
+</ProgramListing>
+
+The implementation is as trivial as the <Literal remap="tt">super</Literal> implementation. Note
+the use of the <Literal remap="tt">entry</Literal> command, which is a command of the
+<Literal remap="tt">ext2&lowbar;group&lowbar;desc</Literal> object, to pass to the correct group descriptor.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The cd command</Title>
+
+<Para>
+The <Literal remap="tt">cd</Literal> command performs the usual cd function. The path to the global
+cd command is a path from <Literal remap="tt">/</Literal>.
+</Para>
+
+<Para>
+<Literal remap="tt">This is one of the best examples of the power of the object oriented
+design and of the dispatching mechanism. The operation is complicated, yet the
+implementation is surprisingly short!</Literal>
+</Para>
+
+<Para>
+
+<ProgramListing>
+void type_ext2___cd (char *command_line)
+
+{
+ char temp [80],buffer [80],*ptr;
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");
+ refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ if (buffer [0] != '/') {
+ wprintw (command_win,"Error - Use a full pathname (begin with '/')\n");
+ refresh_command_win ();return;
+ }
+
+ dispatch ("super");dispatch ("group");dispatch ("inode");
+ dispatch ("next");dispatch ("dir");
+ if (buffer [1] != 0) {
+ sprintf (temp,"cd %s",buffer+1);dispatch (temp);
+ }
+}
+</ProgramListing>
+
+</Para>
+
+<Para>
+Note the number of the dispatch calls!
+</Para>
+
+<Para>
+<Literal remap="tt">super</Literal> is used to get to the superblock. <Literal remap="tt">group</Literal> to get to the
+first group descriptor. <Literal remap="tt">inode</Literal> brings us to the first inode - The bad
+blocks inode. A <Literal remap="tt">next</Literal> is command to pass to the root directory inode,
+a <Literal remap="tt">dir</Literal> command "enters" the directory, and then we let the <Literal remap="tt">object
+specific cd command</Literal> to take us from there (The object is <Literal remap="tt">dir</Literal>, so
+that <Literal remap="tt">dispatch</Literal> will call the <Literal remap="tt">cd</Literal> command of the <Literal remap="tt">dir</Literal> type).
+Note that a symbolic link following could bring us back to the root directory,
+thus the innocent calls above treats nicely such a recursive case!
+</Para>
+
+<Para>
+I feel that the above is <Literal remap="tt">intuitive</Literal> - I was expressing myself "in the
+language" of the ext2 filesystem - (Go to the inode, etc), and the code was
+written exactly in this spirit!
+</Para>
+
+<Para>
+I can write more at this point, but I guess I am already a bit carried
+away with the self compliments :-)
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The superblock</Title>
+
+<Para>
+This section details the handling of the superblock.
+</Para>
+
+<Sect2>
+<Title>The superblock variables</Title>
+
+<Para>
+The superblock object is <Literal remap="tt">ext2&lowbar;super&lowbar;block</Literal>. The definition is just
+taken from the kernel ext2 main include file - /usr/include/linux/ext2&lowbar;fs.h.
+<FOOTNOTE>
+
+<Para>
+Those lines of source are copyrighted by <Literal remap="tt">Remy Card</Literal> - The author of the
+ext2 filesystem, and by <Literal remap="tt">Linus Torvalds</Literal> - The first author of the Linux
+operating system. Please cross reference the section Acknowledgments for the
+full copyright.
+</Para>
+
+</FOOTNOTE>
+
+
+
+<ProgramListing>
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behavior when detecting errors */
+ __u16 s_pad;
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ __u32 s_reserved[0]; /* Padding to the end of the block */
+ __u32 s_reserved[1]; /* Padding to the end of the block */
+ .
+ .
+ .
+ __u32 s_reserved[234]; /* Padding to the end of the block */
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+Note that I <Literal remap="tt">expanded</Literal> the array due to my primitive parser
+implementation. The various fields are described in the <Literal remap="tt">technical
+document</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The superblock commands</Title>
+
+<Para>
+This section explains the commands available in the <Literal remap="tt">ext2&lowbar;super&lowbar;block</Literal>
+type. They all appear in <Literal remap="tt">super&lowbar;com.c</Literal>
+</Para>
+
+<Sect3>
+<Title>The show command</Title>
+
+<Para>
+The <Literal remap="tt">show</Literal> command is overridden here in order to provide more
+information than just the list of variables. A <Literal remap="tt">show</Literal> command will end
+up in calling <Literal remap="tt">type&lowbar;super&lowbar;block&lowbar;&lowbar;&lowbar;show</Literal>.
+</Para>
+
+<Para>
+The first thing that we do is calling the <Literal remap="tt">general show command</Literal> in
+order to display the list of variables.
+</Para>
+
+<Para>
+We then add some interpretation to the various lines to make the data
+somewhat more intuitive (Expansion of the time variables and the creator
+operating system code, for example).
+</Para>
+
+<Para>
+We also display the <Literal remap="tt">backup copy number</Literal> of the superblock in the status
+window. This copy number is saved in the <Literal remap="tt">super&lowbar;info</Literal> global variable -
+<Literal remap="tt">super&lowbar;info.copy&lowbar;num</Literal>. Currently, this is the only variable there ...
+but this type of internal variable saving is typical through my
+implementation.
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>The backup copies handling commands</Title>
+
+<Para>
+The <Literal remap="tt">current copy number</Literal> is available in <Literal remap="tt">super&lowbar;info.copy&lowbar;num</Literal>. It
+was initialized in the ext2 command <Literal remap="tt">super</Literal>, and is used by the various
+superblock routines.
+</Para>
+
+<Para>
+The <Literal remap="tt">gocopy</Literal> routine will pass to another copy of the superblock. The
+new device offset will be computed with the aid of the variables in the
+<Literal remap="tt">file&lowbar;system&lowbar;info</Literal> structure. Then the routine will <Literal remap="tt">dispatch</Literal> to
+the <Literal remap="tt">setoffset</Literal> and the <Literal remap="tt">show</Literal> routines.
+</Para>
+
+<Para>
+The <Literal remap="tt">setactivecopy</Literal> routine will just save the current superblock data
+in a temporary variable of type <Literal remap="tt">ext2&lowbar;super&lowbar;block</Literal>, and will dispatch
+<Literal remap="tt">gocopy 0</Literal> to pass to the main superblock. Then it will place the saved
+data in place of the actual data.
+</Para>
+
+<Para>
+The above two commands can be used if the main superblock is corrupted.
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The group descriptors</Title>
+
+<Para>
+The group descriptors handling mechanism allows the user to take a tour in
+the group descriptors table, stopping at each point, and examining the
+relevant inode table, block allocation map or inode allocation map through
+dispatching to the relevant objects.
+</Para>
+
+<Para>
+Some information about the group descriptors is available in the global
+variable <Literal remap="tt">group&lowbar;info</Literal>, which is of type <Literal remap="tt">struct&lowbar;group&lowbar;info</Literal>:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct struct_group_info {
+ unsigned long copy_num;
+ unsigned long group_num;
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+<Literal remap="tt">group&lowbar;num</Literal> is the index of the current descriptor in the table.
+</Para>
+
+<Para>
+<Literal remap="tt">copy&lowbar;num</Literal> is the number of the current backup copy.
+</Para>
+
+<Sect2>
+<Title>The group descriptor's variables</Title>
+
+<Para>
+
+<ProgramListing>
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[3];
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+The first three variables are used to provide the links to the
+<Literal remap="tt">blockbitmap, inodebitmap and inode</Literal> objects.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Movement in the table</Title>
+
+<Para>
+Movement in the group descriptors table is done using the <Literal remap="tt">next, prev and
+entry</Literal> commands. Note that the first two commands <Literal remap="tt">override</Literal> the
+general commands of the same name. The <Literal remap="tt">next and prev</Literal> command are just
+calling the <Literal remap="tt">entry</Literal> function to do the job. I will show <Literal remap="tt">next</Literal>,
+for example:
+</Para>
+
+<Para>
+
+<ProgramListing>
+void type_ext2_group_desc___next (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",group_info.group_num+entry_offset);
+ dispatch (buffer);
+}
+</ProgramListing>
+
+The <Literal remap="tt">entry</Literal> function is also simple - It just calculates the offset
+using the information in <Literal remap="tt">group&lowbar;info</Literal> and in <Literal remap="tt">file&lowbar;system&lowbar;info</Literal>,
+and uses the usual <Literal remap="tt">setoffset / show</Literal> pair.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The show command</Title>
+
+<Para>
+As usual, the <Literal remap="tt">show</Literal> command is overridden. The implementation is
+similar to the superblock's show implementation - We just call the general
+show command, and add some information in the status window - The contents of
+the <Literal remap="tt">group&lowbar;info</Literal> structure.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Moving between backup copies</Title>
+
+<Para>
+This is done exactly like the superblock case. Please refer to explanation
+there.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Links to the available friends</Title>
+
+<Para>
+From a group descriptor, one typically wants to reach an <Literal remap="tt">inode</Literal>, or
+one of the <Literal remap="tt">allocation bitmaps</Literal>. This is done using the <Literal remap="tt">inode,
+blockbitmap or inodebitmap</Literal> commands. The implementation is again trivial
+- Get the necessary information from the group descriptor, initialize the
+structures of the next type, and issue the <Literal remap="tt">setoffset / settype</Literal> pair.
+</Para>
+
+<Para>
+For example, here is the implementation of the <Literal remap="tt">blockbitmap</Literal> command:
+</Para>
+
+<Para>
+
+<ProgramListing>
+void type_ext2_group_desc___blockbitmap (char *command_line)
+
+{
+ long block_bitmap_offset;
+ char buffer [80];
+
+ block_bitmap_info.entry_num=0;
+ block_bitmap_info.group_num=group_info.group_num;
+
+ block_bitmap_offset=type_data.u.t_ext2_group_desc.bg_block_bitmap;
+ sprintf (buffer,"setoffset block %ld",block_bitmap_offset);dispatch (buffer);
+ sprintf (buffer,"settype block_bitmap");dispatch (buffer);
+}
+</ProgramListing>
+
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The inode table</Title>
+
+<Para>
+The inode handling enables the user to move in the inode table, edit the
+various attributes of the inode, and follow to the next stage - A file or a
+directory.
+</Para>
+
+<Sect2>
+<Title>The inode variables</Title>
+
+<Para>
+
+<ProgramListing>
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS]; /* Pointers to blocks */
+ __u32 i_version; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_size_high; /* High 32bits of size */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u32 l_i_reserved2[2];
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ } osd2; /* OS dependent 2 */
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+The above is the original source code definition. We can see that the inode
+supports <Literal remap="tt">Operating systems specific structures</Literal>. In addition to the
+expansion of the arrays, I have <Literal remap="tt">"flattened</Literal> the inode to support only
+the <Literal remap="tt">Linux</Literal> declaration. It seemed that this one occasion of multiple
+variable aliases didn't justify the complication of generally supporting
+aliases. In any case, the above system specific variables are not used
+internally by EXT2ED, and the user is free to change the definition in
+<Literal remap="tt">ext2.descriptors</Literal> to accommodate for his needs.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The handling functions</Title>
+
+<Para>
+The user interface to <Literal remap="tt">movement</Literal> is the usual <Literal remap="tt">next / prev /
+entry</Literal> interface. There is really nothing special in those functions - The
+size of the inode is fixed, the total number of inodes is known from the
+superblock information, and the current entry can be figured up from the
+device offset and the inode table start offset, which is known from the
+corresponding group descriptor. Those functions are a bit older then some
+other implementations of <Literal remap="tt">next</Literal> and <Literal remap="tt">prev</Literal>, and they do not save
+information in a special structure. Rather, they recompute it when
+necessary.
+</Para>
+
+<Para>
+The <Literal remap="tt">show</Literal> command is overridden here, and provides a lot of additional
+information about the inode - Its type, interpretation of the permissions,
+special ext2 attributes (Immutable file, for example), and a lot more.
+Again, the <Literal remap="tt">general show</Literal> is called first, and then the additional
+information is written.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Accessing files and directories</Title>
+
+<Para>
+From the inode, a <Literal remap="tt">file</Literal> or a <Literal remap="tt">directory</Literal> can typically be reached.
+In order to treat a file, for example, its inode needs to be constantly
+accessed. To satisfy that need, when editing a file or a directory, the
+inode is still saved in memory - <Literal remap="tt">type&lowbar;data</Literal> is not overwritten.
+Rather, the following takes place:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ An internal global structure which is used by the types <Literal remap="tt">file</Literal>
+and <Literal remap="tt">dir</Literal> handling functions is initialized by calling the
+appropriate function.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The type is changed accordingly.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+The result is that a <Literal remap="tt">settype ext2&lowbar;inode</Literal> is the only action necessary
+to return to the inode - We actually never left it.
+</Para>
+
+<Para>
+Follows the implementation of the inode's <Literal remap="tt">file</Literal> command:
+</Para>
+
+<Para>
+
+<ProgramListing>
+void type_ext2_inode___file (char *command_line)
+
+{
+ char buffer [80];
+
+ if (!S_ISREG (type_data.u.t_ext2_inode.i_mode)) {
+ wprintw (command_win,"Error - Inode type is not file\n");
+ refresh_command_win (); return;
+ }
+
+ if (!init_file_info ()) {
+ wprintw (command_win,"Error - Unable to show file\n");
+ refresh_command_win ();return;
+ }
+
+ sprintf (buffer,"settype file");dispatch (buffer);
+}
+</ProgramListing>
+
+</Para>
+
+<Para>
+As we can see - We just call <Literal remap="tt">init&lowbar;file&lowbar;info</Literal> to get the necessary
+information from the inode, and set the type to <Literal remap="tt">file</Literal>. The next call
+to <Literal remap="tt">show</Literal>, will dispatch to the <Literal remap="tt">file's show</Literal> implementation.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Viewing a file</Title>
+
+<Para>
+There isn't an ext2 kernel structure which corresponds to a file - A file is
+just a series of blocks which are determined by its inode. As explained in
+the last section, the inode is never actually left - The type is changed to
+<Literal remap="tt">file</Literal> - A type which contains no variables, and a special structure is
+initialized:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct struct_file_info {
+
+ struct ext2_inodes *inode_ptr;
+
+ long inode_offset;
+ long global_block_num,global_block_offset;
+ long block_num,blocks_count;
+ long file_offset,file_length;
+ long level;
+ unsigned char buffer [EXT2_MAX_BLOCK_SIZE];
+ long offset_in_block;
+
+ int display;
+ /* The following is used if the file is a directory */
+
+ long dir_entry_num,dir_entries_count;
+ long dir_entry_offset;
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+The <Literal remap="tt">inode&lowbar;ptr</Literal> will just point to the inode in <Literal remap="tt">type&lowbar;data</Literal>, which
+is not overwritten while the user is editing the file, as the
+<Literal remap="tt">setoffset</Literal> command is not internally used. The <Literal remap="tt">buffer</Literal>
+will contain the current viewed block of the file. The other variables
+contain information about the current place in the file. For example,
+<Literal remap="tt">global&lowbar;block&lowbar;num</Literal> just contains the current block number.
+</Para>
+
+<Para>
+The general idea is that the above data structure will provide the file
+handling functions all the accurate information which is needed to accomplish
+their task.
+</Para>
+
+<Para>
+The global structure of the above type, <Literal remap="tt">file&lowbar;info</Literal>, is initialized by
+<Literal remap="tt">init&lowbar;file&lowbar;info</Literal> in <Literal remap="tt">file&lowbar;com.c</Literal>, which is called by the
+<Literal remap="tt">type&lowbar;ext2&lowbar;inode&lowbar;&lowbar;&lowbar;file</Literal> function when the user requests to watch the
+file. <Literal remap="tt">It is updated as necessary to provide accurate information as long as
+the file is edited.</Literal>
+</Para>
+
+<Sect2>
+<Title>Returning to the file's inode</Title>
+
+<Para>
+Concerning the method I used to handle files, the above task is trivial:
+
+<ProgramListing>
+void type_file___inode (char *command_line)
+
+{
+ dispatch ("settype ext2_inode");
+}
+</ProgramListing>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>File movement</Title>
+
+<Para>
+EXT2ED keeps track of the current position in the file. Movement inside the
+current block is done using <Literal remap="tt">next, prev and offset</Literal> - They just change
+<Literal remap="tt">file&lowbar;info.offset&lowbar;in&lowbar;block</Literal>.
+</Para>
+
+<Para>
+Movement between blocks is done using <Literal remap="tt">nextblock, prevblock and block</Literal>.
+To accomplish this, the direct blocks, indirect blocks, etc, need to be
+traced. This is done by <Literal remap="tt">file&lowbar;block&lowbar;to&lowbar;global&lowbar;block</Literal>, which accepts a
+file's internal block number, and converts it to the actual filesystem block
+number.
+</Para>
+
+<Para>
+
+<ProgramListing>
+long file_block_to_global_block (long file_block,struct struct_file_info *file_info_ptr)
+
+{
+ long last_direct,last_indirect,last_dindirect;
+ long f_indirect,s_indirect;
+
+ last_direct=EXT2_NDIR_BLOCKS-1;
+ last_indirect=last_direct+file_system_info.block_size/4;
+ last_dindirect=last_indirect+(file_system_info.block_size/4) \
+ *(file_system_info.block_size/4);
+
+ if (file_block &#60;= last_direct) {
+ file_info_ptr-&#62;level=0;
+ return (file_info_ptr-&#62;inode_ptr-&#62;i_block [file_block]);
+ }
+
+ if (file_block &#60;= last_indirect) {
+ file_info_ptr-&#62;level=1;
+ file_block=file_block-last_direct-1;
+ return (return_indirect (file_info_ptr-&#62;inode_ptr-&#62; \
+ i_block [EXT2_IND_BLOCK],file_block));
+ }
+
+ if (file_block &#60;= last_dindirect) {
+ file_info_ptr-&#62;level=2;
+ file_block=file_block-last_indirect-1;
+ return (return_dindirect (file_info_ptr-&#62;inode_ptr-&#62; \
+ i_block [EXT2_DIND_BLOCK],file_block));
+ }
+
+ file_info_ptr-&#62;level=3;
+ file_block=file_block-last_dindirect-1;
+ return (return_tindirect (file_info_ptr-&#62;inode_ptr-&#62; \
+ i_block [EXT2_TIND_BLOCK],file_block));
+}
+</ProgramListing>
+
+<Literal remap="tt">last&lowbar;direct, last&lowbar;indirect, etc</Literal>, contain the last internal block number
+which is accessed by this method - If the requested block is smaller then
+<Literal remap="tt">last&lowbar;direct</Literal>, for example, it is a direct block.
+</Para>
+
+<Para>
+If the block is a direct block, its number is just taken from the inode.
+A non-direct block is handled by <Literal remap="tt">return&lowbar;indirect, return&lowbar;dindirect and
+return&lowbar;tindirect</Literal>, which correspond to indirect, double-indirect and
+triple-indirect. Each of the above functions is constructed using the lower
+level functions. For example, <Literal remap="tt">return&lowbar;dindirect</Literal> is constructed as
+follows:
+</Para>
+
+<Para>
+
+<ProgramListing>
+long return_dindirect (long table_block,long block_num)
+
+{
+ long f_indirect;
+
+ f_indirect=block_num/(file_system_info.block_size/4);
+ f_indirect=return_indirect (table_block,f_indirect);
+ return (return_indirect (f_indirect,block_num%(file_system_info.block_size/4)));
+}
+</ProgramListing>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Object memory</Title>
+
+<Para>
+The <Literal remap="tt">remember</Literal> command is overridden here and in the <Literal remap="tt">dir</Literal> type -
+We just remember the inode of the file. It is just simpler to implement, and
+doesn't seem like a big limitation.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Changing data</Title>
+
+<Para>
+The <Literal remap="tt">set</Literal> command is overridden, and provides the same functionality
+like the usage of the <Literal remap="tt">general set</Literal> command with no type declared. The
+<Literal remap="tt">writedata</Literal> is overridden so that we'll write the edited block
+(file&lowbar;info.buffer) and not <Literal remap="tt">type&lowbar;data</Literal> (Which contains the inode).
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Directories</Title>
+
+<Para>
+A directory is just a file which is formatted according to a special format.
+As such, EXT2ED handles directories and files quite alike. Specifically, the
+same variable of type <Literal remap="tt">struct&lowbar;file&lowbar;info</Literal> which is used in the
+<Literal remap="tt">file</Literal>, is used here.
+</Para>
+
+<Para>
+The <Literal remap="tt">dir</Literal> type uses all the variables in the above structure, as
+opposed to the <Literal remap="tt">file</Literal> type, which didn't use the last ones.
+</Para>
+
+<Sect2>
+<Title>The search&lowbar;dir&lowbar;entries function</Title>
+
+<Para>
+The entire situation is similar to that which was described in the
+<Literal remap="tt">file</Literal> type, with one main change:
+</Para>
+
+<Para>
+The main function in <Literal remap="tt">dir&lowbar;com.c</Literal> is <Literal remap="tt">search&lowbar;dir&lowbar;entries</Literal>. This
+function will <Literal remap="tt">"run"</Literal> on the entire entries in the directory, and will
+call a client's function each time. The client's function is supplied as an
+argument, and will check the current entry for a match, based on its own
+criterion. It will then signal <Literal remap="tt">search&lowbar;dir&lowbar;entries</Literal> whether to
+<Literal remap="tt">ABORT</Literal> the search, whether it <Literal remap="tt">FOUND</Literal> the entry it was looking
+for, or that the entry is still not found, and we should <Literal remap="tt">CONTINUE</Literal>
+searching. Follows the declaration:
+
+<ProgramListing>
+struct struct_file_info search_dir_entries \
+ (int (*action) (struct struct_file_info *info),int *status)
+
+/*
+ This routine runs on all directory entries in the current directory.
+ For each entry, action is called. The return code of action is one of
+ the following:
+
+ ABORT - Current dir entry is returned.
+ CONTINUE - Continue searching.
+ FOUND - Current dir entry is returned.
+
+ If the last entry is reached, it is returned, along with an ABORT status.
+
+ status is updated to the returned code of action.
+*/
+</ProgramListing>
+
+</Para>
+
+<Para>
+With the above tool in hand, many operations are simple to perform - Here is
+the way I counted the entries in the current directory:
+</Para>
+
+<Para>
+
+<ProgramListing>
+long count_dir_entries (void)
+
+{
+ int status;
+
+ return (search_dir_entries (&amp;action_count,&amp;status).dir_entry_num);
+}
+
+int action_count (struct struct_file_info *info)
+
+{
+ return (CONTINUE);
+}
+</ProgramListing>
+
+It will just <Literal remap="tt">CONTINUE</Literal> until the last entry. The returned structure
+(of type <Literal remap="tt">struct&lowbar;file&lowbar;info</Literal>) will have its number in the
+<Literal remap="tt">dir&lowbar;entry&lowbar;num</Literal> field, and this is exactly the required number!
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The cd command</Title>
+
+<Para>
+The <Literal remap="tt">cd</Literal> command accepts a relative path, and moves there ...
+The implementation is of-course a bit more complicated:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ The path is checked that it is not an absolute path (from <Literal remap="tt">/</Literal>).
+If it is, we let the <Literal remap="tt">general cd</Literal> to do the job by calling
+directly <Literal remap="tt">type&lowbar;ext2&lowbar;&lowbar;&lowbar;cd</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The path is divided into the nearest path and the rest of the path.
+For example, cd 1/2/3/4 is divided into <Literal remap="tt">1</Literal> and into
+<Literal remap="tt">2/3/4</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ It is the first part of the path that we need to search for in the
+current directory. We search for it using <Literal remap="tt">search&lowbar;dir&lowbar;entries</Literal>,
+which accepts the <Literal remap="tt">action&lowbar;name</Literal> function as the user defined
+function.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">search&lowbar;dir&lowbar;entries</Literal> will scan the entire entries and will call
+our <Literal remap="tt">action&lowbar;name</Literal> function for each entry. In
+<Literal remap="tt">action&lowbar;name</Literal>, the required name will be checked against the
+name of the current entry, and <Literal remap="tt">FOUND</Literal> will be returned when a
+match occurs.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ If the required entry is found, we dispatch a <Literal remap="tt">remember</Literal>
+command to insert the current <Literal remap="tt">inode</Literal> into the object memory.
+This is required to easily support <Literal remap="tt">symbolic links</Literal> - If we
+find later that the inode pointed by the entry is actually a
+symbolic link, we'll need to return to this point, and the above
+inode doesn't have (and can't have, because of <Literal remap="tt">hard links</Literal>) the
+information necessary to "move back".
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ We then dispatch a <Literal remap="tt">followinode</Literal> command to reach the inode
+pointed by the required entry. This command will automatically
+change the type to <Literal remap="tt">ext2&lowbar;inode</Literal> - We are now at an inode, and
+all the inode commands are available.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ We check the inode's type to see if it is a directory. If it is, we
+dispatch a <Literal remap="tt">dir</Literal> command to "enter the directory", and
+recursively call ourself (The type is <Literal remap="tt">dir</Literal> again) by
+dispatching a <Literal remap="tt">cd</Literal> command, with the rest of the path as an
+argument.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ If the inode's type is a symbolic link (only fast symbolic link were
+meanwhile implemented. I guess this is typically the case.), we note
+the path it is pointing at, the saved inode is recalled, we dispatch
+<Literal remap="tt">dir</Literal> to get back to the original directory, and we call
+ourself again with the <Literal remap="tt">link path/rest of the path</Literal> argument.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In any other case, we just stop at the resulting inode.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The block and inode allocation bitmaps</Title>
+
+<Para>
+The block allocation bitmap is reached by the corresponding group descriptor.
+The group descriptor handling functions will save the necessary information
+into a structure of the <Literal remap="tt">struct&lowbar;block&lowbar;bitmap&lowbar;info</Literal> type:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct struct_block_bitmap_info {
+ unsigned long entry_num;
+ unsigned long group_num;
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+The <Literal remap="tt">show</Literal> command is overridden, and will show the block as a series of
+bits, each bit corresponding to a block. The main variable is the
+<Literal remap="tt">entry&lowbar;num</Literal> variable, declared above, which is just the current block
+number in this block group. The current entry is highlighted, and the
+<Literal remap="tt">next, prev and entry</Literal> commands just change the above variable.
+</Para>
+
+<Para>
+The <Literal remap="tt">allocate and deallocate</Literal> change the specified bits. Nothing
+special about them - They just contain code which converts between bit and
+byte locations.
+</Para>
+
+<Para>
+The <Literal remap="tt">inode allocation bitmap</Literal> is treated in much the same fashion, with
+the same commands available.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Filesystem size limitation</Title>
+
+<Para>
+While an ext2 filesystem has a size limit of <Literal remap="tt">4 TB</Literal>, EXT2ED currently
+<Literal remap="tt">can't</Literal> handle filesystems which are <Literal remap="tt">bigger than 2 GB</Literal>.
+</Para>
+
+<Para>
+This limitation results from my usage of <Literal remap="tt">32 bit long variables</Literal> and
+of the <Literal remap="tt">fseek</Literal> filesystem call, which can't seek up to 4 TB.
+</Para>
+
+<Para>
+By looking in the <Literal remap="tt">ext2 library</Literal> source code by <Literal remap="tt">Theodore Ts'o</Literal>,
+I discovered the <Literal remap="tt">llseek</Literal> system call which can seek to a
+<Literal remap="tt">64 bit unsigned long long</Literal> offset. Correcting the situation is not
+difficult in concept - I need to change long into unsigned long long where
+appropriate and modify <Literal remap="tt">disk.c</Literal> to use the llseek system call.
+</Para>
+
+<Para>
+However, fixing the above limitation involves making changes in many places
+in the code and will obviously make the entire code less stable. For that
+reason, I chose to release EXT2ED as it is now and to postpone the above fix
+to the next release.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Conclusion</Title>
+
+<Para>
+Had I known in advance the structure of the ext2 filesystem, I feel that
+the resulting design would have been quite different from the presented
+design above.
+</Para>
+
+<Para>
+EXT2ED has now two levels of abstraction - A <Literal remap="tt">general</Literal> filesystem, and an
+<Literal remap="tt">ext2</Literal> filesystem, and the surface is more or less prepared for additions
+of other filesystems. Had I approached the design in the "engineering" way,
+I guess that the first level above would not have existed.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Copyright</Title>
+
+<Para>
+EXT2ED is Copyright (C) 1995 Gadi Oxman.
+</Para>
+
+<Para>
+EXT2ED is hereby placed under the GPL - Gnu Public License. You are free and
+welcome to copy, view and modify the sources. My only wish is that my
+copyright presented above will be left and that a list of the bug fixes,
+added features, etc, will be provided.
+</Para>
+
+<Para>
+The entire EXT2ED project is based, of-course, on the kernel sources. The
+<Literal remap="tt">ext2.descriptors</Literal> distributed with EXT2ED is a slightly modified
+version of the main ext2 include file, /usr/include/linux/ext2&lowbar;fs.h. Follows
+the original copyright:
+</Para>
+
+<Para>
+
+<ProgramListing>
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+</ProgramListing>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Acknowledgments</Title>
+
+<Para>
+EXT2ED was constructed as a student project in the software
+laboratory of the faculty of electrical-engineering in the
+<Literal remap="tt">Technion - Israel's institute of technology</Literal>.
+</Para>
+
+<Para>
+At first, I would like to thank <Literal remap="tt">Avner Lottem</Literal> and <Literal remap="tt">Doctor Ilana
+David</Literal> for their interest and assistance in this project.
+</Para>
+
+<Para>
+I would also like to thank the following people, who were involved in the
+design and implementation of the ext2 filesystem kernel code and support
+utilities:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Remy Card</Literal>
+
+Who designed, implemented and maintains the ext2 filesystem kernel
+code, and some of the ext2 utilities. <Literal remap="tt">Remy Card</Literal> is also the
+author of several helpful slides concerning the ext2 filesystem.
+Specifically, he is the author of <Literal remap="tt">File Management in the Linux
+Kernel</Literal> and of <Literal remap="tt">The Second Extended File System - Current
+State, Future Development</Literal>.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Wayne Davison</Literal>
+
+Who designed the ext2 filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Stephen Tweedie</Literal>
+
+Who helped designing the ext2 filesystem kernel code and wrote the
+slides <Literal remap="tt">Optimizations in File Systems</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Theodore Ts'o</Literal>
+
+Who is the author of several ext2 utilities and of the ext2 library
+<Literal remap="tt">libext2fs</Literal> (which I didn't use, simply because I didn't know
+it exists when I started to work on my project).
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Lastly, I would like to thank, of-course, <Literal remap="tt">Linus Torvalds</Literal> and the
+<Literal remap="tt">Linux community</Literal> for providing all of us with such a great operating
+system.
+</Para>
+
+<Para>
+Please contact me in a case of bug report, suggestions, or just about
+anything concerning EXT2ED.
+</Para>
+
+<Para>
+Enjoy,
+</Para>
+
+<Para>
+Gadi Oxman &lt;tgud@tochnapc2.technion.ac.il&gt;
+</Para>
+
+<Para>
+Haifa, August 95
+</Para>
+
+</Sect1>
+
+</Article>
diff --git a/ext2ed/doc/ext2fs-overview.sgml b/ext2ed/doc/ext2fs-overview.sgml
new file mode 100644
index 0000000..0d54f07
--- /dev/null
+++ b/ext2ed/doc/ext2fs-overview.sgml
@@ -0,0 +1,1569 @@
+<!DOCTYPE Article PUBLIC "-//Davenport//DTD DocBook V3.0//EN">
+
+<Article>
+
+<ArtHeader>
+
+<Title>The extended-2 filesystem overview</Title>
+<AUTHOR
+>
+<FirstName>Gadi Oxman, tgud@tochnapc2.technion.ac.il</FirstName>
+</AUTHOR
+>
+<PubDate>v0.1, August 3 1995</PubDate>
+
+</ArtHeader>
+
+<Sect1>
+<Title>Preface</Title>
+
+<Para>
+This document attempts to present an overview of the internal structure of
+the ext2 filesystem. It was written in summer 95, while I was working on the
+<Literal remap="tt">ext2 filesystem editor project (EXT2ED)</Literal>.
+</Para>
+
+<Para>
+In the process of constructing EXT2ED, I acquired knowledge of the various
+design aspects of the the ext2 filesystem. This document is a result of an
+effort to document this knowledge.
+</Para>
+
+<Para>
+This is only the initial version of this document. It is obviously neither
+error-prone nor complete, but at least it provides a starting point.
+</Para>
+
+<Para>
+In the process of learning the subject, I have used the following sources /
+tools:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Experimenting with EXT2ED, as it was developed.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The ext2 kernel sources:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The main ext2 include file,
+<FILENAME>/usr/include/linux/ext2&lowbar;fs.h</FILENAME>
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The contents of the directory <FILENAME>/usr/src/linux/fs/ext2</FILENAME>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The VFS layer sources (only a bit).
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The slides: The Second Extended File System, Current State, Future
+Development, by <personname><firstname>Remy</firstname> <surname>Card</surname></personname>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The slides: Optimisation in File Systems, by <personname><firstname>Stephen</firstname> <surname>Tweedie</surname></personname>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The various ext2 utilities.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Introduction</Title>
+
+<Para>
+The <Literal remap="tt">Second Extended File System (Ext2fs)</Literal> is very popular among Linux
+users. If you use Linux, chances are that you are using the ext2 filesystem.
+</Para>
+
+<Para>
+Ext2fs was designed by <personname><firstname>Remy</firstname> <surname>Card</surname></personname> and <personname><firstname>Wayne</firstname> <surname>Davison</surname></personname>. It was
+implemented by <personname><firstname>Remy</firstname> <surname>Card</surname></personname> and was further enhanced by <personname><firstname>Stephen</firstname>
+<surname>Tweedie</surname></personname> and <personname><firstname>Theodore</firstname> <surname>Ts'o</surname></personname>.
+</Para>
+
+<Para>
+The ext2 filesystem is still under development. I will document here
+version 0.5a, which is distributed along with Linux 1.2.x. At this time of
+writing, the most recent version of Linux is 1.3.13, and the version of the
+ext2 kernel source is 0.5b. A lot of fancy enhancements are planned for the
+ext2 filesystem in Linux 1.3, so stay tuned.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>A filesystem - Why do we need it?</Title>
+
+<Para>
+I thought that before we dive into the various small details, I'll reserve a
+few minutes for the discussion of filesystems from a general point of view.
+</Para>
+
+<Para>
+A <Literal remap="tt">filesystem</Literal> consists of two word - <Literal remap="tt">file</Literal> and <Literal remap="tt">system</Literal>.
+</Para>
+
+<Para>
+Everyone knows the meaning of the word <Literal remap="tt">file</Literal> - A bunch of data put
+somewhere. where? This is an important question. I, for example, usually
+throw almost everything into a single drawer, and have difficulties finding
+something later.
+</Para>
+
+<Para>
+This is where the <Literal remap="tt">system</Literal> comes in - Instead of just throwing the data
+to the device, we generalize and construct a <Literal remap="tt">system</Literal> which will
+virtualize for us a nice and ordered structure in which we could arrange our
+data in much the same way as books are arranged in a library. The purpose of
+the filesystem, as I understand it, is to make it easy for us to update and
+maintain our data.
+</Para>
+
+<Para>
+Normally, by <Literal remap="tt">mounting</Literal> filesystems, we just use the nice and logical
+virtual structure. However, the disk knows nothing about that - The device
+driver views the disk as a large continuous paper in which we can write notes
+wherever we wish. It is the task of the filesystem management code to store
+bookkeeping information which will serve the kernel for showing us the nice
+and ordered virtual structure.
+</Para>
+
+<Para>
+In this document, we consider one particular administrative structure - The
+Second Extended Filesystem.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The Linux VFS layer</Title>
+
+<Para>
+When Linux was first developed, it supported only one filesystem - The
+<Literal remap="tt">Minix</Literal> filesystem. Today, Linux has the ability to support several
+filesystems concurrently. This was done by the introduction of another layer
+between the kernel and the filesystem code - The Virtual File System (VFS).
+</Para>
+
+<Para>
+The kernel "speaks" with the VFS layer. The VFS layer passes the kernel's
+request to the proper filesystem management code. I haven't learned much of
+the VFS layer as I didn't need it for the construction of EXT2ED so that I
+can't elaborate on it. Just be aware that it exists.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>About blocks and block groups</Title>
+
+<Para>
+In order to ease management, the ext2 filesystem logically divides the disk
+into small units called <Literal remap="tt">blocks</Literal>. A block is the smallest unit which
+can be allocated. Each block in the filesystem can be <Literal remap="tt">allocated</Literal> or
+<Literal remap="tt">free</Literal>.
+<FOOTNOTE>
+
+<Para>
+The Ext2fs source code refers to the concept of <Literal remap="tt">fragments</Literal>, which I
+believe are supposed to be sub-block allocations. As far as I know,
+fragments are currently unsupported in Ext2fs.
+</Para>
+
+</FOOTNOTE>
+
+The block size can be selected to be 1024, 2048 or 4096 bytes when creating
+the filesystem.
+</Para>
+
+<Para>
+Ext2fs groups together a fixed number of sequential blocks into a <Literal remap="tt">group
+block</Literal>. The resulting situation is that the filesystem is managed as a
+series of group blocks. This is done in order to keep related information
+physically close on the disk and to ease the management task. As a result,
+much of the filesystem management reduces to management of a single blocks
+group.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The view of inodes from the point of view of a blocks group</Title>
+
+<Para>
+Each file in the filesystem is reserved a special <Literal remap="tt">inode</Literal>. I don't want
+to explain inodes now. Rather, I would like to treat it as another resource,
+much like a <Literal remap="tt">block</Literal> - Each blocks group contains a limited number of
+inode, while any specific inode can be <Literal remap="tt">allocated</Literal> or
+<Literal remap="tt">unallocated</Literal>.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The group descriptors</Title>
+
+<Para>
+Each blocks group is accompanied by a <Literal remap="tt">group descriptor</Literal>. The group
+descriptor summarizes some necessary information about the specific group
+block. Follows the definition of the group descriptor, as defined in
+<FILENAME>/usr/include/linux/ext2&lowbar;fs.h</FILENAME>:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[3];
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+The last three variables: <Literal remap="tt">bg&lowbar;free&lowbar;blocks&lowbar;count, bg&lowbar;free&lowbar;inodes&lowbar;count and bg&lowbar;used&lowbar;dirs&lowbar;count</Literal> provide statistics about the use of the three
+resources in a blocks group - The <Literal remap="tt">blocks</Literal>, the <Literal remap="tt">inodes</Literal> and the
+<Literal remap="tt">directories</Literal>. I believe that they are used by the kernel for balancing
+the load between the various blocks groups.
+</Para>
+
+<Para>
+<Literal remap="tt">bg&lowbar;block&lowbar;bitmap</Literal> contains the block number of the <Literal remap="tt">block allocation
+bitmap block</Literal>. This is used to allocate / deallocate each block in the
+specific blocks group.
+</Para>
+
+<Para>
+<Literal remap="tt">bg&lowbar;inode&lowbar;bitmap</Literal> is fully analogous to the previous variable - It
+contains the block number of the <Literal remap="tt">inode allocation bitmap block</Literal>, which
+is used to allocate / deallocate each specific inode in the filesystem.
+</Para>
+
+<Para>
+<Literal remap="tt">bg&lowbar;inode&lowbar;table</Literal> contains the block number of the start of the
+<Literal remap="tt">inode table of the current blocks group</Literal>. The <Literal remap="tt">inode table</Literal> is
+just the actual inodes which are reserved for the current block.
+</Para>
+
+<Para>
+The block bitmap block, inode bitmap block and the inode table are created
+when the filesystem is created.
+</Para>
+
+<Para>
+The group descriptors are placed one after the other. Together they make the
+<Literal remap="tt">group descriptors table</Literal>.
+</Para>
+
+<Para>
+Each blocks group contains the entire table of group descriptors in its
+second block, right after the superblock. However, only the first copy (in
+group 0) is actually used by the kernel. The other copies are there for
+backup purposes and can be of use if the main copy gets corrupted.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The block bitmap allocation block</Title>
+
+<Para>
+Each blocks group contains one special block which is actually a map of the
+entire blocks in the group, with respect to their allocation status. Each
+<Literal remap="tt">bit</Literal> in the block bitmap indicated whether a specific block in the
+group is used or free.
+</Para>
+
+<Para>
+The format is actually quite simple - Just view the entire block as a series
+of bits. For example,
+</Para>
+
+<Para>
+Suppose the block size is 1024 bytes. As such, there is a place for
+1024*8=8192 blocks in a group block. This number is one of the fields in the
+filesystem's <Literal remap="tt">superblock</Literal>, which will be explained later.
+</Para>
+
+<Para>
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Block 0 in the blocks group is managed by bit 0 of byte 0 in the bitmap
+block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Block 7 in the blocks group is managed by bit 7 of byte 0 in the bitmap
+block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Block 8 in the blocks group is managed by bit 0 of byte 1 in the bitmap
+block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Block 8191 in the blocks group is managed by bit 7 of byte 1023 in the
+bitmap block.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+A value of "<Literal remap="tt">1</Literal>" in the appropriate bit signals that the block is
+allocated, while a value of "<Literal remap="tt">0</Literal>" signals that the block is
+unallocated.
+</Para>
+
+<Para>
+You will probably notice that typically, all the bits in a byte contain the
+same value, making the byte's value <Literal remap="tt">0</Literal> or <Literal remap="tt">0ffh</Literal>. This is done by
+the kernel on purpose in order to group related data in physically close
+blocks, since the physical device is usually optimized to handle such a close
+relationship.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The inode allocation bitmap</Title>
+
+<Para>
+The format of the inode allocation bitmap block is exactly like the format of
+the block allocation bitmap block. The explanation above is valid here, with
+the work <Literal remap="tt">block</Literal> replaced by <Literal remap="tt">inode</Literal>. Typically, there are much less
+inodes then blocks in a blocks group and thus only part of the inode bitmap
+block is used. The number of inodes in a blocks group is another variable
+which is listed in the <Literal remap="tt">superblock</Literal>.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>On the inode and the inode tables</Title>
+
+<Para>
+An inode is a main resource in the ext2 filesystem. It is used for various
+purposes, but the main two are:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Support of files
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Support of directories
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Each file, for example, will allocate one inode from the filesystem
+resources.
+</Para>
+
+<Para>
+An ext2 filesystem has a total number of available inodes which is determined
+while creating the filesystem. When all the inodes are used, for example, you
+will not be able to create an additional file even though there will still
+be free blocks on the filesystem.
+</Para>
+
+<Para>
+Each inode takes up 128 bytes in the filesystem. By default, <Literal remap="tt">mke2fs</Literal>
+reserves an inode for each 4096 bytes of the filesystem space.
+</Para>
+
+<Para>
+The inodes are placed in several tables, each of which contains the same
+number of inodes and is placed at a different blocks group. The goal is to
+place inodes and their related files in the same blocks group because of
+locality arguments.
+</Para>
+
+<Para>
+The number of inodes in a blocks group is available in the superblock variable
+<Literal remap="tt">s&lowbar;inodes&lowbar;per&lowbar;group</Literal>. For example, if there are 2000 inodes per group,
+group 0 will contain the inodes 1-2000, group 2 will contain the inodes
+2001-4000, and so on.
+</Para>
+
+<Para>
+Each inode table is accessed from the group descriptor of the specific
+blocks group which contains the table.
+</Para>
+
+<Para>
+Follows the structure of an inode in Ext2fs:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ union {
+ struct {
+ __u32 l_i_reserved1;
+ } linux1;
+ struct {
+ __u32 h_i_translator;
+ } hurd1;
+ struct {
+ __u32 m_i_reserved1;
+ } masix1;
+ } osd1; /* OS dependent 1 */
+ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
+ __u32 i_version; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_size_high; /* High 32bits of size */
+ __u32 i_faddr; /* Fragment address */
+ union {
+ struct {
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u32 l_i_reserved2[2];
+ } linux2;
+ struct {
+ __u8 h_i_frag; /* Fragment number */
+ __u8 h_i_fsize; /* Fragment size */
+ __u16 h_i_mode_high;
+ __u16 h_i_uid_high;
+ __u16 h_i_gid_high;
+ __u32 h_i_author;
+ } hurd2;
+ struct {
+ __u8 m_i_frag; /* Fragment number */
+ __u8 m_i_fsize; /* Fragment size */
+ __u16 m_pad1;
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
+};
+</ProgramListing>
+
+</Para>
+
+<Sect2>
+<Title>The allocated blocks</Title>
+
+<Para>
+The basic functionality of an inode is to group together a series of
+allocated blocks. There is no limitation on the allocated blocks - Each
+block can be allocated to each inode. Nevertheless, block allocation will
+usually be done in series to take advantage of the locality principle.
+</Para>
+
+<Para>
+The inode is not always used in that way. I will now explain the allocation
+of blocks, assuming that the current inode type indeed refers to a list of
+allocated blocks.
+</Para>
+
+<Para>
+It was found experimentally that many of the files in the filesystem are
+actually quite small. To take advantage of this effect, the kernel provides
+storage of up to 12 block numbers in the inode itself. Those blocks are
+called <Literal remap="tt">direct blocks</Literal>. The advantage is that once the kernel has the
+inode, it can directly access the file's blocks, without an additional disk
+access. Those 12 blocks are directly specified in the variables
+<Literal remap="tt">i&lowbar;block[0] to i&lowbar;block[11]</Literal>.
+</Para>
+
+<Para>
+<Literal remap="tt">i&lowbar;block[12]</Literal> is the <Literal remap="tt">indirect block</Literal> - The block pointed by
+i&lowbar;block&lsqb;12] will <Literal remap="tt">not</Literal> be a data block. Rather, it will just contain a
+list of direct blocks. For example, if the block size is 1024 bytes, since
+each block number is 4 bytes long, there will be place for 256 indirect
+blocks. That is, block 13 till block 268 in the file will be accessed by the
+<Literal remap="tt">indirect block</Literal> method. The penalty in this case, compared to the
+direct blocks case, is that an additional access to the device is needed -
+We need <Literal remap="tt">two</Literal> accesses to reach the required data block.
+</Para>
+
+<Para>
+In much the same way, <Literal remap="tt">i&lowbar;block[13]</Literal> is the <Literal remap="tt">double indirect block</Literal>
+and <Literal remap="tt">i&lowbar;block[14]</Literal> is the <Literal remap="tt">triple indirect block</Literal>.
+</Para>
+
+<Para>
+<Literal remap="tt">i&lowbar;block[13]</Literal> points to a block which contains pointers to indirect
+blocks. Each one of them is handled in the way described above.
+</Para>
+
+<Para>
+In much the same way, the triple indirect block is just an additional level
+of indirection - It will point to a list of double indirect blocks.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The i&lowbar;mode variable</Title>
+
+<Para>
+The i&lowbar;mode variable is used to determine the <Literal remap="tt">inode type</Literal> and the
+associated <Literal remap="tt">permissions</Literal>. It is best described by representing it as an
+octal number. Since it is a 16 bit variable, there will be 6 octal digits.
+Those are divided into two parts - The rightmost 4 digits and the leftmost 2
+digits.
+</Para>
+
+<Sect3>
+<Title>The rightmost 4 octal digits</Title>
+
+<Para>
+The rightmost 4 digits are <Literal remap="tt">bit options</Literal> - Each bit has its own
+purpose.
+</Para>
+
+<Para>
+The last 3 digits (Octal digits 0,1 and 2) are just the usual permissions,
+in the known form <Literal remap="tt">rwxrwxrwx</Literal>. Digit 2 refers to the user, digit 1 to
+the group and digit 2 to everyone else. They are used by the kernel to grant
+or deny access to the object presented by this inode.
+<FOOTNOTE>
+
+<Para>
+A <Literal remap="tt">smarter</Literal> permissions control is one of the enhancements planned for
+Linux 1.3 - The ACL (Access Control Lists). Actually, from browsing of the
+kernel source, some of the ACL handling is already done.
+</Para>
+
+</FOOTNOTE>
+
+</Para>
+
+<Para>
+Bit number 9 signals that the file (I'll refer to the object presented by
+the inode as file even though it can be a special device, for example) is
+<Literal remap="tt">set VTX</Literal>. I still don't know what is the meaning of "VTX".
+</Para>
+
+<Para>
+Bit number 10 signals that the file is <Literal remap="tt">set group id</Literal> - I don't know
+exactly the meaning of the above either.
+</Para>
+
+<Para>
+Bit number 11 signals that the file is <Literal remap="tt">set user id</Literal>, which means that
+the file will run with an effective user id root.
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>The leftmost two octal digits</Title>
+
+<Para>
+Note the the leftmost octal digit can only be 0 or 1, since the total number
+of bits is 16.
+</Para>
+
+<Para>
+Those digits, as opposed to the rightmost 4 digits, are not bit mapped
+options. They determine the type of the "file" to which the inode belongs:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">01</Literal> - The file is a <Literal remap="tt">FIFO</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">02</Literal> - The file is a <Literal remap="tt">character device</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">04</Literal> - The file is a <Literal remap="tt">directory</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">06</Literal> - The file is a <Literal remap="tt">block device</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">10</Literal> - The file is a <Literal remap="tt">regular file</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">12</Literal> - The file is a <Literal remap="tt">symbolic link</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">14</Literal> - The file is a <Literal remap="tt">socket</Literal>.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+<Sect2>
+<Title>Time and date</Title>
+
+<Para>
+Linux records the last time in which various operations occurred with the
+file. The time and date are saved in the standard C library format - The
+number of seconds which passed since 00:00:00 GMT, January 1, 1970. The
+following times are recorded:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">i&lowbar;ctime</Literal> - The time in which the inode was last allocated. In
+other words, the time in which the file was created.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">i&lowbar;mtime</Literal> - The time in which the file was last modified.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">i&lowbar;atime</Literal> - The time in which the file was last accessed.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">i&lowbar;dtime</Literal> - The time in which the inode was deallocated. In
+other words, the time in which the file was deleted.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>i&lowbar;size</Title>
+
+<Para>
+<Literal remap="tt">i&lowbar;size</Literal> contains information about the size of the object presented by
+the inode. If the inode corresponds to a regular file, this is just the size
+of the file in bytes. In other cases, the interpretation of the variable is
+different.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>User and group id</Title>
+
+<Para>
+The user and group id of the file are just saved in the variables
+<Literal remap="tt">i&lowbar;uid</Literal> and <Literal remap="tt">i&lowbar;gid</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Hard links</Title>
+
+<Para>
+Later, when we'll discuss the implementation of directories, it will be
+explained that each <Literal remap="tt">directory entry</Literal> points to an inode. It is quite
+possible that a <Literal remap="tt">single inode</Literal> will be pointed to from <Literal remap="tt">several</Literal>
+directories. In that case, we say that there exist <Literal remap="tt">hard links</Literal> to the
+file - The file can be accessed from each of the directories.
+</Para>
+
+<Para>
+The kernel keeps track of the number of hard links in the variable
+<Literal remap="tt">i&lowbar;links&lowbar;count</Literal>. The variable is set to "1" when first allocating the
+inode, and is incremented with each additional link. Deletion of a file will
+delete the current directory entry and will decrement the number of links.
+Only when this number reaches zero, the inode will be actually deallocated.
+</Para>
+
+<Para>
+The name <Literal remap="tt">hard link</Literal> is used to distinguish between the alias method
+described above, to another alias method called <Literal remap="tt">symbolic linking</Literal>,
+which will be described later.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>The Ext2fs extended flags</Title>
+
+<Para>
+The ext2 filesystem associates additional flags with an inode. The extended
+attributes are stored in the variable <Literal remap="tt">i&lowbar;flags</Literal>. <Literal remap="tt">i&lowbar;flags</Literal> is a 32
+bit variable. Only the 7 rightmost bits are defined. Of them, only 5 bits
+are used in version 0.5a of the filesystem. Specifically, the
+<Literal remap="tt">undelete</Literal> and the <Literal remap="tt">compress</Literal> features are not implemented, and
+are to be introduced in Linux 1.3 development.
+</Para>
+
+<Para>
+The currently available flags are:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ bit 0 - Secure deletion.
+
+When this bit is on, the file's blocks are zeroed when the file is
+deleted. With this bit off, they will just be left with their
+original data when the inode is deallocated.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 1 - Undelete.
+
+This bit is not supported yet. It will be used to provide an
+<Literal remap="tt">undelete</Literal> feature in future Ext2fs developments.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 2 - Compress file.
+
+This bit is also not supported. The plan is to offer "compression on
+the fly" in future releases.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 3 - Synchronous updates.
+
+With this bit on, the meta-data will be written synchronously to the
+disk, as if the filesystem was mounted with the "sync" mount option.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 4 - Immutable file.
+
+When this bit is on, the file will stay as it is - Can not be
+changed, deleted, renamed, no hard links, etc, before the bit is
+cleared.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 5 - Append only file.
+
+With this option active, data will only be appended to the file.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ bit 6 - Do not dump this file.
+
+I think that this bit is used by the port of dump to linux (ported by
+<Literal remap="tt">Remy Card</Literal>) to check if the file should not be dumped.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Symbolic links</Title>
+
+<Para>
+The <Literal remap="tt">hard links</Literal> presented above are just another pointers to the same
+inode. The important aspect is that the inode number is <Literal remap="tt">fixed</Literal> when
+the link is created. This means that the implementation details of the
+filesystem are visible to the user - In a pure abstract usage of the
+filesystem, the user should not care about inodes.
+</Para>
+
+<Para>
+The above causes several limitations:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Hard links can be done only in the same filesystem. This is obvious,
+since a hard link is just an inode number in some directory entry,
+and the above elements are filesystem specific.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ You can not "replace" the file which is pointed to by the hard link
+after the link creation. "Replacing" the file in one directory will
+still leave the original file in the other directory - The
+"replacement" will not deallocate the original inode, but rather
+allocate another inode for the new version, and the directory entry
+at the other place will just point to the old inode number.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+<Literal remap="tt">Symbolic link</Literal>, on the other hand, is analyzed at <Literal remap="tt">run time</Literal>. A
+symbolic link is just a <Literal remap="tt">pathname</Literal> which is accessible from an inode.
+As such, it "speaks" in the language of the abstract filesystem. When the
+kernel reaches a symbolic link, it will <Literal remap="tt">follow it in run time</Literal> using
+its normal way of reaching directories.
+</Para>
+
+<Para>
+As such, symbolic link can be made <Literal remap="tt">across different filesystems</Literal> and a
+replacement of a file with a new version will automatically be active on all
+its symbolic links.
+</Para>
+
+<Para>
+The disadvantage is that hard link doesn't consume space except to a small
+directory entry. Symbolic link, on the other hand, consumes at least an
+inode, and can also consume one block.
+</Para>
+
+<Para>
+When the inode is identified as a symbolic link, the kernel needs to find
+the path to which it points.
+</Para>
+
+<Sect3>
+<Title>Fast symbolic links</Title>
+
+<Para>
+When the pathname contains up to 64 bytes, it can be saved directly in the
+inode, on the <Literal remap="tt">i&lowbar;block[0] - i&lowbar;block[15]</Literal> variables, since those are not
+needed in that case. This is called <Literal remap="tt">fast</Literal> symbolic link. It is fast
+because the pathname resolution can be done using the inode itself, without
+accessing additional blocks. It is also economical, since it allocates only
+an inode. The length of the pathname is stored in the <Literal remap="tt">i&lowbar;size</Literal>
+variable.
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>Slow symbolic links</Title>
+
+<Para>
+Starting from 65 bytes, additional block is allocated (by the use of
+<Literal remap="tt">i&lowbar;block[0]</Literal>) and the pathname is stored in it. It is called slow
+because the kernel needs to read additional block to resolve the pathname.
+The length is again saved in <Literal remap="tt">i&lowbar;size</Literal>.
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+<Sect2>
+<Title>i&lowbar;version</Title>
+
+<Para>
+<Literal remap="tt">i&lowbar;version</Literal> is used with regard to Network File System. I don't know
+its exact use.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Reserved variables</Title>
+
+<Para>
+As far as I know, the variables which are connected to ACL and fragments
+are not currently used. They will be supported in future versions.
+</Para>
+
+<Para>
+Ext2fs is being ported to other operating systems. As far as I know,
+at least in linux, the os dependent variables are also not used.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Special reserved inodes</Title>
+
+<Para>
+The first ten inodes on the filesystem are special inodes:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Inode 1 is the <Literal remap="tt">bad blocks inode</Literal> - I believe that its data
+blocks contain a list of the bad blocks in the filesystem, which
+should not be allocated.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inode 2 is the <Literal remap="tt">root inode</Literal> - The inode of the root directory.
+It is the starting point for reaching a known path in the filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inode 3 is the <Literal remap="tt">acl index inode</Literal>. Access control lists are
+currently not supported by the ext2 filesystem, so I believe this
+inode is not used.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inode 4 is the <Literal remap="tt">acl data inode</Literal>. Of course, the above applies
+here too.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inode 5 is the <Literal remap="tt">boot loader inode</Literal>. I don't know its
+usage.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inode 6 is the <Literal remap="tt">undelete directory inode</Literal>. It is also a
+foundation for future enhancements, and is currently not used.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Inodes 7-10 are <Literal remap="tt">reserved</Literal> and currently not used.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Directories</Title>
+
+<Para>
+A directory is implemented in the same way as files are implemented (with
+the direct blocks, indirect blocks, etc) - It is just a file which is
+formatted with a special format - A list of directory entries.
+</Para>
+
+<Para>
+Follows the definition of a directory entry:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct ext2_dir_entry {
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u16 name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+</ProgramListing>
+
+</Para>
+
+<Para>
+Ext2fs supports file names of varying lengths, up to 255 bytes. The
+<Literal remap="tt">name</Literal> field above just contains the file name. Note that it is
+<Literal remap="tt">not zero terminated</Literal>; Instead, the variable <Literal remap="tt">name&lowbar;len</Literal> contains
+the length of the file name.
+</Para>
+
+<Para>
+The variable <Literal remap="tt">rec&lowbar;len</Literal> is provided because the directory entries are
+padded with zeroes so that the next entry will be in an offset which is
+a multiplication of 4. The resulting directory entry size is stored in
+<Literal remap="tt">rec&lowbar;len</Literal>. If the directory entry is the last in the block, it is
+padded with zeroes till the end of the block, and rec&lowbar;len is updated
+accordingly.
+</Para>
+
+<Para>
+The <Literal remap="tt">inode</Literal> variable points to the inode of the above file.
+</Para>
+
+<Para>
+Deletion of directory entries is done by appending of the deleted entry
+space to the previous (or next, I am not sure) entry.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>The superblock</Title>
+
+<Para>
+The <Literal remap="tt">superblock</Literal> is a block which contains information which describes
+the state of the internal filesystem.
+</Para>
+
+<Para>
+The superblock is located at the <Literal remap="tt">fixed offset 1024</Literal> in the device. Its
+length is 1024 bytes also.
+</Para>
+
+<Para>
+The superblock, like the group descriptors, is copied on each blocks group
+boundary for backup purposes. However, only the main copy is used by the
+kernel.
+</Para>
+
+<Para>
+The superblock contain three types of information:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Filesystem parameters which are fixed and which were determined when
+this specific filesystem was created. Some of those parameters can
+be different in different installations of the ext2 filesystem, but
+can not be changed once the filesystem was created.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Filesystem parameters which are tunable - Can always be changed.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Information about the current filesystem state.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Follows the superblock definition:
+</Para>
+
+<Para>
+
+<ProgramListing>
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behaviour when detecting errors */
+ __u16 s_pad;
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ __u32 s_reserved[235]; /* Padding to the end of the block */
+};
+</ProgramListing>
+
+</Para>
+
+<Sect2>
+<Title>superblock identification</Title>
+
+<Para>
+The ext2 filesystem's superblock is identified by the <Literal remap="tt">s&lowbar;magic</Literal> field.
+The current ext2 magic number is 0xEF53. I presume that "EF" means "Extended
+Filesystem". In versions of the ext2 filesystem prior to 0.2B, the magic
+number was 0xEF51. Those filesystems are not compatible with the current
+versions; Specifically, the group descriptors definition is different. I
+doubt if there still exists such a installation.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Filesystem fixed parameters</Title>
+
+<Para>
+By using the word <Literal remap="tt">fixed</Literal>, I mean fixed with respect to a particular
+installation. Those variables are usually not fixed with respect to
+different installations.
+</Para>
+
+<Para>
+The <Literal remap="tt">block size</Literal> is determined by using the <Literal remap="tt">s&lowbar;log&lowbar;block&lowbar;size</Literal>
+variable. The block size is 1024*pow (2,s&lowbar;log&lowbar;block&lowbar;size) and should be
+between 1024 and 4096. The available options are 1024, 2048 and 4096.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;inodes&lowbar;count</Literal> contains the total number of available inodes.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;blocks&lowbar;count</Literal> contains the total number of available blocks.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;first&lowbar;data&lowbar;block</Literal> specifies in which of the <Literal remap="tt">device block</Literal> the
+<Literal remap="tt">superblock</Literal> is present. The superblock is always present at the fixed
+offset 1024, but the device block numbering can differ. For example, if the
+block size is 1024, the superblock will be at <Literal remap="tt">block 1</Literal> with respect to
+the device. However, if the block size is 4096, offset 1024 is included in
+<Literal remap="tt">block 0</Literal> of the device, and in that case <Literal remap="tt">s&lowbar;first&lowbar;data&lowbar;block</Literal>
+will contain 0. At least this is how I understood this variable.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;blocks&lowbar;per&lowbar;group</Literal> contains the number of blocks which are grouped
+together as a blocks group.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;inodes&lowbar;per&lowbar;group</Literal> contains the number of inodes available in a group
+block. I think that this is always the total number of inodes divided by the
+number of blocks groups.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;creator&lowbar;os</Literal> contains a code number which specifies the operating
+system which created this specific filesystem:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Linux</Literal> :-) is specified by the value <Literal remap="tt">0</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Hurd</Literal> is specified by the value <Literal remap="tt">1</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Masix</Literal> is specified by the value <Literal remap="tt">2</Literal>.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;rev&lowbar;level</Literal> contains the major version of the ext2 filesystem.
+Currently this is always <Literal remap="tt">0</Literal>, as the most recent version is 0.5B. It
+will probably take some time until we reach version 1.0.
+</Para>
+
+<Para>
+As far as I know, fragments (sub-block allocations) are currently not
+supported and hence a block is equal to a fragment. As a result,
+<Literal remap="tt">s&lowbar;log&lowbar;frag&lowbar;size</Literal> and <Literal remap="tt">s&lowbar;frags&lowbar;per&lowbar;group</Literal> are always equal to
+<Literal remap="tt">s&lowbar;log&lowbar;block&lowbar;size</Literal> and <Literal remap="tt">s&lowbar;blocks&lowbar;per&lowbar;group</Literal>, respectively.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Ext2fs error handling</Title>
+
+<Para>
+The ext2 filesystem error handling is based on the following philosophy:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Identification of problems is done by the kernel code.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The correction task is left to an external utility, such as
+<Literal remap="tt">e2fsck by Theodore Ts'o</Literal> for <Literal remap="tt">automatic</Literal> analysis and
+correction, or perhaps <Literal remap="tt">debugfs by Theodore Ts'o</Literal> and
+<Literal remap="tt">EXT2ED by myself</Literal>, for <Literal remap="tt">hand</Literal> analysis and correction.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+<Para>
+The <Literal remap="tt">s&lowbar;state</Literal> variable is used by the kernel to pass the identification
+result to third party utilities:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">bit 0</Literal> of s&lowbar;state is reset when the partition is mounted and
+set when the partition is unmounted. Thus, a value of 0 on an
+unmounted filesystem means that the filesystem was not unmounted
+properly - The filesystem is not "clean" and probably contains
+errors.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">bit 1</Literal> of s&lowbar;state is set by the kernel when it detects an
+error in the filesystem. A value of 0 doesn't mean that there isn't
+an error in the filesystem, just that the kernel didn't find any.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The kernel behavior when an error is found is determined by the user tunable
+parameter <Literal remap="tt">s&lowbar;errors</Literal>:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The kernel will ignore the error and continue if <Literal remap="tt">s&lowbar;errors=1</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The kernel will remount the filesystem in read-only mode if
+<Literal remap="tt">s&lowbar;errors=2</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ A kernel panic will be issued if <Literal remap="tt">s&lowbar;errors=3</Literal>.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The default behavior is to ignore the error.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Additional parameters used by e2fsck</Title>
+
+<Para>
+Of-course, <Literal remap="tt">e2fsck</Literal> will check the filesystem if errors were detected
+or if the filesystem is not clean.
+</Para>
+
+<Para>
+In addition, each time the filesystem is mounted, <Literal remap="tt">s&lowbar;mnt&lowbar;count</Literal> is
+incremented. When s&lowbar;mnt&lowbar;count reaches <Literal remap="tt">s&lowbar;max&lowbar;mnt&lowbar;count</Literal>, <Literal remap="tt">e2fsck</Literal>
+will force a check on the filesystem even though it may be clean. It will
+then zero s&lowbar;mnt&lowbar;count. <Literal remap="tt">s&lowbar;max&lowbar;mnt&lowbar;count</Literal> is a tunable parameter.
+</Para>
+
+<Para>
+E2fsck also records the last time in which the file system was checked in
+the <Literal remap="tt">s&lowbar;lastcheck</Literal> variable. The user tunable parameter
+<Literal remap="tt">s&lowbar;checkinterval</Literal> will contain the number of seconds which are allowed
+to pass since <Literal remap="tt">s&lowbar;lastcheck</Literal> until a check is forced. A value of
+<Literal remap="tt">0</Literal> disables time-based check.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Additional user tunable parameters</Title>
+
+<Para>
+<Literal remap="tt">s&lowbar;r&lowbar;blocks&lowbar;count</Literal> contains the number of disk blocks which are
+reserved for root, the user whose id number is <Literal remap="tt">s&lowbar;def&lowbar;resuid</Literal> and the
+group whose id number is <Literal remap="tt">s&lowbar;deg&lowbar;resgid</Literal>. The kernel will refuse to
+allocate those last <Literal remap="tt">s&lowbar;r&lowbar;blocks&lowbar;count</Literal> if the user is not one of the
+above. This is done so that the filesystem will usually not be 100&percnt; full,
+since 100&percnt; full filesystems can affect various aspects of operation.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;def&lowbar;resuid</Literal> and <Literal remap="tt">s&lowbar;def&lowbar;resgid</Literal> contain the id of the user and
+of the group who can use the reserved blocks in addition to root.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>Filesystem current state</Title>
+
+<Para>
+<Literal remap="tt">s&lowbar;free&lowbar;blocks&lowbar;count</Literal> contains the current number of free blocks
+in the filesystem.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;free&lowbar;inodes&lowbar;count</Literal> contains the current number of free inodes in the
+filesystem.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;mtime</Literal> contains the time at which the system was last mounted.
+</Para>
+
+<Para>
+<Literal remap="tt">s&lowbar;wtime</Literal> contains the last time at which something was changed in the
+filesystem.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Copyright</Title>
+
+<Para>
+This document contains source code which was taken from the Linux ext2
+kernel source code, mainly from <FILENAME>/usr/include/linux/ext2&lowbar;fs.h</FILENAME>. Follows
+the original copyright:
+</Para>
+
+<Para>
+
+<ProgramListing>
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+</ProgramListing>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Acknowledgments</Title>
+
+<Para>
+I would like to thank the following people, who were involved in the
+design and implementation of the ext2 filesystem kernel code and support
+utilities:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Remy Card</Literal>
+
+Who designed, implemented and maintains the ext2 filesystem kernel
+code, and some of the ext2 utilities. <Literal remap="tt">Remy Card</Literal> is also the
+author of several helpful slides concerning the ext2 filesystem.
+Specifically, he is the author of <Literal remap="tt">File Management in the Linux
+Kernel</Literal> and of <Literal remap="tt">The Second Extended File System - Current
+State, Future Development</Literal>.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Wayne Davison</Literal>
+
+Who designed the ext2 filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Stephen Tweedie</Literal>
+
+Who helped designing the ext2 filesystem kernel code and wrote the
+slides <Literal remap="tt">Optimizations in File Systems</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Literal remap="tt">Theodore Ts'o</Literal>
+
+Who is the author of several ext2 utilities and of the ext2 library
+<Literal remap="tt">libext2fs</Literal> (which I didn't use, simply because I didn't know
+it exists when I started to work on my project).
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Lastly, I would like to thank, of-course, <Literal remap="tt">Linus Torvalds</Literal> and the
+<Literal remap="tt">Linux community</Literal> for providing all of us with such a great operating
+system.
+</Para>
+
+<Para>
+Please contact me in a case of an error report, suggestions, or just about
+anything concerning this document.
+</Para>
+
+<Para>
+Enjoy,
+</Para>
+
+<Para>
+Gadi Oxman &lt;tgud@tochnapc2.technion.ac.il&gt;
+</Para>
+
+<Para>
+Haifa, August 95
+</Para>
+
+</Sect1>
+
+</Article>
diff --git a/ext2ed/doc/user-guide.sgml b/ext2ed/doc/user-guide.sgml
new file mode 100644
index 0000000..1e8f3cd
--- /dev/null
+++ b/ext2ed/doc/user-guide.sgml
@@ -0,0 +1,2258 @@
+<!DOCTYPE Article PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<Article>
+
+<ArticleInfo>
+
+<Title>EXT2ED - The Extended-2 filesystem editor - User's guide</Title>
+<AUTHOR>
+<FirstName>Gadi Oxman, tgud@tochnapc2.technion.ac.il</FirstName>
+</AUTHOR>
+<PubDate>v0.1, August 3 1995</PubDate>
+
+<Abstract>
+
+<Para>
+This is only the initial version of this document. It may be unclear at
+some places. Please send me feedback with anything regarding to it.
+</Para>
+
+</Abstract>
+
+</ArticleInfo>
+
+<Sect1>
+<Title>About EXT2ED documentation</Title>
+
+<Para>
+The EXT2ED documentation consists of three parts:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The ext2 filesystem overview.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The EXT2ED user's guide.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The EXT2ED design and implementation.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+If you intend to used EXT2ED, I strongly suggest that you would be familiar
+with the material presented in the <Literal remap="tt">ext2 filesystem overview</Literal> as well.
+</Para>
+
+<Para>
+If you also intend to browse and modify the source code, I suggest that you
+will also read the article <Literal remap="tt">The EXT2ED design and implementation</Literal>, as it
+provides a general overview of the structure of my source code.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Introduction</Title>
+
+<Para>
+EXT2ED is a "disk editor" for the ext2 filesystem. Its purpose is to show
+you the internal structures of the ext2 filesystem in an rather intuitive
+and logical way, so that it will be easier to "travel" between the various
+internal filesystem structures.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Basic concepts in EXT2ED</Title>
+
+<Para>
+Two basic concepts in EXT2ED are <Literal remap="tt">commands</Literal> and <Literal remap="tt">types</Literal>.
+</Para>
+
+<Para>
+EXT2ED is object-oriented in the sense that it defines objects in the
+filesystem, like a <Literal remap="tt">super-block</Literal> or a <Literal remap="tt">directory</Literal>. An object is
+something which "knows" how to handle some aspect of the filesystem.
+</Para>
+
+<Para>
+Your interaction with EXT2ED is done through <Literal remap="tt">commands</Literal> which EXT2ED
+accepts. There are three levels of commands:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ General Commands
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Extended-2 Filesystem general commands
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Type specific commands
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+The General commands are always available.
+</Para>
+
+<Para>
+The ext2 general commands are available only when editing an ext2 filesystem.
+</Para>
+
+<Para>
+The Type specific commands are available when editing a specific object in the
+filesystem. Each object typically comes with its own set of internal
+variables, and its own set of commands, which are fine tuned handle the
+corresponding structure in the filesystem.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Running EXT2ED</Title>
+
+<Para>
+Running EXT2ED is as simple as typing <Literal remap="tt">ext2ed</Literal> from the shell prompt.
+There are no command line switches.
+</Para>
+
+<Para>
+When first run, EXT2ED parses its configuration file, <Literal remap="tt">ext2ed.conf</Literal>.
+This file must exist.
+</Para>
+
+<Para>
+When the configuration file processing is done, EXT2ED screen should appear
+on the screen, with the command prompt <Literal remap="tt">ext2ed&#62;</Literal> displayed.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>EXT2ED user interface</Title>
+
+<Para>
+EXT2ED uses the <Emphasis>ncurses</Emphasis> library for screen management. Your screen
+will be divided into four parts, from top to bottom:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Title window
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Status window
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Main editing window
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Command window
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+The title window just displays the current version of EXT2ED.
+</Para>
+
+<Para>
+The status window will display various information regarding the state of
+the editing at this point.
+</Para>
+
+<Para>
+The main editing window is the place at which the actual data will be shown.
+Almost every command will cause some display at this window. This window, as
+opposed to the three others, is of variable length - You always look at one
+page of it. The current page and the total numbers of pages at this moment
+is displayed at the status window. Moving between pages is done by the use
+of the <Command>pgdn</Command> and <Command>pgup</Command> commands.
+</Para>
+
+<Para>
+The command window is at the bottom of the screen. It always displays a
+command prompt <Literal remap="tt">ext2ed&#62;</Literal> and allows you to type a command. Feedback
+about the commands entered is displayed to this window also.
+</Para>
+
+<Para>
+EXT2ED uses the <Emphasis>readline</Emphasis> library while processing a command line. All
+the usual editing keys are available. Each entered command is placed into a
+history of commands, and can be recalled later. Command Completion is also
+supported - Just start to type a command, and press the completion key.
+</Para>
+
+<Para>
+Pressing <Literal remap="tt">enter</Literal> at the command window, without entering a command,
+recalls the last command. This is useful when moving between close entries,
+in the <Command>next</Command> command, for example.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Getting started</Title>
+
+<Sect2>
+<Title>A few precautions</Title>
+
+<Para>
+EXT2ED is a tool for filesystem <Literal remap="tt">editing</Literal>. As such, it can be
+<Literal remap="tt">dangerous</Literal>. The summary to the subsections below is that
+<Literal remap="tt">You must know what you are doing</Literal>.
+</Para>
+
+<Sect3 id="mounted-ref">
+<Title>A mounted filesystem</Title>
+
+<Para>
+EXT2ED is not designed to work on a mounted filesystem - It is complicated
+enough as it is; I didn't even try to think of handling the various race
+conditions. As such, please respect the following advice:
+</Para>
+
+<Para>
+<Literal remap="tt">Do not use EXT2ED on a mounted filesystem !</Literal>
+</Para>
+
+<Para>
+EXT2ED will not allow write access to a mounted filesystem. Although it is
+fairly easy to change EXT2ED so that it will be allowed, I hereby request
+again- EXT2ED is not designed for that action, and will most likely corrupt
+data if used that way. Please don't do that.
+</Para>
+
+<Para>
+Concerning read access, I chose to leave the decision for the user through
+the configuration file option <Literal remap="tt">AllowMountedRead</Literal>. Although read access
+on a mounted partition will not do any damage to the filesystem, the data
+displayed to you will not be reliable, and showing you incorrect information
+may be as bad as corrupting the filesystem. However, you may still wish to
+do that.
+</Para>
+
+</Sect3>
+
+<Sect3>
+<Title>Write access</Title>
+
+<Para>
+Considering the obvious sensitivity of the subject, I took the following
+actions:
+</Para>
+
+<Para>
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ EXT2ED will always start with a read-only access. Write access mode
+needs to be specifically entered by the <Command>enablewrite</Command> command.
+Until this is done, no write will be allowed. Write access can be
+disabled at any time with <Command>disablewrite</Command>. When
+<Command>enablewrite</Command> is issued, the device is reopened in read-write
+mode. Needless to say, the device permissions should allow that.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ As a second level of protection, you can disallow write access in
+the configuration file by using the <Literal remap="tt">AllowChanges off</Literal>
+configuration option. In this case, the <Command>enablewrite</Command> command
+will be refused.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ When write access is enabled, the data will never change
+immediately. Rather, a specific <Command>writedata</Command> command is needed
+to update the object in the disk with the changed object in memory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In addition, A logging option is provided through the configuration
+file options <Literal remap="tt">LogChanges</Literal> and <Literal remap="tt">LogFile</Literal>. With logging
+enabled, each change to the disk will be logged at a very primitive
+level - A hex dump of the original data and of the new written data.
+The log file will be a text file which is easily readable, and you
+can make use of it to undo any changes which you made (EXT2ED doesn't
+make use of the log file for that purpose, it just logs the changes).
+</Para>
+</ListItem>
+
+</OrderedList>
+
+Please remember that this is only the initial release of EXT2ED, and it is
+not very much tested - It is reasonable to assume that <Literal remap="tt">there are
+bugs</Literal>.
+However, the logging option above can offer protection even from this
+unfortunate case. Therefor, I highly recommend that at least when first
+working with EXT2ED, the logging option will be enabled, despite the disk
+space which it consumes.
+</Para>
+
+</Sect3>
+
+</Sect2>
+
+<Sect2 id="help-ref">
+<Title>The help command</Title>
+
+<Para>
+When loaded, EXT2ED will show a short help screen. This help screen can
+always be retrieved by the command <Command>help</Command>. The help screen displays a
+list of all the commands which are available at this point. At startup, only
+the <Literal remap="tt">General commands</Literal> are available.
+This will change with time, since each object has its own commands. Thus,
+commands which are available now may not be available later.
+Using <Command>help</Command> <Emphasis>command</Emphasis> will display additional information about
+the specific command <Emphasis>command</Emphasis>.
+</Para>
+
+</Sect2>
+
+<Sect2 id="setdevice-ref">
+<Title>The setdevice command</Title>
+
+<Para>
+The first command that is usually entered to EXT2ED is the <Command>setdevice</Command>
+command. This command simply tells EXT2ED on which device the filesystem is
+present. For example, suppose my ext2 filesystem is on the first partition
+of my ide disk. The command will be:
+
+<Screen>
+setdevice /dev/hda1
+</Screen>
+
+The following actions will take place in the following order:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ EXT2ED will check if the partition is mounted.
+If the partition is mounted (<Literal remap="tt">highly not recommended</Literal>),
+the accept/reject behavior will be decided by the configuration
+file. Cross reference section <XRef LinkEnd="mounted-ref">.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The specified device will be opened in read-only mode. The
+permissions of the device should be set in a way that allows
+you to open the device for read access.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Autodetection of an ext2 filesystem will be made by searching for
+the ext2 magic number in the main superblock.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In the case of a successful recognition of an ext2 filesystem, the
+ext2 filesystem specific commands and the ext2 specific object
+definitions will be registered. The object definitions will be read
+at run time from a file specified by the configuration file.
+
+In case of a corrupted ext2 filesystem, it is quite possible that
+the main superblock is damaged and autodetection will fail. In that
+case, use the configuration option <Literal remap="tt">ForceExt2 on</Literal>. This is not
+the default case since EXT2ED can be used at a lower level to edit a
+non-ext2 filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In a case of a successful autodetection, essential information about
+the filesystem such as the block size will be read from the
+superblock, unless the used overrides this behavior with an
+configuration option (not recommended). In that case, the parameters
+will be read from the configuration file.
+
+In a case of an autodetection failure, the essential parameters
+will be read from the configuration file.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+Assuming that you are editing an ext2 filesystem and that everything goes
+well, you will notice that additional commands are now available in the help
+screen, under the section <Literal remap="tt">ext2 filesystem general commands</Literal>. In
+addition, EXT2ED now recognizes a few objects which are essential to the
+editing of an ext2 filesystem.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Two levels of usage</Title>
+
+<Sect2>
+<Title>Low level usage</Title>
+
+<Para>
+This section explains what EXT2ED provides even when not editing an ext2
+filesystem.
+</Para>
+
+<Para>
+Even at this level, EXT2ED is more than just a hex editor. It still allows
+definition of objects and variables in run time through a user file,
+although of-course the objects will not have special fine tuned functions
+connected to them. EXT2ED will allow you to move in the filesystem using
+<Command>setoffset</Command>, and to apply an object definition on a specific place
+using <Command>settype</Command> <Emphasis>type</Emphasis>. From this point and on, the object will
+be shown <Literal remap="tt">in its native form</Literal> - You will see a list of the
+variables rather than just a hex dump, and you will be able to change each
+variable in the intuitive form <Command>set variable=value</Command>.
+</Para>
+
+<Para>
+To define objects, use the configuration option <Literal remap="tt">AlternateDescriptors</Literal>.
+</Para>
+
+<Para>
+There are now two forms of editing:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Editing without a type. In this case, the disk block will be shown
+as a text+hex dump, and you will be able to move along and change it.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Editing with a type. In this case, the object's variables will be
+shown, and you will be able to change each variable in its native form.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>High level usage</Title>
+
+<Para>
+EXT2ED was designed for the editing of the ext2 filesystem. As such, it
+"understands" the filesystem structure to some extent. Each object now has
+special fine tuned 'C' functions connected to it, which knows how to display
+it in an intuitive form, and how the object fits in the general design of
+the ext2 filesystem. It is of-course much easier to use this type of
+editing. For example:
+
+<Screen>
+Issue <Emphasis>group 2</Emphasis> to look at the main copy of the third group block
+descriptor. With <Emphasis>gocopy 1</Emphasis> you can move to its first backup copy,
+and with <Emphasis>inode</Emphasis> you can start editing the inode table of the above
+group block. From here, if the inode corresponds to a file, you can
+use <Emphasis>file</Emphasis> to edit the file in a "continuous" way, using
+<Emphasis>nextblock</Emphasis> to pass to its next block, letting EXT2ED following by
+itself the direct blocks, indirect blocks, ..., while still preserving the
+actual view of the exact block usage of the file.
+</Screen>
+
+The point is that the "tour" of the filesystem will now be synchronous rather
+than asynchronous - Each object has the "links" to pass between connected
+logical structures, and special fine-tuned functions to deal with it.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>General commands</Title>
+
+<Para>
+I will now start with a systematic explanation of the general commands.
+Please feel free to experiment, but take care when using the
+<Literal remap="tt">enablewrite</Literal> command.
+</Para>
+
+<Para>
+Whenever a command syntax is specified, arguments which are optional are
+enclosed with square brackets.
+</Para>
+
+<Para>
+Please note that in EXT2ED, each command can be overridden by a specific
+object to provide special fine-tuned functionality. In general, I was
+attempting to preserve the similarity between those functions, which are
+accessible by the same name.
+</Para>
+
+<Sect2 id="disablewrite-ref">
+<Title>disablewrite</Title>
+
+<Para>
+
+<Screen>
+Syntax: disablewrite
+</Screen>
+
+<Command>disablewrite</Command> is used to reopen the device with read-only access. When
+first running EXT2ED, the device is opened in read-only mode, and an
+explicit <Command>enablewrite</Command> is required for write access. When finishing
+with changing, a <Command>disablewrite</Command> is recommended for safety. Cross
+reference section <XRef LinkEnd="disablewrite-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2 id="enablewrite-ref">
+<Title>enablewrite</Title>
+
+<Para>
+
+<Screen>
+Syntax: enablewrite
+</Screen>
+
+<Command>enablewrite</Command> is used to reopen the device with read-write access.
+When first running EXT2ED, the device is opened in read-only mode, and an
+explicit <Command>enablewrite</Command> is required for write access.
+<Command>enablewrite</Command> will fail if write access is disabled from the
+configuration file by the <Literal remap="tt">AllowChanges off</Literal> configuration option.
+Even after <Command>enablewrite</Command>, an explicit <Command>writedata</Command>
+is required to actually write the new data to the disk.
+When finishing with changing, a <Command>disablewrite</Command> is recommended for safety.
+Cross reference section <XRef LinkEnd="enablewrite-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>help</Title>
+
+<Para>
+
+<Screen>
+Syntax: help [command]
+</Screen>
+
+The <Command>help</Command> command is described at section <XRef LinkEnd="help-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2 id="next-ref">
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [number]
+</Screen>
+
+This section describes the <Emphasis>general command</Emphasis> <Command>next</Command>. <Command>next</Command>
+is overridden by several types in EXT2ED, to provide fine-tuned
+functionality.
+</Para>
+
+<Para>
+The <Literal remap="tt">next general command</Literal> behavior is depended on whether you are editing a
+specific object, or none.
+</Para>
+
+<Para>
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ In the case where Type is <Literal remap="tt">none</Literal> (The current type is showed
+on the status window by the <Command>show</Command> command), <Literal remap="tt">next</Literal>
+passes to the next <Emphasis>number</Emphasis> bytes in the current edited block.
+If <Emphasis>number</Emphasis> is not specified, <Emphasis>number=1</Emphasis> is assumed.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In the case where Type is defined, the <Command>next</Command> commands assumes
+that you are editing an array of objects of that type, and the
+<Command>next</Command> command will just pass to the next entry in the array.
+If <Emphasis>number</Emphasis> is defined, it will pass <Emphasis>number</Emphasis> entries
+ahead.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2 id="pgdn-ref">
+<Title>pgdn</Title>
+
+<Para>
+
+<Screen>
+Syntax: pgdn
+</Screen>
+
+Usually the edited data doesn't fit into the visible main window. In this
+case, the status window will indicate that there is more to see "below" by
+the message <Literal remap="tt">Page x of y</Literal>. This means that there are <Emphasis>y</Emphasis> pages
+total, and you are currently viewing the <Emphasis>x</Emphasis> page. With the <Command>pgdn</Command>
+command, you can pass to the next available page.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>pgup</Title>
+
+<Para>
+
+<Screen>
+Syntax: pgup
+</Screen>
+
+</Para>
+
+<Para>
+<Command>pgup</Command> is the opposite of <Command>pgdn</Command> - It will pass to the previous
+page. Cross reference section <XRef LinkEnd="pgdn-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [number]
+</Screen>
+
+</Para>
+
+<Para>
+<Command>prev</Command> is the opposite of <Command>next</Command>. Cross reference section
+<XRef LinkEnd="next-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2 id="recall-ref">
+<Title>recall</Title>
+
+<Para>
+
+<Screen>
+Syntax: recall object
+</Screen>
+
+<Command>recall</Command> is the opposite of <Command>remember</Command>. It will place you at the
+place you where when saving the object position and type information. Cross
+reference section <XRef LinkEnd="remember-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>redraw</Title>
+
+<Para>
+
+<Screen>
+Syntax: redraw
+</Screen>
+
+Sometimes the screen display gets corrupted. I still have problems with
+this. The <Command>redraw</Command> command simply redraws the entire display screen.
+</Para>
+
+</Sect2>
+
+<Sect2 id="remember-ref">
+<Title>remember</Title>
+
+<Para>
+
+<Screen>
+Syntax: remember object
+</Screen>
+
+EXT2ED provides you <Literal remap="tt">memory</Literal> of objects; While editing, you may reach an
+object which you will like to return to later. The <Command>remember</Command> command
+will store in memory the current place and type of the object. You can
+return to the object by using the <Command>recall</Command> command. Cross reference
+section <XRef LinkEnd="recall-ref">.
+</Para>
+
+<Para>
+<Literal remap="tt">Note:</Literal>
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ When remembering a <Literal remap="tt">file</Literal> or a <Literal remap="tt">directory</Literal>, the
+corresponding inode will be saved in memory. The basic reason is that
+the inode is essential for finding the blocks of the file or the
+directory.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>set</Title>
+
+<Para>
+
+<Screen>
+Syntax: set [text || hex] arg1 [arg2 arg3 ...]
+
+or
+
+Syntax: set variable=value
+</Screen>
+
+The <Command>set</Command> command is used to modify the current data.
+The <Command>set general command</Command> behavior is depended on whether you are editing a
+specific object, or none.
+</Para>
+
+<Para>
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ In the case where Type is <Command>none</Command>, the first syntax should be
+used. The set command affects the data starting at the current
+highlighted position in the edited block.
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ When using the <Command>set hex</Command> command, a list of
+hexadecimal bytes should follow.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ When using the <Command>set text</Command> command, it should be followed
+by a text string.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+Examples:
+
+<Screen>
+ set hex 09 0a 0b 0c 0d 0e 0f
+ set text Linux is just great !
+
+</Screen>
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ In the case where Type is defined, the second syntax should be used.
+The set commands just sets the variable <Emphasis>variable</Emphasis> with the
+value <Emphasis>value</Emphasis>.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+In any case, the data is only changed in memory. For an actual update to the
+disk, use the <Command>writedata</Command> command.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>setdevice</Title>
+
+<Para>
+
+<Screen>
+Syntax: setdevice device
+</Screen>
+
+The <Command>setdevice</Command> command is described at section <XRef LinkEnd="setdevice-ref">.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>setoffset</Title>
+
+<Para>
+
+<Screen>
+Syntax: setoffset [block || type] [+|-]offset
+</Screen>
+
+The <Command>setoffset</Command> command is used to move asynchronously inside the file
+system. It is considered a low level command, and usually should not be used
+when editing an ext2 filesystem, simply because movement is better
+utilized through the specific ext2 commands.
+</Para>
+
+<Para>
+The <Command>offset</Command> is in bytes, and meanwhile should be positive and smaller
+than 2GB.
+</Para>
+
+<Para>
+Use of the <Command>block</Command> modifier changes the counting unit to block.
+</Para>
+
+<Para>
+Use of the <Literal remap="tt">+ or -</Literal> modifiers signals that the offset is relative to
+the current position.
+</Para>
+
+<Para>
+use of the <Literal remap="tt">type</Literal> modifier is allowed only with relative offset. This
+modifier will multiply the offset by the size of the current type.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>settype</Title>
+
+<Para>
+
+<Screen>
+Syntax: settype type || [none | hex]
+</Screen>
+
+The <Command>settype</Command> command is used to move apply the object definitions of
+the type <Emphasis>type</Emphasis> on the current position. It is considered a low level
+command and usually should not be used when editing an ext2 filesystem since
+EXT2ED provides better tools. It is of-course very useful when editing a
+non-ext2 filesystem and using user-defined objects.
+</Para>
+
+<Para>
+When <Emphasis>type</Emphasis> is <Emphasis>hex</Emphasis> or <Emphasis>none</Emphasis>, the data will be displayed as
+a hex and text dump.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>show</Title>
+
+<Para>
+
+<Screen>
+Syntax: show
+</Screen>
+
+The <Command>show</Command> command will show the data of the current object at the
+current position on the main display window. It will also update the status
+window with type specific information. It may be necessary to use
+<Command>pgdn</Command> and <Command>pgup</Command> to view the entire data.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>writedata</Title>
+
+<Para>
+
+<Screen>
+Syntax: writedata
+</Screen>
+
+The <Command>writedata</Command> command will update the disk with the object data that
+is currently in memory. This is the point at which actual change is made to
+the filesystem. Without this command, the edited data will not have any
+effect. Write access should be allowed for a successful update.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>Editing an ext2 filesystem</Title>
+
+<Para>
+In order to edit an ext2 filesystem, you should, of course, know the structure
+of the ext2 filesystem. If you feel that you lack some knowledge in this
+area, I suggest that you do some of the following:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Read the supplied ext2 technical information. I tried to summarize
+the basic information which is needed to get you started.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Get the slides that Remy Card (The author of the ext2 filesystem)
+prepared concerning the ext2 filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Read the kernel sources.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+At this point, you should be familiar with the following terms:
+<Literal remap="tt">block, inode, superblock, block groups, block allocation bitmap, inode
+allocation bitmap, group descriptors, file, directory.</Literal>Most of the above
+are objects in EXT2ED.
+</Para>
+
+<Para>
+When editing an ext2 filesystem it is recommended that you use the ext2
+specific commands, rather then the general commands <Command>setoffset</Command> and
+<Command>settype</Command>, mainly because:
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ In most cases it will be unreliable, and will display incorrect
+information.
+
+Sometimes in order to edit an object, EXT2ED needs the information
+of some other related objects. For example, when editing a
+directory, EXT2ED needs access to the inode of the edited directory.
+Simply setting the type to a directory <Literal remap="tt">will be unreliable</Literal>,
+since the object assumes that you passed through its inode to reach
+it, and expects this information, which isn't initialized if you
+directly set the type to a directory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ EXT2ED offers far better tools for handling the ext2 filesystem
+using the ext2 specific commands.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>ext2 general commands</Title>
+
+<Para>
+The <Literal remap="tt">ext2 general commands</Literal> are available only when you are editing an
+ext2 filesystem. They are <Literal remap="tt">general</Literal> in the sense that they are not
+specific to some object, and can be invoked anytime.
+</Para>
+
+<Sect2 id="general-superblock">
+<Title>super</Title>
+
+<Para>
+
+<Screen>
+Syntax: super
+</Screen>
+
+The <Command>super</Command> command will "bring you" to the main superblock copy. It
+will automatically set the object type to <Literal remap="tt">ext2&lowbar;super&lowbar;block</Literal>. Then you
+will be able to view and edit the superblock. When you are in the
+superblock, other commands will be available.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>group</Title>
+
+<Para>
+
+<Screen>
+Syntax: group [number]
+</Screen>
+
+The <Command>group</Command> command will "bring you" to the main copy of the
+<Emphasis>number</Emphasis> group descriptor. It will automatically set the object type to
+<Literal remap="tt">ext2&lowbar;group&lowbar;desc</Literal>. Then you will be able to view and edit the group
+descriptor entry. When you are there, other commands will be available.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>cd</Title>
+
+<Para>
+
+<Screen>
+Syntax: cd path
+</Screen>
+
+The <Command>cd</Command> command will let you travel in the filesystem in the nice way
+that the mounted filesystem would have let you.
+</Para>
+
+<Para>
+The <Command>cd</Command> command is a complicated command. Although it may sound
+simple at first, an implementation of a typical cd requires passing through
+the group descriptors, inodes, directory entries, etc. For example:
+</Para>
+
+<Para>
+The innocent cd /usr command can be done by using more primitive
+EXT2ED commands in the following way (It is implemented exactly this way):
+
+<OrderedList>
+<ListItem>
+
+<Para>
+ Using <Command>group 0</Command> to go to the first group descriptor.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>inode</Command> to get to the Bad blocks inode.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>next</Command> to pass to the root directory inode.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>dir</Command> to see the directory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>next</Command> until we find the directory usr.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>followinode</Command> to pass to the inode corresponding to usr.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>dir</Command> to see the directory of /usr.
+</Para>
+</ListItem>
+
+</OrderedList>
+
+And those commands aren't that primitive; For example, the tracing of the
+blocks which belong to the root directory is done automatically by the dir
+command behind the scenes, and the followinode command will automatically
+"run" to the correct group descriptor in order to find the required inode.
+</Para>
+
+<Para>
+The path to the <Command>general cd</Command> command needs to be a full pathname -
+Starting from <Filename>/</Filename>. The <Command>cd</Command> command stops at the last reachable
+point, which can be a directory entry, in which case the type will be set to
+<Literal remap="tt">dir</Literal>, or an inode, in which case the type will be set to
+<Literal remap="tt">ext2&lowbar;inode</Literal>. Symbolic links (Only fast symbolic links, meanwhile) are
+automatically followed (if they are not across filesystems, of-course). If
+the type is set to <Literal remap="tt">dir</Literal>, you can use a path relative to the
+"current directory".
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The superblock</Title>
+
+<Para>
+The superblock can always be reached by the ext2 general command
+<Command>super</Command>. Cross reference section <XRef LinkEnd="general-superblock">.
+</Para>
+
+<Para>
+The status window will show you which copy of the superblock copies you are
+currently editing.
+</Para>
+
+<Para>
+The main data window will show you the values of the various superblock
+variables, along with some interpretation of the values.
+</Para>
+
+<Para>
+Data can be changed with the <Command>set</Command> and <Command>writedata</Command> commands.
+
+<Screen>
+For example, set s_r_blocks_count=1400 will reserve 1400 blocks for root.
+</Screen>
+
+</Para>
+
+<Sect2>
+<Title>gocopy</Title>
+
+<Para>
+
+<Screen>
+Syntax: gocopy number
+</Screen>
+
+The <Command>gocopy</Command> command will "bring you" to the backup copy <Emphasis>number</Emphasis>
+of the superblock copies. <Command>gocopy 0</Command>, for example, will bring you to
+the main copy.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>setactivecopy</Title>
+
+<Para>
+
+<Screen>
+Syntax: setactivecopy
+</Screen>
+
+The <Command>setactivecopy</Command> command will copy the contents of the current
+superblock copy onto the contents of the main copy. It will also switch to
+editing of the main copy. No actual data is written to disk, of-course,
+until you issue the <Command>writedata</Command> command.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The group descriptors</Title>
+
+<Para>
+The group descriptors can be edited by the <Command>group</Command> command.
+</Para>
+
+<Para>
+The status window will indicate the current group descriptor, the total
+number of group descriptors (and hence of group blocks), and the backup copy
+number.
+</Para>
+
+<Para>
+The main data window will just show you the values of the various variables.
+</Para>
+
+<Para>
+Basically, you can use the <Command>next</Command> and <Command>prev</Command> commands, along with the
+<Command>set</Command> command, to modify the group descriptors.
+</Para>
+
+<Para>
+The group descriptors object is a junction, from which you can reach:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The inode table of the corresponding block group (the <Literal remap="tt">inode</Literal>
+command)
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The block allocation bitmap (the <Literal remap="tt">blockbitmap</Literal> command)
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The inode allocation bitmap (the <Literal remap="tt">inodebitmap</Literal> command)
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Sect2>
+<Title>blockbitmap</Title>
+
+<Para>
+
+<Screen>
+Syntax: blockbitmap
+</Screen>
+
+The <Command>blockbitmap</Command> command will let you edit the block bitmap allocation
+block of the current group block.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>entry</Title>
+
+<Para>
+
+<Screen>
+Syntax: entry number
+</Screen>
+
+The <Command>entry</Command> command will move you to the <Emphasis>number</Emphasis> group descriptor in the
+group descriptors table.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>inode</Title>
+
+<Para>
+
+<Screen>
+Syntax: inode
+</Screen>
+
+The <Command>inode</Command> command will pass you to the first inode in the current
+group block.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>inodebitmap</Title>
+
+<Para>
+
+<Screen>
+Syntax: inodebitmap
+</Screen>
+
+The <Command>inodebitmap</Command> command will let you edit the inode bitmap allocation
+block of the current group block.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [number]
+</Screen>
+
+The <Command>next</Command> command will pass to the next <Emphasis>number</Emphasis> group
+descriptor. If <Emphasis>number</Emphasis> is omitted, <Emphasis>number=1</Emphasis> is assumed.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [number]
+</Screen>
+
+The <Command>prev</Command> command will pass to the previous <Emphasis>number</Emphasis> group
+descriptor. If <Emphasis>number</Emphasis> is omitted, <Emphasis>number=1</Emphasis> is assumed.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>setactivecopy</Title>
+
+<Para>
+
+<Screen>
+Syntax: setactivecopy
+</Screen>
+
+The <Command>setactivecopy</Command> command copies the contents of the current group
+descriptor, to its main copy. The updated main copy will then be shown. No
+actual change is made to the disk until you issue the <Command>writedata</Command>
+command.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The inode</Title>
+
+<Para>
+An inode can be reached by the following two ways:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ Using <Command>inode</Command> from the corresponding group descriptor.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using <Command>followinode</Command> from a directory entry.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Using the <Command>cd</Command> command with the pathname to the file.
+
+For example, <Command>cd /usr/src/ext2ed/ext2ed.h</Command>
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The status window will indicate:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The current global inode number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The total total number of inodes.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ On which block group the inode is allocated.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The total number of inodes in this group block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The index of the current inode in the current group block.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The type of the inode (file, directory, special, etc).
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The main data window, in addition to the list of variables, will contain
+some interpretations on the right side.
+</Para>
+
+<Para>
+If the inode corresponds to a file, you can use the <Command>file</Command> command to
+edit the file.
+</Para>
+
+<Para>
+If the inode is an inode of a directory, you can use the <Command>dir</Command> command
+to edit the directory.
+</Para>
+
+<Sect2>
+<Title>dir</Title>
+
+<Para>
+
+<Screen>
+Syntax: dir
+</Screen>
+
+If the inode mode corresponds to a directory (shown on the status window),
+you can enter directory mode editing by using <Literal remap="tt">dir</Literal>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>entry</Title>
+
+<Para>
+
+<Screen>
+Syntax: entry number
+</Screen>
+
+The <Command>entry</Command> command will move you to the <Emphasis>number</Emphasis> inode in the
+current inode table.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>file</Title>
+
+<Para>
+
+<Screen>
+Syntax: file
+</Screen>
+
+If the inode mode corresponds to a file (shown on the status window),
+you can enter file mode editing by using <Command>file</Command>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>group</Title>
+
+<Para>
+
+<Screen>
+Syntax: group
+</Screen>
+
+The <Command>group</Command> command is used to go to the group descriptor of the
+current group block.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [number]
+</Screen>
+
+The <Command>next</Command> command will pass to the next <Emphasis>number</Emphasis> inode.
+If <Emphasis>number</Emphasis> is omitted, <Emphasis>number=1</Emphasis> is assumed.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [number]
+</Screen>
+
+The <Command>prev</Command> command will pass to the previous <Emphasis>number</Emphasis> inode.
+If <Emphasis>number</Emphasis> is omitted, <Emphasis>number=1</Emphasis> is assumed.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The file</Title>
+
+<Para>
+When editing a file, EXT2ED offers you a both a continuous and a true
+fragmented view of the file - The file is still shown block by block with
+the true block number at each stage and EXT2ED offers you commands which
+allow you to move between the <Literal remap="tt">file blocks</Literal>, while finding the
+allocated blocks by using the inode information behind the scenes.
+</Para>
+
+<Para>
+Aside from this, the editing is just a <Literal remap="tt">hex editing</Literal> - You move the
+cursor in the current block of the file by using <Command>next</Command> and
+<Command>prev</Command>, move between blocks by <Command>nextblock</Command> and <Command>prevblock</Command>,
+and make changes by the <Command>set</Command> command. Note that the set command is
+overridden here - There are no variables. The <Command>writedata</Command> command will
+update the current block to the disk.
+</Para>
+
+<Para>
+Reaching a file can be done by using the <Command>file</Command> command from its inode.
+The inode can be reached by any other means, for example, by the
+<Command>cd</Command> command, if you know the file name.
+</Para>
+
+<Para>
+The status window will indicate:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The global block number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The internal file block number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The file offset.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The file size.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The file inode number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The indirection level - Whether it is a direct block (0), indirect
+(1), etc.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+The main data window will display the file either in hex mode or in text
+mode, select-able by the <Command>display</Command> command.
+</Para>
+
+<Para>
+In hex mode, EXT2ED will display offsets in the current block, along with a
+text and hex dump of the current block.
+</Para>
+
+<Para>
+In either case the <Literal remap="tt">current place</Literal> will be highlighted. In the hex mode
+it will be always highlighted, while in the text mode it will be highlighted
+if the character is display-able.
+</Para>
+
+<Sect2>
+<Title>block</Title>
+
+<Para>
+
+<Screen>
+Syntax: block block_num
+</Screen>
+
+The <Command>block</Command> command is used to move inside the file. The
+<Emphasis>block&lowbar;num</Emphasis> argument is the requested internal file block number. A
+value of 0 will reach the beginning of the file.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>display</Title>
+
+<Para>
+
+<Screen>
+Syntax: display [text || hex]
+</Screen>
+
+The <Command>display</Command> command changes the display mode of the file.
+<Command>display
+hex</Command> will switch to <Command>hex mode</Command>, while <Command>display text</Command> will switch
+to text mode. The default mode when no <Command>display</Command> command is issued is
+<Command>hex mode</Command>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>inode</Title>
+
+<Para>
+
+<Screen>
+Syntax: inode
+</Screen>
+
+The <Command>inode</Command> command will return to the inode of the current file.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [num]
+</Screen>
+
+The <Command>next</Command> command will pass to the next byte in the file. If
+<Emphasis>num</Emphasis> is supplied, it will pass to the next <Emphasis>num</Emphasis> bytes.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>nextblock</Title>
+
+<Para>
+
+<Screen>
+Syntax: nextblock [num]
+</Screen>
+
+The <Command>nextblock</Command> command will pass to the next block in the file. If
+<Emphasis>num</Emphasis> is supplied, it will pass to the next <Emphasis>num</Emphasis> blocks.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [num]
+</Screen>
+
+The <Command>prev</Command> command will pass to the previous byte in the file. If
+<Emphasis>num</Emphasis> is supplied, it will pass to the previous <Emphasis>num</Emphasis> bytes.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prevblock</Title>
+
+<Para>
+
+<Screen>
+Syntax: prevblock [num]
+</Screen>
+
+The <Command>nextblock</Command> command will pass to the previous block in the file. If
+<Emphasis>num</Emphasis> is supplied, it will pass to the previous <Emphasis>num</Emphasis> blocks.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>offset</Title>
+
+<Para>
+
+<Screen>
+Syntax: offset file_offset
+</Screen>
+
+The <Command>offset</Command> command will move to the specified offset in the file.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>set</Title>
+
+<Para>
+
+<Screen>
+Syntax: set [text || hex] arg1 [arg2 arg3 ...]
+</Screen>
+
+The <Command>file set</Command> command is working like the <Literal remap="tt">general set command</Literal>,
+with <Literal remap="tt">type=none</Literal>. There are no variables.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>writedata</Title>
+
+<Para>
+
+<Screen>
+Syntax: writedata
+</Screen>
+
+The <Command>writedata</Command> command will update the current file block in the disk.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The directory</Title>
+
+<Para>
+When editing a file, EXT2ED analyzes for you both the allocation blocks of
+the directory entries, and the directory entries.
+</Para>
+
+<Para>
+Each directory entry is displayed on one row. You can move the highlighted
+entry with the usual <Command>next</Command> and <Command>prev</Command> commands, and "dive in"
+with the <Command>followinode</Command> command.
+</Para>
+
+<Para>
+The status window will indicate:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ The directory entry number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The total number of directory entries in this directory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The current global block number.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The current offset in the entire directory - When viewing the
+directory as a continuous file.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The inode number of the directory itself.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ The indirection level - Whether it is a direct block (0), indirect
+(1), etc.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Sect2>
+<Title>cd</Title>
+
+<Para>
+
+<Screen>
+Syntax: cd [path]
+</Screen>
+
+The <Command>cd</Command> command is used in the usual meaning, like the global cd
+command.
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+ If <Emphasis>path</Emphasis> is not specified, the current directory entry is
+followed.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Emphasis>path</Emphasis> can be relative to the current directory.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ <Emphasis>path</Emphasis> can also end up in a file, in which case the file inode
+will be reached.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+ Symbolic link (fast only, meanwhile) is automatically followed.
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>entry</Title>
+
+<Para>
+
+<Screen>
+Syntax: entry [entry_num]
+</Screen>
+
+The <Command>entry</Command> command sets <Emphasis>entry&lowbar;num</Emphasis> as the current directory
+entry.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>followinode</Title>
+
+<Para>
+
+<Screen>
+Syntax: followinode
+</Screen>
+
+The <Command>followinode</Command> command will move you to the inode pointed by the
+current directory entry.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>inode</Title>
+
+<Para>
+
+<Screen>
+Syntax: inode
+</Screen>
+
+The <Command>inode</Command> command will return you to the parent inode of the whole
+directory listing.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [num]
+</Screen>
+
+The <Command>next</Command> command will pass to the next directory entry.
+If <Emphasis>num</Emphasis> is supplied, it will pass to the next <Emphasis>num</Emphasis> entries.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [num]
+</Screen>
+
+The <Command>prev</Command> command will pass to the previous directory entry.
+If <Emphasis>num</Emphasis> is supplied, it will pass to the previous <Emphasis>num</Emphasis> entries.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>writedata</Title>
+
+<Para>
+
+<Screen>
+Syntax: writedata
+</Screen>
+
+The <Command>writedata</Command> command will write the current directory entry to the
+disk.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1 id="block-bitmap">
+<Title>The block allocation bitmap</Title>
+
+<Para>
+The <Literal remap="tt">block allocation bitmap</Literal> of any block group can be reached from
+the corresponding group descriptor.
+</Para>
+
+<Para>
+You will be offered a bit listing of the entire blocks in the group. The
+current block will be highlighted and its number will be displayed in the
+status window.
+</Para>
+
+<Para>
+A value of "1" means that the block is allocated, while a value of "0"
+signals that it is free. The value is also interpreted in the status
+window. You can use the usual <Command>next/prev</Command> commands, along with the
+<Command>allocate/deallocate</Command> commands.
+</Para>
+
+<Sect2>
+<Title>allocate</Title>
+
+<Para>
+
+<Screen>
+Syntax: allocate [num]
+</Screen>
+
+The <Command>allocate</Command> command allocates <Emphasis>num</Emphasis> blocks, starting from the
+highlighted position. If <Emphasis>num</Emphasis> is not specified, <Emphasis>num=1</Emphasis> is assumed.
+Of-course, no actual change is made until you issue a <Command>writedata</Command> command.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>deallocate</Title>
+
+<Para>
+
+<Screen>
+Syntax: deallocate [num]
+</Screen>
+
+The <Command>deallocate</Command> command deallocates <Emphasis>num</Emphasis> blocks, starting from the
+highlighted position. If <Emphasis>num</Emphasis> is not specified, <Emphasis>num=1</Emphasis> is assumed.
+Of-course, no actual change is made until you issue a <Command>writedata</Command> command.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>entry</Title>
+
+<Para>
+
+<Screen>
+Syntax: entry [entry_num]
+</Screen>
+
+The <Command>entry</Command> command sets the current highlighted block to
+<Emphasis>entry&lowbar;num</Emphasis>.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>next</Title>
+
+<Para>
+
+<Screen>
+Syntax: next [num]
+</Screen>
+
+The <Command>next</Command> command will pass to the next bit, which corresponds to the
+next block. If <Emphasis>num</Emphasis> is supplied, it will pass to the next <Emphasis>num</Emphasis>
+bits.
+</Para>
+
+</Sect2>
+
+<Sect2>
+<Title>prev</Title>
+
+<Para>
+
+<Screen>
+Syntax: prev [num]
+</Screen>
+
+The <Command>prev</Command> command will pass to the previous bit, which corresponds to the
+previous block. If <Emphasis>num</Emphasis> is supplied, it will pass to the previous
+<Emphasis>num</Emphasis> bits.
+</Para>
+
+</Sect2>
+
+</Sect1>
+
+<Sect1>
+<Title>The inode allocation bitmap</Title>
+
+<Para>
+The <Literal remap="tt">inode allocation bitmap</Literal> is very similar to the block allocation
+bitmap explained above. It is also reached from the corresponding group
+descriptor. Please refer to section <XRef LinkEnd="block-bitmap">.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Filesystem size limitation</Title>
+
+<Para>
+While an ext2 filesystem has a size limit of <Literal remap="tt">4 TB</Literal>, EXT2ED currently
+<Literal remap="tt">can't</Literal> handle filesystems which are <Literal remap="tt">bigger than 2 GB</Literal>.
+</Para>
+
+<Para>
+I am sorry for the inconvenience. This will hopefully be fixed in future
+releases.
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Copyright</Title>
+
+<Para>
+EXT2ED is Copyright (C) 1995 Gadi Oxman.
+</Para>
+
+<Para>
+EXT2ED is hereby placed under the GPL - Gnu Public License. You are free and
+welcome to copy, view and modify the sources. My only wish is that my
+copyright presented above will be left and that a list of the bug fixes,
+added features, etc, will be provided.
+</Para>
+
+<Para>
+The entire EXT2ED project is based, of-course, on the kernel sources. The
+<Literal remap="tt">ext2.descriptors</Literal> distributed with EXT2ED is a slightly modified
+version of the main ext2 include file, /usr/include/linux/ext2&lowbar;fs.h. Follows
+the original copyright:
+</Para>
+
+<Para>
+
+<Screen>
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+</Screen>
+
+</Para>
+
+</Sect1>
+
+<Sect1>
+<Title>Acknowledgments</Title>
+
+<Para>
+EXT2ED was constructed as a student project in the software
+laboratory of the faculty of electrical-engineering in the
+<Literal remap="tt">Technion - Israel's institute of technology</Literal>.
+</Para>
+
+<Para>
+At first, I would like to thank <PersonName><FirstName>Avner</FirstName> <SurName>Lottem</SurName></PersonName> and <PersonName><Honorific>Doctor</Honorific> <FirstName>Ilana</FirstName> <SurName>David</Surname></PersonName> for their interest and assistance in this project.
+</Para>
+
+<Para>
+I would also like to thank the following people, who were involved in the
+design and implementation of the ext2 filesystem kernel code and support
+utilities:
+
+<ItemizedList>
+<ListItem>
+
+<Para>
+<PersonName><FirstName>Remy</FirstName> <SurName>Card</SurName></PersonName>
+
+Who designed, implemented and maintains the ext2 filesystem kernel
+code, and some of the ext2 utilities. Remy Card is also the author
+of several helpful slides concerning the ext2 filesystem.
+Specifically, he is the author of <Literal remap="tt">File Management in the Linux
+Kernel</Literal> and of <Literal remap="tt">The Second Extended File System - Current State,
+Future Development</Literal>.
+
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+<PersonName><FirstName>Wayne</FirstName> <SurName>Davison</SurName></PersonName>
+
+Who designed the ext2 filesystem.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+<PersonName><FirstName>Stephen</FirstName> <Surname>Tweedie</SurName></PersonName>
+
+Who helped designing the ext2 filesystem kernel code and wrote the
+slides <Literal remap="tt">Optimizations in File Systems</Literal>.
+</Para>
+</ListItem>
+<ListItem>
+
+<Para>
+<PersonName><FirstName>Theodore</FirstName> <SurName>Ts'o</SurName></PersonName>
+
+Who is the author of several ext2 utilities and of the ext2 library
+<Literal remap="tt">libext2fs</Literal> (which I didn't use, simply because I didn't know
+it exists when I started to work on my project).
+</Para>
+</ListItem>
+
+</ItemizedList>
+
+</Para>
+
+<Para>
+Lastly, I would like to thank, of-course, <PersonName><FirstName>Linus</FirstName> <SurName>Torvalds</SurName></PersonName> and the
+Linux community for providing all of us with such a great operating
+system.
+</Para>
+
+<Para>
+Please contact me in a case of bug report, suggestions, or just about
+anything concerning EXT2ED.
+</Para>
+
+<Para>
+Enjoy,
+</Para>
+
+<Para>
+Gadi Oxman &lt;tgud@tochnapc2.technion.ac.il&gt;
+</Para>
+
+<Para>
+Haifa, August 95
+</Para>
+
+</Sect1>
+
+</Article>
diff --git a/ext2ed/ext2.descriptors b/ext2ed/ext2.descriptors
new file mode 100644
index 0000000..e356f4c
--- /dev/null
+++ b/ext2ed/ext2.descriptors
@@ -0,0 +1,976 @@
+Extended 2 filesystem structure definitions for ext2ed.
+
+Most of this file is just copied from the ext2 main include file.
+
+My parser is very primitive - It only searches for the struct keywords,
+and uses the variables in there. The rest of the file is just ignored.
+
+You will find at the end a few additional types which are not available in
+the original include file, such as the types "file" and "dir". They have
+no variables, but are necessary due to the way ext2ed binds C commands
+to specific types.
+
+Gadi Oxman, 7/95
+
+Here is the original copyright:
+
+/*
+ * linux/include/linux/ext2_fs.h
+ *
+ * Copyright (C) 1992, 1993, 1994 Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * from
+ *
+ * linux/include/linux/minix_fs.h
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+
+/*
+ * ACL structures
+ */
+struct ext2_acl_header /* Header of Access Control Lists */
+{
+ unsigned long aclh_size;
+ unsigned long aclh_file_count;
+ unsigned long aclh_acle_count;
+ unsigned long aclh_first_acle;
+};
+
+struct ext2_acl_entry /* Access Control List Entry */
+{
+ unsigned long acle_size;
+ unsigned short acle_perms; /* Access permissions */
+ unsigned short acle_type; /* Type of entry */
+ unsigned short acle_tag; /* User or group identity */
+ unsigned short acle_pad1;
+ unsigned long acle_next; /* Pointer on next entry for the */
+ /* same inode or on next free entry */
+};
+
+/*
+ * Structure of a blocks group descriptor
+ */
+
+struct ext2_group_desc
+{
+ __u32 bg_block_bitmap; /* Blocks bitmap block */
+ __u32 bg_inode_bitmap; /* Inodes bitmap block */
+ __u32 bg_inode_table; /* Inodes table block */
+ __u16 bg_free_blocks_count; /* Free blocks count */
+ __u16 bg_free_inodes_count; /* Free inodes count */
+ __u16 bg_used_dirs_count; /* Directories count */
+ __u16 bg_pad;
+ __u32 bg_reserved[0];
+ __u32 bg_reserved[1];
+ __u32 bg_reserved[2];
+};
+
+/*
+ * Structure of an inode on the disk
+ */
+struct ext2_inode {
+ __u16 i_mode; /* File mode */
+ __u16 i_uid; /* Owner Uid */
+ __u32 i_size; /* Size in bytes */
+ __u32 i_atime; /* Access time */
+ __u32 i_ctime; /* Creation time */
+ __u32 i_mtime; /* Modification time */
+ __u32 i_dtime; /* Deletion Time */
+ __u16 i_gid; /* Group Id */
+ __u16 i_links_count; /* Links count */
+ __u32 i_blocks; /* Blocks count */
+ __u32 i_flags; /* File flags */
+ __u32 l_i_reserved1;
+ __u32 i_block[0]; /* Pointers to blocks */
+ __u32 i_block[1]; /* Pointers to blocks */
+ __u32 i_block[2]; /* Pointers to blocks */
+ __u32 i_block[3]; /* Pointers to blocks */
+ __u32 i_block[4]; /* Pointers to blocks */
+ __u32 i_block[5]; /* Pointers to blocks */
+ __u32 i_block[6]; /* Pointers to blocks */
+ __u32 i_block[7]; /* Pointers to blocks */
+ __u32 i_block[8]; /* Pointers to blocks */
+ __u32 i_block[9]; /* Pointers to blocks */
+ __u32 i_block[10]; /* Pointers to blocks */
+ __u32 i_block[11]; /* Pointers to blocks */
+ __u32 i_block[12]; /* Pointers to blocks */
+ __u32 i_block[13]; /* Pointers to blocks */
+ __u32 i_block[14]; /* Pointers to blocks */
+ __u32 i_version; /* File version (for NFS) */
+ __u32 i_file_acl; /* File ACL */
+ __u32 i_size_high; /* High 32bits of size */
+ __u32 i_faddr; /* Fragment address */
+ __u8 l_i_frag; /* Fragment number */
+ __u8 l_i_fsize; /* Fragment size */
+ __u16 i_pad1;
+ __u16 l_i_uid_high; /* these 2 fields */
+ __u16 l_i_gid_high; /* were reserved2[0] */
+ __u32 l_i_reserved2;
+};
+
+/*
+ * Structure of the super block
+ */
+struct ext2_super_block {
+ __u32 s_inodes_count; /* Inodes count */
+ __u32 s_blocks_count; /* Blocks count */
+ __u32 s_r_blocks_count; /* Reserved blocks count */
+ __u32 s_free_blocks_count; /* Free blocks count */
+ __u32 s_free_inodes_count; /* Free inodes count */
+ __u32 s_first_data_block; /* First Data Block */
+ __u32 s_log_block_size; /* Block size */
+ __s32 s_log_frag_size; /* Fragment size */
+ __u32 s_blocks_per_group; /* # Blocks per group */
+ __u32 s_frags_per_group; /* # Fragments per group */
+ __u32 s_inodes_per_group; /* # Inodes per group */
+ __u32 s_mtime; /* Mount time */
+ __u32 s_wtime; /* Write time */
+ __u16 s_mnt_count; /* Mount count */
+ __s16 s_max_mnt_count; /* Maximal mount count */
+ __u16 s_magic; /* Magic signature */
+ __u16 s_state; /* File system state */
+ __u16 s_errors; /* Behaviour when detecting errors */
+ __u16 s_minor_rev_level; /* minor revision level */
+ __u32 s_lastcheck; /* time of last check */
+ __u32 s_checkinterval; /* max. time between checks */
+ __u32 s_creator_os; /* OS */
+ __u32 s_rev_level; /* Revision level */
+ __u16 s_def_resuid; /* Default uid for reserved blocks */
+ __u16 s_def_resgid; /* Default gid for reserved blocks */
+ __u32 s_first_ino; /* First non-reserved inode */
+ __u16 s_inode_size; /* size of inode structure */
+ __u16 s_block_group_nr; /* block group # of this superblock */
+ __u32 s_feature_compat; /* compatible feature set */
+ __u32 s_feature_incompat; /* incompatible feature set */
+ __u32 s_feature_ro_compat; /* readonly-compatible feature set */
+ char[16] s_uuid; /* 128-bit uuid for volume */
+ char[16] s_volume_name; /* volume name */
+ char[64] s_last_mounted; /* directory where last mounted */
+ __u32 s_algorithm_usage_bitmap; /* For compression */
+ /*
+ * Performance hints. Directory preallocation should only
+ * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on.
+ */
+ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
+ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
+ __u16 s_padding1;
+ /*
+ * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set.
+ */
+ char[16] s_journal_uuid; /* uuid of journal superblock */
+ __u32 s_journal_inum; /* inode number of journal file */
+ __u32 s_journal_dev; /* device number of journal file */
+ __u32 s_last_orphan; /* start of list of inodes to delete */
+
+
+ __u32 s_reserved[0]; /* Padding to the end of the block */
+ __u32 s_reserved[1];
+ __u32 s_reserved[2];
+ __u32 s_reserved[3];
+ __u32 s_reserved[4];
+ __u32 s_reserved[5];
+ __u32 s_reserved[6];
+ __u32 s_reserved[7];
+ __u32 s_reserved[8];
+ __u32 s_reserved[9];
+ __u32 s_reserved[10];
+ __u32 s_reserved[11];
+ __u32 s_reserved[12];
+ __u32 s_reserved[13];
+ __u32 s_reserved[14];
+ __u32 s_reserved[15];
+ __u32 s_reserved[16];
+ __u32 s_reserved[17];
+ __u32 s_reserved[18];
+ __u32 s_reserved[19];
+ __u32 s_reserved[20];
+ __u32 s_reserved[21];
+ __u32 s_reserved[22];
+ __u32 s_reserved[23];
+ __u32 s_reserved[24];
+ __u32 s_reserved[25];
+ __u32 s_reserved[26];
+ __u32 s_reserved[27];
+ __u32 s_reserved[28];
+ __u32 s_reserved[29];
+ __u32 s_reserved[30];
+ __u32 s_reserved[31];
+ __u32 s_reserved[32];
+ __u32 s_reserved[33];
+ __u32 s_reserved[34];
+ __u32 s_reserved[35];
+ __u32 s_reserved[36];
+ __u32 s_reserved[37];
+ __u32 s_reserved[38];
+ __u32 s_reserved[39];
+ __u32 s_reserved[40];
+ __u32 s_reserved[41];
+ __u32 s_reserved[42];
+ __u32 s_reserved[43];
+ __u32 s_reserved[44];
+ __u32 s_reserved[45];
+ __u32 s_reserved[46];
+ __u32 s_reserved[47];
+ __u32 s_reserved[48];
+ __u32 s_reserved[49];
+ __u32 s_reserved[50];
+ __u32 s_reserved[51];
+ __u32 s_reserved[52];
+ __u32 s_reserved[53];
+ __u32 s_reserved[54];
+ __u32 s_reserved[55];
+ __u32 s_reserved[56];
+ __u32 s_reserved[57];
+ __u32 s_reserved[58];
+ __u32 s_reserved[59];
+ __u32 s_reserved[60];
+ __u32 s_reserved[61];
+ __u32 s_reserved[62];
+ __u32 s_reserved[63];
+ __u32 s_reserved[64];
+ __u32 s_reserved[65];
+ __u32 s_reserved[66];
+ __u32 s_reserved[67];
+ __u32 s_reserved[68];
+ __u32 s_reserved[69];
+ __u32 s_reserved[70];
+ __u32 s_reserved[71];
+ __u32 s_reserved[72];
+ __u32 s_reserved[73];
+ __u32 s_reserved[74];
+ __u32 s_reserved[75];
+ __u32 s_reserved[76];
+ __u32 s_reserved[77];
+ __u32 s_reserved[78];
+ __u32 s_reserved[79];
+ __u32 s_reserved[80];
+ __u32 s_reserved[81];
+ __u32 s_reserved[82];
+ __u32 s_reserved[83];
+ __u32 s_reserved[84];
+ __u32 s_reserved[85];
+ __u32 s_reserved[86];
+ __u32 s_reserved[87];
+ __u32 s_reserved[88];
+ __u32 s_reserved[89];
+ __u32 s_reserved[90];
+ __u32 s_reserved[91];
+ __u32 s_reserved[92];
+ __u32 s_reserved[93];
+ __u32 s_reserved[94];
+ __u32 s_reserved[95];
+ __u32 s_reserved[96];
+ __u32 s_reserved[97];
+ __u32 s_reserved[98];
+ __u32 s_reserved[99];
+ __u32 s_reserved[100];
+ __u32 s_reserved[101];
+ __u32 s_reserved[102];
+ __u32 s_reserved[103];
+ __u32 s_reserved[104];
+ __u32 s_reserved[105];
+ __u32 s_reserved[106];
+ __u32 s_reserved[107];
+ __u32 s_reserved[108];
+ __u32 s_reserved[109];
+ __u32 s_reserved[110];
+ __u32 s_reserved[111];
+ __u32 s_reserved[112];
+ __u32 s_reserved[113];
+ __u32 s_reserved[114];
+ __u32 s_reserved[115];
+ __u32 s_reserved[116];
+ __u32 s_reserved[117];
+ __u32 s_reserved[118];
+ __u32 s_reserved[119];
+ __u32 s_reserved[120];
+ __u32 s_reserved[121];
+ __u32 s_reserved[122];
+ __u32 s_reserved[123];
+ __u32 s_reserved[124];
+ __u32 s_reserved[125];
+ __u32 s_reserved[126];
+ __u32 s_reserved[127];
+ __u32 s_reserved[128];
+ __u32 s_reserved[129];
+ __u32 s_reserved[130];
+ __u32 s_reserved[131];
+ __u32 s_reserved[132];
+ __u32 s_reserved[133];
+ __u32 s_reserved[134];
+ __u32 s_reserved[135];
+ __u32 s_reserved[136];
+ __u32 s_reserved[137];
+ __u32 s_reserved[138];
+ __u32 s_reserved[139];
+ __u32 s_reserved[140];
+ __u32 s_reserved[141];
+ __u32 s_reserved[142];
+ __u32 s_reserved[143];
+ __u32 s_reserved[144];
+ __u32 s_reserved[145];
+ __u32 s_reserved[146];
+ __u32 s_reserved[147];
+ __u32 s_reserved[148];
+ __u32 s_reserved[149];
+ __u32 s_reserved[150];
+ __u32 s_reserved[151];
+ __u32 s_reserved[152];
+ __u32 s_reserved[153];
+ __u32 s_reserved[154];
+ __u32 s_reserved[155];
+ __u32 s_reserved[156];
+ __u32 s_reserved[157];
+ __u32 s_reserved[158];
+ __u32 s_reserved[159];
+ __u32 s_reserved[160];
+ __u32 s_reserved[161];
+ __u32 s_reserved[162];
+ __u32 s_reserved[163];
+ __u32 s_reserved[164];
+ __u32 s_reserved[165];
+ __u32 s_reserved[166];
+ __u32 s_reserved[167];
+ __u32 s_reserved[168];
+ __u32 s_reserved[169];
+ __u32 s_reserved[170];
+ __u32 s_reserved[171];
+ __u32 s_reserved[172];
+ __u32 s_reserved[173];
+ __u32 s_reserved[174];
+ __u32 s_reserved[175];
+ __u32 s_reserved[176];
+ __u32 s_reserved[177];
+ __u32 s_reserved[178];
+ __u32 s_reserved[179];
+ __u32 s_reserved[180];
+ __u32 s_reserved[181];
+ __u32 s_reserved[182];
+ __u32 s_reserved[183];
+ __u32 s_reserved[184];
+ __u32 s_reserved[185];
+ __u32 s_reserved[186];
+ __u32 s_reserved[187];
+ __u32 s_reserved[188];
+ __u32 s_reserved[189];
+ __u32 s_reserved[190];
+ __u32 s_reserved[191];
+ __u32 s_reserved[192];
+ __u32 s_reserved[193];
+ __u32 s_reserved[194];
+ __u32 s_reserved[195];
+ __u32 s_reserved[196];
+};
+
+The following is actually not used, due to the variable length of the
+name field. EXT2ED handles directories through the type "dir" below.
+
+/*
+ * Structure of a directory entry
+ */
+
+/* struct ext2_dir_entry { */
+/*
+ __u32 inode; /* Inode number */
+ __u16 rec_len; /* Directory entry length */
+ __u16 name_len; /* Name length */
+ char name[EXT2_NAME_LEN]; /* File name */
+};
+*/
+
+struct file {
+};
+
+struct dir {
+};
+
+struct block_bitmap {
+};
+
+struct inode_bitmap {
+};
+
+struct ext2_dx_root_node {
+ __u32 dot_inode;
+ __u16 dot_rec_len;
+ __u8 dot_name_len;
+ __u8 dot_file_type;
+ char[4] dot_name;
+ __u32 dot_dot_inode;
+ __u16 dot_dot_rec_len;
+ __u8 dot_dot_name_len;
+ __u8 dot_dot_file_type;
+ char[4] dot_dot_name;
+ __u32 reserved_zero;
+ __u8 hash_version; /* 0 now, 1 at release */
+ __u8 info_length; /* 8 */
+ __u8 indirect_levels;
+ __u8 unused_flags;
+ __u16 limit;
+ __u16 count;
+ __u32 block[0];
+ __u32 hash[1];
+ __u32 block[1];
+ __u32 hash[2];
+ __u32 block[2];
+ __u32 hash[3];
+ __u32 block[3];
+ __u32 hash[4];
+ __u32 block[4];
+ __u32 hash[5];
+ __u32 block[5];
+ __u32 hash[6];
+ __u32 block[6];
+ __u32 hash[7];
+ __u32 block[7];
+ __u32 hash[8];
+ __u32 block[8];
+ __u32 hash[9];
+ __u32 block[9];
+ __u32 hash[10];
+ __u32 block[10];
+ __u32 hash[11];
+ __u32 block[11];
+ __u32 hash[12];
+ __u32 block[12];
+ __u32 hash[13];
+ __u32 block[13];
+ __u32 hash[14];
+ __u32 block[14];
+ __u32 hash[15];
+ __u32 block[15];
+ __u32 hash[16];
+ __u32 block[16];
+ __u32 hash[17];
+ __u32 block[17];
+ __u32 hash[18];
+ __u32 block[18];
+ __u32 hash[19];
+ __u32 block[19];
+ __u32 hash[20];
+ __u32 block[20];
+ __u32 hash[21];
+ __u32 block[21];
+ __u32 hash[22];
+ __u32 block[22];
+ __u32 hash[23];
+ __u32 block[23];
+ __u32 hash[24];
+ __u32 block[24];
+ __u32 hash[25];
+ __u32 block[25];
+ __u32 hash[26];
+ __u32 block[26];
+ __u32 hash[27];
+ __u32 block[27];
+ __u32 hash[28];
+ __u32 block[28];
+ __u32 hash[29];
+ __u32 block[29];
+ __u32 hash[30];
+ __u32 block[30];
+ __u32 hash[31];
+ __u32 block[31];
+ __u32 hash[32];
+ __u32 block[32];
+ __u32 hash[33];
+ __u32 block[33];
+ __u32 hash[34];
+ __u32 block[34];
+ __u32 hash[35];
+ __u32 block[35];
+ __u32 hash[36];
+ __u32 block[36];
+ __u32 hash[37];
+ __u32 block[37];
+ __u32 hash[38];
+ __u32 block[38];
+ __u32 hash[39];
+ __u32 block[39];
+ __u32 hash[40];
+ __u32 block[40];
+ __u32 hash[41];
+ __u32 block[41];
+ __u32 hash[42];
+ __u32 block[42];
+ __u32 hash[43];
+ __u32 block[43];
+ __u32 hash[44];
+ __u32 block[44];
+ __u32 hash[45];
+ __u32 block[45];
+ __u32 hash[46];
+ __u32 block[46];
+ __u32 hash[47];
+ __u32 block[47];
+ __u32 hash[48];
+ __u32 block[48];
+ __u32 hash[49];
+ __u32 block[49];
+ __u32 hash[50];
+ __u32 block[50];
+ __u32 hash[51];
+ __u32 block[51];
+ __u32 hash[52];
+ __u32 block[52];
+ __u32 hash[53];
+ __u32 block[53];
+ __u32 hash[54];
+ __u32 block[54];
+ __u32 hash[55];
+ __u32 block[55];
+ __u32 hash[56];
+ __u32 block[56];
+ __u32 hash[57];
+ __u32 block[57];
+ __u32 hash[58];
+ __u32 block[58];
+ __u32 hash[59];
+ __u32 block[59];
+ __u32 hash[60];
+ __u32 block[60];
+ __u32 hash[61];
+ __u32 block[61];
+ __u32 hash[62];
+ __u32 block[62];
+ __u32 hash[63];
+ __u32 block[63];
+ __u32 hash[64];
+ __u32 block[64];
+ __u32 hash[65];
+ __u32 block[65];
+ __u32 hash[66];
+ __u32 block[66];
+ __u32 hash[67];
+ __u32 block[67];
+ __u32 hash[68];
+ __u32 block[68];
+ __u32 hash[69];
+ __u32 block[69];
+ __u32 hash[70];
+ __u32 block[70];
+ __u32 hash[71];
+ __u32 block[71];
+ __u32 hash[72];
+ __u32 block[72];
+ __u32 hash[73];
+ __u32 block[73];
+ __u32 hash[74];
+ __u32 block[74];
+ __u32 hash[75];
+ __u32 block[75];
+ __u32 hash[76];
+ __u32 block[76];
+ __u32 hash[77];
+ __u32 block[77];
+ __u32 hash[78];
+ __u32 block[78];
+ __u32 hash[79];
+ __u32 block[79];
+ __u32 hash[80];
+ __u32 block[80];
+ __u32 hash[81];
+ __u32 block[81];
+ __u32 hash[82];
+ __u32 block[82];
+ __u32 hash[83];
+ __u32 block[83];
+ __u32 hash[84];
+ __u32 block[84];
+ __u32 hash[85];
+ __u32 block[85];
+ __u32 hash[86];
+ __u32 block[86];
+ __u32 hash[87];
+ __u32 block[87];
+ __u32 hash[88];
+ __u32 block[88];
+ __u32 hash[89];
+ __u32 block[89];
+ __u32 hash[80];
+ __u32 block[80];
+ __u32 hash[81];
+ __u32 block[81];
+ __u32 hash[82];
+ __u32 block[82];
+ __u32 hash[83];
+ __u32 block[83];
+ __u32 hash[84];
+ __u32 block[84];
+ __u32 hash[85];
+ __u32 block[85];
+ __u32 hash[86];
+ __u32 block[86];
+ __u32 hash[87];
+ __u32 block[87];
+ __u32 hash[88];
+ __u32 block[88];
+ __u32 hash[89];
+ __u32 block[89];
+ __u32 hash[90];
+ __u32 block[90];
+ __u32 hash[91];
+ __u32 block[91];
+ __u32 hash[92];
+ __u32 block[92];
+ __u32 hash[93];
+ __u32 block[93];
+ __u32 hash[94];
+ __u32 block[94];
+ __u32 hash[95];
+ __u32 block[95];
+ __u32 hash[96];
+ __u32 block[96];
+ __u32 hash[97];
+ __u32 block[97];
+ __u32 hash[98];
+ __u32 block[98];
+ __u32 hash[99];
+ __u32 block[99];
+ __u32 hash[100];
+ __u32 block[100];
+ __u32 hash[101];
+ __u32 block[101];
+ __u32 hash[102];
+ __u32 block[102];
+ __u32 hash[103];
+ __u32 block[103];
+ __u32 hash[104];
+ __u32 block[104];
+ __u32 hash[105];
+ __u32 block[105];
+ __u32 hash[106];
+ __u32 block[106];
+ __u32 hash[107];
+ __u32 block[107];
+ __u32 hash[108];
+ __u32 block[108];
+ __u32 hash[109];
+ __u32 block[109];
+ __u32 hash[110];
+ __u32 block[110];
+ __u32 hash[111];
+ __u32 block[111];
+ __u32 hash[112];
+ __u32 block[112];
+ __u32 hash[113];
+ __u32 block[113];
+ __u32 hash[114];
+ __u32 block[114];
+ __u32 hash[115];
+ __u32 block[115];
+ __u32 hash[116];
+ __u32 block[116];
+ __u32 hash[117];
+ __u32 block[117];
+ __u32 hash[118];
+ __u32 block[118];
+ __u32 hash[119];
+ __u32 block[119];
+ __u32 hash[120];
+ __u32 block[120];
+ __u32 hash[121];
+ __u32 block[121];
+ __u32 hash[122];
+ __u32 block[122];
+ __u32 hash[123];
+ __u32 block[123];
+ __u32 hash[124];
+ __u32 block[124];
+ __u32 hash[125];
+ __u32 block[125];
+ __u32 hash[126];
+ __u32 block[126];
+ __u32 hash[127];
+ __u32 block[127];
+};
+
+struct ext2_dx_int_node {
+ __u32 fake_inode;
+ __u16 fake_rec_len;
+ __u8 fake_name_len;
+ __u8 fake_file_type;
+ __u16 limit;
+ __u16 count;
+ __u32 block[0];
+ __u32 hash[1];
+ __u32 block[1];
+ __u32 hash[2];
+ __u32 block[2];
+ __u32 hash[3];
+ __u32 block[3];
+ __u32 hash[4];
+ __u32 block[4];
+ __u32 hash[5];
+ __u32 block[5];
+ __u32 hash[6];
+ __u32 block[6];
+ __u32 hash[7];
+ __u32 block[7];
+ __u32 hash[8];
+ __u32 block[8];
+ __u32 hash[9];
+ __u32 block[9];
+ __u32 hash[10];
+ __u32 block[10];
+ __u32 hash[11];
+ __u32 block[11];
+ __u32 hash[12];
+ __u32 block[12];
+ __u32 hash[13];
+ __u32 block[13];
+ __u32 hash[14];
+ __u32 block[14];
+ __u32 hash[15];
+ __u32 block[15];
+ __u32 hash[16];
+ __u32 block[16];
+ __u32 hash[17];
+ __u32 block[17];
+ __u32 hash[18];
+ __u32 block[18];
+ __u32 hash[19];
+ __u32 block[19];
+ __u32 hash[20];
+ __u32 block[20];
+ __u32 hash[21];
+ __u32 block[21];
+ __u32 hash[22];
+ __u32 block[22];
+ __u32 hash[23];
+ __u32 block[23];
+ __u32 hash[24];
+ __u32 block[24];
+ __u32 hash[25];
+ __u32 block[25];
+ __u32 hash[26];
+ __u32 block[26];
+ __u32 hash[27];
+ __u32 block[27];
+ __u32 hash[28];
+ __u32 block[28];
+ __u32 hash[29];
+ __u32 block[29];
+ __u32 hash[30];
+ __u32 block[30];
+ __u32 hash[31];
+ __u32 block[31];
+ __u32 hash[32];
+ __u32 block[32];
+ __u32 hash[33];
+ __u32 block[33];
+ __u32 hash[34];
+ __u32 block[34];
+ __u32 hash[35];
+ __u32 block[35];
+ __u32 hash[36];
+ __u32 block[36];
+ __u32 hash[37];
+ __u32 block[37];
+ __u32 hash[38];
+ __u32 block[38];
+ __u32 hash[39];
+ __u32 block[39];
+ __u32 hash[40];
+ __u32 block[40];
+ __u32 hash[41];
+ __u32 block[41];
+ __u32 hash[42];
+ __u32 block[42];
+ __u32 hash[43];
+ __u32 block[43];
+ __u32 hash[44];
+ __u32 block[44];
+ __u32 hash[45];
+ __u32 block[45];
+ __u32 hash[46];
+ __u32 block[46];
+ __u32 hash[47];
+ __u32 block[47];
+ __u32 hash[48];
+ __u32 block[48];
+ __u32 hash[49];
+ __u32 block[49];
+ __u32 hash[50];
+ __u32 block[50];
+ __u32 hash[51];
+ __u32 block[51];
+ __u32 hash[52];
+ __u32 block[52];
+ __u32 hash[53];
+ __u32 block[53];
+ __u32 hash[54];
+ __u32 block[54];
+ __u32 hash[55];
+ __u32 block[55];
+ __u32 hash[56];
+ __u32 block[56];
+ __u32 hash[57];
+ __u32 block[57];
+ __u32 hash[58];
+ __u32 block[58];
+ __u32 hash[59];
+ __u32 block[59];
+ __u32 hash[60];
+ __u32 block[60];
+ __u32 hash[61];
+ __u32 block[61];
+ __u32 hash[62];
+ __u32 block[62];
+ __u32 hash[63];
+ __u32 block[63];
+ __u32 hash[64];
+ __u32 block[64];
+ __u32 hash[65];
+ __u32 block[65];
+ __u32 hash[66];
+ __u32 block[66];
+ __u32 hash[67];
+ __u32 block[67];
+ __u32 hash[68];
+ __u32 block[68];
+ __u32 hash[69];
+ __u32 block[69];
+ __u32 hash[70];
+ __u32 block[70];
+ __u32 hash[71];
+ __u32 block[71];
+ __u32 hash[72];
+ __u32 block[72];
+ __u32 hash[73];
+ __u32 block[73];
+ __u32 hash[74];
+ __u32 block[74];
+ __u32 hash[75];
+ __u32 block[75];
+ __u32 hash[76];
+ __u32 block[76];
+ __u32 hash[77];
+ __u32 block[77];
+ __u32 hash[78];
+ __u32 block[78];
+ __u32 hash[79];
+ __u32 block[79];
+ __u32 hash[80];
+ __u32 block[80];
+ __u32 hash[81];
+ __u32 block[81];
+ __u32 hash[82];
+ __u32 block[82];
+ __u32 hash[83];
+ __u32 block[83];
+ __u32 hash[84];
+ __u32 block[84];
+ __u32 hash[85];
+ __u32 block[85];
+ __u32 hash[86];
+ __u32 block[86];
+ __u32 hash[87];
+ __u32 block[87];
+ __u32 hash[88];
+ __u32 block[88];
+ __u32 hash[89];
+ __u32 block[89];
+ __u32 hash[80];
+ __u32 block[80];
+ __u32 hash[81];
+ __u32 block[81];
+ __u32 hash[82];
+ __u32 block[82];
+ __u32 hash[83];
+ __u32 block[83];
+ __u32 hash[84];
+ __u32 block[84];
+ __u32 hash[85];
+ __u32 block[85];
+ __u32 hash[86];
+ __u32 block[86];
+ __u32 hash[87];
+ __u32 block[87];
+ __u32 hash[88];
+ __u32 block[88];
+ __u32 hash[89];
+ __u32 block[89];
+ __u32 hash[90];
+ __u32 block[90];
+ __u32 hash[91];
+ __u32 block[91];
+ __u32 hash[92];
+ __u32 block[92];
+ __u32 hash[93];
+ __u32 block[93];
+ __u32 hash[94];
+ __u32 block[94];
+ __u32 hash[95];
+ __u32 block[95];
+ __u32 hash[96];
+ __u32 block[96];
+ __u32 hash[97];
+ __u32 block[97];
+ __u32 hash[98];
+ __u32 block[98];
+ __u32 hash[99];
+ __u32 block[99];
+ __u32 hash[100];
+ __u32 block[100];
+ __u32 hash[101];
+ __u32 block[101];
+ __u32 hash[102];
+ __u32 block[102];
+ __u32 hash[103];
+ __u32 block[103];
+ __u32 hash[104];
+ __u32 block[104];
+ __u32 hash[105];
+ __u32 block[105];
+ __u32 hash[106];
+ __u32 block[106];
+ __u32 hash[107];
+ __u32 block[107];
+ __u32 hash[108];
+ __u32 block[108];
+ __u32 hash[109];
+ __u32 block[109];
+ __u32 hash[110];
+ __u32 block[110];
+ __u32 hash[111];
+ __u32 block[111];
+ __u32 hash[112];
+ __u32 block[112];
+ __u32 hash[113];
+ __u32 block[113];
+ __u32 hash[114];
+ __u32 block[114];
+ __u32 hash[115];
+ __u32 block[115];
+ __u32 hash[116];
+ __u32 block[116];
+ __u32 hash[117];
+ __u32 block[117];
+ __u32 hash[118];
+ __u32 block[118];
+ __u32 hash[119];
+ __u32 block[119];
+ __u32 hash[120];
+ __u32 block[120];
+ __u32 hash[121];
+ __u32 block[121];
+ __u32 hash[122];
+ __u32 block[122];
+ __u32 hash[123];
+ __u32 block[123];
+ __u32 hash[124];
+ __u32 block[124];
+ __u32 hash[125];
+ __u32 block[125];
+ __u32 hash[126];
+ __u32 block[126];
+ __u32 hash[127];
+ __u32 block[127];
+};
+
+
diff --git a/ext2ed/ext2_com.c b/ext2ed/ext2_com.c
new file mode 100644
index 0000000..ed004f2
--- /dev/null
+++ b/ext2ed/ext2_com.c
@@ -0,0 +1,98 @@
+/*
+
+/usr/src/ext2ed/ext2_com.c
+
+A part of the extended file system 2 disk editor.
+
+--------------------------------------
+Extended-2 filesystem General commands
+--------------------------------------
+
+The commands here will be registered when we are editing an ext2 filesystem
+
+First written on: July 28 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+void type_ext2___super (char *command_line)
+
+/*
+
+We are moving to the superblock - Just use setoffset and settype. The offset was gathered in the
+initialization phase (but is constant - 1024).
+
+*/
+
+{
+ char buffer [80];
+
+ super_info.copy_num=0;
+ sprintf (buffer,"setoffset %ld",file_system_info.super_block_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_super_block");dispatch (buffer);
+}
+
+void type_ext2___cd (char *command_line)
+
+/*
+
+A global cd command - The path should start with /.
+
+We implement it through dispatching to our primitive functions.
+
+*/
+
+{
+ char temp [80],buffer [80],*ptr;
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ if (buffer [0] != '/') {
+ wprintw (command_win,"Error - Use a full pathname (begin with '/')\n");refresh_command_win ();return;
+ }
+
+ /* Note the various dispatches below - They should be intuitive if you know the ext2 filesystem structure */
+
+ dispatch ("super");dispatch ("group");dispatch ("inode");dispatch ("next");dispatch ("dir");
+ if (buffer [1] != 0) {
+ sprintf (temp,"cd %s",buffer+1);dispatch (temp);
+ }
+}
+
+void type_ext2___group (char *command_line)
+
+/*
+
+We go to the group descriptors.
+First, we go to the first group descriptor in the main copy.
+Then, we use the group's entry command to pass to another group.
+
+*/
+
+{
+ long group_num=0;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ group_num=atol (buffer);
+ }
+
+ group_info.copy_num=0;group_info.group_num=0;
+ sprintf (buffer,"setoffset %ld",file_system_info.first_group_desc_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_group_desc");dispatch (buffer);
+ sprintf (buffer,"entry %ld",group_num);dispatch (buffer);
+}
diff --git a/ext2ed/ext2ed.8.in b/ext2ed/ext2ed.8.in
new file mode 100644
index 0000000..e827277
--- /dev/null
+++ b/ext2ed/ext2ed.8.in
@@ -0,0 +1,72 @@
+.\" -*- nroff -*-
+.TH EXT2ED 8 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@"
+.SH NAME
+ext2ed \- ext2 file system editor
+.SH SYNOPSIS
+.B ext2ed
+.SH DESCRIPTION
+.B ext2ed
+in an
+.B editor
+for the
+.B second extended file system.
+Its aim is to show you the various internal file system structures in an
+intuitive form so that you would be able to easily understand and modify
+them.
+.SH DOCUMENTATION
+The documentation is not available in man page format. Instead, I have
+written three articles which are related to ext2ed:
+
+The first article is
+.B The user's guide.
+This article explains how to use ext2ed.
+
+The second article is
+.B The Ext2fs overview.
+This article gives an overview of internal structure of the ext2 file system.
+You need to understand the internal layout in order to effectively edit
+your file system.
+
+The third article is
+.B EXT2ED - Design and implementation.
+This article explains how I constructed ext2ed. You may want to have a look
+in it if you plan to view or modify the source code.
+
+.SH WARNING
+
+.B
+Do not use ext2ed on a mounted file system.
+
+.SH FILES
+.TP
+.I /usr/bin/ext2ed
+The program itself.
+.TP
+.I @root_sysconfdir@/ext2ed.conf
+ext2ed's configuration file.
+.TP
+.I @datadir@/ext2.descriptors
+Definition of the various objects for the ext2 file system.
+.TP
+.I /var/log/ext2ed.log
+Log file of actual changes made to the file system.
+.TP
+.I /usr/man/man8/ext2ed.8
+The manual page.
+.TP
+.I @datadir@/doc/ext2ed/user-guide-0.1.ps
+The user's guide.
+.TP
+.I @datadir@/doc/ext2ed/Ext2fs-overview-0.1.ps
+Technical overview of the ext2 file system.
+.TP
+.I @datadir@/doc/ext2ed/ext2ed-design-0.1.ps
+EXT2ED design notes.
+
+.SH BUGS
+File Systems bigger than 2 GB aren't yet supported.
+.SH AUTHOR
+Gadi Oxman <tgud@tochnapc2.technion.ac.il>
+.SH SEE ALSO
+.BR e2fsck (8),
+.BR debugfs (8)
diff --git a/ext2ed/ext2ed.conf.in b/ext2ed/ext2ed.conf.in
new file mode 100644
index 0000000..c079623
--- /dev/null
+++ b/ext2ed/ext2ed.conf.in
@@ -0,0 +1,79 @@
+##############################################################################
+# ext2ed.conf #
+# #
+# Configuration file for the extended 2 file system disk editor. #
+##############################################################################
+
+# Ext2Descriptors is the location of the ext2 filesystem structure
+# definitions.
+
+
+Ext2Descriptors @datadir@/ext2.descriptors
+
+
+# Using AlternateDescriptors you can declare additional structures. Those
+# structures can contain only variables. Linking functions to the objects is
+# possible only through source code additions.
+
+
+AlternateDescriptors
+
+
+# LogFile is the location of the log file. Actual changes to the filesystem
+# are logged there. See also LogChanges.
+
+
+LogFile /var/log/ext2ed.log
+
+
+# The following selects the default behavior when changes are made to the
+# filesystem. When on, each change will be logged - Both the previous data
+# and the new written data.
+
+
+LogChanges on
+
+
+# AllowChanges off will not allow ext2ed to do any changes to the
+# filesystem - The "enablewrite" command will not work. When on, enablewrite
+# will still have to be issued to allow write access.
+
+
+AllowChanges on
+
+
+# With this option you can choose whether ext2ed will allow read-only mode on
+# a mounted filesystem. Read-Write mode is never allowed on a mounted
+# filesystem.
+
+
+AllowMountedRead on
+
+
+# When ForceExt2 is set to on, the filesystem is assumed to be ext2
+# filesystem, despite the possibly corrupt superblock magic number reading.
+# All the ext2 specific commands will be available despite the possible
+# autodetection failure.
+
+ForceExt2 off
+
+
+# Normally, the various filesystem parameters such as the block size and the
+# total number of blocks are gathered from the ext2 filesystem itself.
+# However, on a corrupt filesystem, ext2ed is unable to get the right
+# parameters. In this case, they will be taken from here. See also
+# ForceDefault.
+
+
+DefaultBlockSize 1024
+DefaultTotalBlocks 2097151 # ~2 GB total size
+DefaultBlocksInGroup 8192
+
+# With ForceDefault on, you can force the use of the default parameters
+# above. This is not recommended, as ext2ed will fallback by default to those
+# parameters if it can't figure up the parameters from the filesystem itself.
+
+
+ForceDefault off
+
+
diff --git a/ext2ed/ext2ed.h b/ext2ed/ext2ed.h
new file mode 100644
index 0000000..0ac77b7
--- /dev/null
+++ b/ext2ed/ext2ed.h
@@ -0,0 +1,435 @@
+
+/*
+
+/usr/src/ext2ed/ext2ed.h
+
+A part of the extended file system 2 disk editor.
+
+--------------------------------------
+Include file for the ext2 disk editor.
+--------------------------------------
+
+This file contains declarations which are needed by all the files in ext2ed.
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#ifndef EXT2ED_EDITOR_H
+#define EXT2ED_EDITOR_H
+
+/*
+
+-----------------------
+ User definable options
+-----------------------
+
+*/
+
+#define DEBUG /* Activate self-sanity checks */
+
+#include <ext2fs/ext2_fs.h> /* Main kernel ext2 include file */
+#include <sys/stat.h>
+
+#include <ncurses.h>
+
+#define MAX_FIELDS 400
+
+#define MAX_COMMAND_LINE 81
+#define MAX_COMMANDS_NUM 30 /* Maximum number of commands of one type */
+#define REMEMBER_COUNT 30 /* Object memory size */
+
+/*
+ The user screen consists of four parts:
+
+ 1. Title window (title_win).
+ 2. Show (status) window (show_win).
+ 3. Main show pad (show_pad).
+ 4. Command window (command_win).
+
+*/
+
+/*
+
+ The show pad is mapped to the space left between the other three windows.
+
+ If you wondered why ext2ed grabs so memory, the answer is probably below - I wanted to treat
+ the virtual display as infinite. Decrease the following for more realistic memory consumption.
+
+*/
+
+#define SHOW_PAD_LINES 3000
+#define SHOW_PAD_COLS (COLS > 140 ? COLS : 140)
+
+#define COMMAND_WIN_LINES 6 /* Change this to your preferences */
+#define TITLE_WIN_LINES 3
+#define SHOW_WIN_LINES 3
+
+#define HEX 1
+#define TEXT 2
+
+#ifndef EXT2_PRE_02B_MAGIC
+ #define EXT2_PRE_02B_MAGIC 0xEF51
+#endif
+
+
+typedef void (*PF) (char *); /* Used to point to the dispatched functions */
+
+struct struct_commands { /* Holds commands of an object */
+ int last_command;
+ char *names [MAX_COMMANDS_NUM];
+ char *descriptions [MAX_COMMANDS_NUM];
+ PF callback [MAX_COMMANDS_NUM];
+};
+
+struct struct_descriptor { /* Describes an object */
+ unsigned long length;
+ unsigned char name [60];
+ unsigned short fields_num;
+ unsigned char field_names [MAX_FIELDS][80];
+ unsigned char field_types [MAX_FIELDS];
+ unsigned short field_lengths [MAX_FIELDS];
+ unsigned short field_positions [MAX_FIELDS];
+ struct struct_commands type_commands;
+ struct struct_descriptor *prev,*next;
+};
+
+#define FIELD_TYPE_INT 1
+#define FIELD_TYPE_UINT 2
+#define FIELD_TYPE_CHAR 3
+
+struct struct_type_data { /* The object's data is usually here */
+ long offset_in_block;
+
+ union union_type_data { /* Format it in various ways */
+ char buffer [EXT2_MAX_BLOCK_SIZE];
+ struct ext2_acl_header t_ext2_acl_header;
+ struct ext2_acl_entry t_ext2_acl_entry;
+ struct ext2_group_desc t_ext2_group_desc;
+ struct ext2_inode t_ext2_inode;
+ struct ext2_super_block t_ext2_super_block;
+ struct ext2_dir_entry t_ext2_dir_entry;
+ } u;
+};
+
+struct struct_file_system_info { /* Important information about the filesystem */
+ unsigned long long file_system_size;
+ unsigned long super_block_offset;
+ unsigned long first_group_desc_offset;
+ unsigned long groups_count;
+ unsigned long inodes_per_block;
+ unsigned long blocks_per_group; /* The name is misleading; beware */
+ unsigned long no_blocks_in_group;
+ unsigned short block_size;
+ struct ext2_super_block super_block;
+};
+
+struct struct_file_info { /* Used to handle files and directories */
+
+ struct ext2_inode *inode_ptr;
+
+ long inode_offset;
+ long global_block_num,global_block_offset;
+ long block_num,blocks_count;
+ long file_offset,file_length;
+ long level;
+ unsigned char buffer [EXT2_MAX_BLOCK_SIZE];
+ long offset_in_block;
+
+ int display;
+ /* The following is used if the file is a directory */
+
+ long dir_entry_num,dir_entries_count;
+ long dir_entry_offset;
+};
+
+struct struct_super_info { /* Used to handle the superblock */
+ unsigned long copy_num;
+};
+
+struct struct_group_info { /* Used to handle the group descriptors */
+ unsigned long copy_num;
+ unsigned long group_num;
+};
+
+struct struct_block_bitmap_info { /* Used in blockbitmap_com.c */
+ unsigned long entry_num;
+ unsigned long group_num;
+};
+
+struct struct_inode_bitmap_info { /* Used in inodebitmap_com.c */
+ unsigned long entry_num;
+ unsigned long group_num;
+};
+
+struct struct_remember_lifo { /* Implements the objects circular memory */
+ long entries_count;
+
+ long offset [REMEMBER_COUNT];
+ struct struct_descriptor *type [REMEMBER_COUNT];
+ char name [REMEMBER_COUNT][80];
+};
+
+struct struct_pad_info { /* Used to zoom into the pad window */
+ int display_lines,display_cols;
+ int line,col;
+ int max_line,max_col;
+ int disable_output;
+};
+
+/* Global variables (defined mostly in main.c) */
+
+/* Configurable variables (Through configuration file) */
+
+extern char AlternateDescriptors [200];
+extern char Ext2Descriptors [200];
+extern char LogFile [200];
+extern int LogChanges;
+extern int AllowChanges;
+extern int AllowMountedRead;
+extern int ForceExt2;
+extern int DefaultBlockSize;
+extern unsigned long DefaultTotalBlocks;
+extern unsigned long DefaultBlocksInGroup;
+extern int ForceDefault;
+
+extern char device_name [80];
+extern char last_command_line [80];
+extern FILE *device_handle;
+extern long device_offset;
+extern int mounted;
+
+extern short block_size;
+extern struct struct_commands general_commands;
+extern struct struct_commands ext2_commands;
+extern struct struct_descriptor *first_type;
+extern struct struct_descriptor *last_type;
+extern struct struct_descriptor *current_type;
+extern struct struct_type_data type_data;
+extern struct struct_file_system_info file_system_info;
+extern struct struct_file_info file_info,first_file_info;
+extern struct struct_group_info group_info;
+extern struct struct_super_info super_info;
+extern struct struct_block_bitmap_info block_bitmap_info;
+extern struct struct_inode_bitmap_info inode_bitmap_info;
+extern struct struct_remember_lifo remember_lifo;
+extern struct struct_pad_info show_pad_info;
+extern int write_access;
+
+extern int redraw_request;
+extern char lines_s [80];
+extern char cols_s [80];
+
+
+/* init.c */
+
+extern int init (void);
+extern void prepare_to_close (void);
+extern int set_struct_descriptors (char *file_name);
+extern void free_struct_descriptors (void);
+extern struct struct_descriptor *add_new_descriptor (char *name);
+extern void add_new_variable (struct struct_descriptor *descriptor,char *v_type,char *v_name);
+extern void fill_type_commands (struct struct_descriptor *ptr);
+extern void add_user_command (struct struct_commands *ptr,char *name,char *description,PF callback);
+extern void free_user_commands (struct struct_commands *ptr);
+extern int set_file_system_info (void);
+extern int process_configuration_file (void);
+extern void add_general_commands (void);
+extern void add_ext2_general_commands (void);
+extern void check_mounted (char *name);
+
+int get_next_option (FILE *fp,char *option,char *value);
+void init_readline (void);
+void init_signals (void);
+void signal_SIGWINCH_handler (int sig_num);
+void signal_SIGTERM_handler (int sig_num);
+void signal_SIGSEGV_handler (int sig_num);
+
+/* general_com.c */
+
+/* General commands which are available always */
+
+extern void help (char *command_line);
+extern void set (char *command_line);
+extern void set_device (char *command_line);
+extern void set_offset (char *command_line);
+extern void set_type (char *command_line);
+extern void show (char *command_line);
+extern void pgup (char *command_line);
+extern void pgdn (char *command_line);
+extern void redraw (char *command_line);
+extern void remember (char *command_line);
+extern void recall (char *command_line);
+extern void cd (char *command_line);
+extern void enable_write (char *command_line);
+extern void disable_write (char *command_line);
+extern void write_data (char *command_line);
+extern void next (char *command_line);
+extern void prev (char *command_line);
+
+void hex_set (char *command_line);
+void detailed_help (char *text);
+
+
+/* ext2_com.c */
+
+/* Extended2 filesystem general commands - Available only when editing an
+ ext2 filesystem */
+
+extern void type_ext2___super (char *command_line);
+extern void type_ext2___group (char *command_line);
+extern void type_ext2___cd (char *command_line);
+
+
+/* main.c */
+
+extern int version_major,version_minor;
+extern char revision_date [80];
+extern char email_address [80];
+
+#ifdef DEBUG
+extern void internal_error (char *description,char *source_name,char *function_name);
+#endif
+
+void parser (void);
+extern int dispatch (char *command_line);
+char *parse_word (char *source,char *dest);
+char *complete_command (char *text,int state);
+char *dupstr (char *src);
+
+
+
+/* disk.c */
+
+extern int load_type_data (void);
+extern int write_type_data (void);
+extern int low_read (unsigned char *buffer,unsigned long length,unsigned long offset);
+extern int low_write (unsigned char *buffer,unsigned long length,unsigned long offset);
+extern int log_changes (unsigned char *buffer,unsigned long length,unsigned long offset);
+
+/* file_com.c */
+
+extern int init_file_info (void);
+extern void type_file___show (char *command_line);
+extern void type_file___inode (char *command_line);
+extern void type_file___display (char *command_line);
+extern void type_file___prev (char *command_line);
+extern void type_file___next (char *command_line);
+extern void type_file___offset (char *command_line);
+extern void type_file___prevblock (char *command_line);
+extern void type_file___nextblock (char *command_line);
+extern void type_file___block (char *command_line);
+extern void type_file___remember (char *command_line);
+extern void type_file___set (char *command_line);
+extern void type_file___writedata (char *command_line);
+
+extern long file_block_to_global_block (long file_block,struct struct_file_info *file_info_ptr);
+extern long return_indirect (long table_block,long block_num);
+extern long return_dindirect (long table_block,long block_num);
+extern long return_tindirect (long table_block,long block_num);
+
+void file_show_hex (void);
+void file_show_text (void);
+void show_status (void);
+
+/* inode_com.c */
+
+extern void type_ext2_inode___next (char *command_line);
+extern void type_ext2_inode___prev (char *command_line);
+extern void type_ext2_inode___show (char *command_line);
+extern void type_ext2_inode___group (char *command_line);
+extern void type_ext2_inode___entry (char *command_line);
+extern void type_ext2_inode___file (char *command_line);
+extern void type_ext2_inode___dir (char *command_line);
+
+extern long inode_offset_to_group_num (long inode_offset);
+extern long int inode_offset_to_inode_num (long inode_offset);
+extern long int inode_num_to_inode_offset (long inode_num);
+
+/* dir_com.c */
+
+extern int init_dir_info (struct struct_file_info *info);
+extern void type_dir___show (char *command_line);
+extern void type_dir___inode (char *command_line);
+extern void type_dir___pgdn (char *command_line);
+extern void type_dir___pgup (char *command_line);
+extern void type_dir___prev (char *command_line);
+extern void type_dir___next (char *command_line);
+extern void type_dir___followinode (char *command_line);
+extern void type_dir___remember (char *command_line);
+extern void type_dir___cd (char *command_line);
+extern void type_dir___entry (char *command_line);
+extern void type_dir___writedata (char *command_line);
+extern void type_dir___set (char *command_line);
+
+#define HEX 1
+#define TEXT 2
+
+#define ABORT 0
+#define CONTINUE 1
+#define FOUND 2
+
+struct struct_file_info search_dir_entries (int (*action) (struct struct_file_info *info),int *status);
+int action_count (struct struct_file_info *info);
+void show_dir_status (void);
+long count_dir_entries (void);
+int action_name (struct struct_file_info *info);
+int action_entry_num (struct struct_file_info *info);
+int action_show (struct struct_file_info *info);
+
+/* super_com.c */
+
+extern void type_ext2_super_block___show (char *command_line);
+extern void type_ext2_super_block___gocopy (char *command_line);
+extern void type_ext2_super_block___setactivecopy (char *command_line);
+
+/* group_com.c */
+
+extern void type_ext2_group_desc___next (char *command_line);
+extern void type_ext2_group_desc___prev (char *command_line);
+extern void type_ext2_group_desc___entry (char *command_line);
+extern void type_ext2_group_desc___show (char *command_line);
+extern void type_ext2_group_desc___inode (char *command_line);
+extern void type_ext2_group_desc___gocopy (char *command_line);
+extern void type_ext2_group_desc___blockbitmap (char *command_line);
+extern void type_ext2_group_desc___inodebitmap (char *command_line);
+extern void type_ext2_group_desc___setactivecopy (char *command_line);
+
+/* blockbitmap_com.c */
+
+extern void type_ext2_block_bitmap___show (char *command_line);
+extern void type_ext2_block_bitmap___entry (char *command_line);
+extern void type_ext2_block_bitmap___next (char *command_line);
+extern void type_ext2_block_bitmap___prev (char *command_line);
+extern void type_ext2_block_bitmap___allocate (char *command_line);
+extern void type_ext2_block_bitmap___deallocate (char *command_line);
+void allocate_block (long entry_num);
+void deallocate_block (long entry_num);
+
+/* inodebitmap_bom.c */
+
+extern void type_ext2_inode_bitmap___show (char *command_line);
+extern void type_ext2_inode_bitmap___entry (char *command_line);
+extern void type_ext2_inode_bitmap___next (char *command_line);
+extern void type_ext2_inode_bitmap___prev (char *command_line);
+extern void type_ext2_inode_bitmap___allocate (char *command_line);
+extern void type_ext2_inode_bitmap___deallocate (char *command_line);
+void allocate_inode (long entry_num);
+void deallocate_inode (long entry_num);
+
+/* win.c */
+
+extern WINDOW *title_win,*show_win,*command_win,*show_pad;
+
+extern void init_windows (void);
+extern void refresh_title_win (void);
+extern void refresh_show_win (void);
+extern void refresh_show_pad (void);
+extern void refresh_command_win (void);
+extern void show_info (void);
+extern void redraw_all (void);
+extern void close_windows (void);
+
+#endif /* EXT2ED_EDITOR_H */
diff --git a/ext2ed/file_com.c b/ext2ed/file_com.c
new file mode 100644
index 0000000..d667959
--- /dev/null
+++ b/ext2ed/file_com.c
@@ -0,0 +1,565 @@
+/*
+
+/usr/src/ext2ed/file_com.c
+
+A part of the extended file system 2 disk editor.
+
+----------------------------
+Commands which handle a file
+----------------------------
+
+First written on: April 18 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+int init_file_info (void)
+
+{
+ struct ext2_inode *ptr;
+
+ ptr=&type_data.u.t_ext2_inode;
+
+ file_info.inode_ptr=ptr;
+ file_info.inode_offset=device_offset;
+
+ file_info.global_block_num=ptr->i_block [0];
+ file_info.global_block_offset=ptr->i_block [0]*file_system_info.block_size;
+ file_info.block_num=0;
+ file_info.blocks_count=(ptr->i_size+file_system_info.block_size-1)/file_system_info.block_size;
+ file_info.file_offset=0;
+ file_info.file_length=ptr->i_size;
+ file_info.level=0;
+ file_info.offset_in_block=0;
+
+ file_info.display=HEX;
+
+ low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+
+ return (1);
+}
+
+
+void type_file___inode (char *command_line)
+
+{
+ dispatch ("settype ext2_inode");
+}
+
+void type_file___show (char *command_line)
+
+{
+ if (file_info.display==HEX)
+ file_show_hex ();
+ if (file_info.display==TEXT)
+ file_show_text ();
+}
+
+void type_file___nextblock (char *command_line)
+
+{
+ long block_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ block_offset*=atol (buffer);
+ }
+
+ if (file_info.block_num+block_offset >= file_info.blocks_count) {
+ wprintw (command_win,"Error - Block offset out of range\n");wrefresh (command_win);
+ return;
+ }
+
+ file_info.block_num+=block_offset;
+ file_info.global_block_num=file_block_to_global_block (file_info.block_num,&file_info);
+ file_info.global_block_offset=file_info.global_block_num*file_system_info.block_size;
+ file_info.file_offset=file_info.block_num*file_system_info.block_size;
+
+ low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_file___next (char *command_line)
+
+{
+ int offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ if (file_info.offset_in_block+offset < file_system_info.block_size) {
+ file_info.offset_in_block+=offset;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
+ }
+}
+
+void type_file___offset (char *command_line)
+
+{
+ unsigned long offset;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset=atol (buffer);
+ }
+ else {
+ wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();
+ return;
+ }
+
+ if (offset < file_system_info.block_size) {
+ file_info.offset_in_block=offset;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
+ }
+}
+
+void type_file___prev (char *command_line)
+
+{
+ int offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ if (file_info.offset_in_block-offset >= 0) {
+ file_info.offset_in_block-=offset;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
+ }
+}
+
+void type_file___prevblock (char *command_line)
+
+{
+ long block_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ block_offset*=atol (buffer);
+ }
+
+ if (file_info.block_num-block_offset < 0) {
+ wprintw (command_win,"Error - Block offset out of range\n");wrefresh (command_win);
+ return;
+ }
+
+ file_info.block_num-=block_offset;
+ file_info.global_block_num=file_block_to_global_block (file_info.block_num,&file_info);
+ file_info.global_block_offset=file_info.global_block_num*file_system_info.block_size;
+ file_info.file_offset=file_info.block_num*file_system_info.block_size;
+
+ low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_file___block (char *command_line)
+
+{
+ long block_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Invalid arguments\n");wrefresh (command_win);
+ return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+ block_offset=atol (buffer);
+
+ if (block_offset < 0 || block_offset >= file_info.blocks_count) {
+ wprintw (command_win,"Error - Block offset out of range\n");wrefresh (command_win);
+ return;
+ }
+
+ file_info.block_num=block_offset;
+ file_info.global_block_num=file_block_to_global_block (file_info.block_num,&file_info);
+ file_info.global_block_offset=file_info.global_block_num*file_system_info.block_size;
+ file_info.file_offset=file_info.block_num*file_system_info.block_size;
+
+ low_read (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_file___display (char *command_line)
+
+{
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0)
+ strcpy (buffer,"hex");
+ else
+ ptr=parse_word (ptr,buffer);
+
+ if (strcasecmp (buffer,"hex")==0) {
+ wprintw (command_win,"Display set to hex\n");wrefresh (command_win);
+ file_info.display=HEX;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else if (strcasecmp (buffer,"text")==0) {
+ wprintw (command_win,"Display set to text\n");wrefresh (command_win);
+ file_info.display=TEXT;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Invalid arguments\n");wrefresh (command_win);
+ }
+}
+
+void file_show_hex (void)
+
+{
+ long offset=0,l,i;
+ unsigned char *ch_ptr;
+
+ /* device_offset and type_data points to the inode */
+
+ show_pad_info.line=0;
+
+ wmove (show_pad,0,0);
+ ch_ptr=file_info.buffer;
+ for (l=0;l<file_system_info.block_size/16;l++) {
+ if (file_info.file_offset+offset>file_info.file_length-1) break;
+ wprintw (show_pad,"%08ld : ",offset);
+ for (i=0;i<16;i++) {
+
+ if (file_info.file_offset+offset+i>file_info.file_length-1) {
+ wprintw (show_pad," ");
+ }
+
+ else {
+ if (file_info.offset_in_block==offset+i)
+ wattrset (show_pad,A_REVERSE);
+
+ if (ch_ptr [i]>=' ' && ch_ptr [i]<='z')
+ wprintw (show_pad,"%c",ch_ptr [i]);
+ else
+ wprintw (show_pad,".");
+
+ if (file_info.offset_in_block==offset+i)
+ wattrset (show_pad,A_NORMAL);
+ }
+ }
+
+ wprintw (show_pad," ");
+ for (i=0;i<16;i++) {
+ if (file_info.file_offset+offset+i>file_info.file_length-1) break;
+ if (file_info.offset_in_block==offset+i)
+ wattrset (show_pad,A_REVERSE);
+
+ wprintw (show_pad,"%02x",ch_ptr [i]);
+
+ if (file_info.offset_in_block==offset+i) {
+ wattrset (show_pad,A_NORMAL);
+ show_pad_info.line=l-l % show_pad_info.display_lines;
+ }
+
+ wprintw (show_pad," ");
+
+ }
+
+ wprintw (show_pad,"\n");
+ offset+=i;
+ ch_ptr+=i;
+ }
+
+ show_pad_info.max_line=l-1;
+
+ refresh_show_pad ();
+
+ show_status ();
+}
+
+void file_show_text (void)
+
+{
+ long offset=0,last_offset,l=0,cols=0;
+ unsigned char *ch_ptr;
+
+ /* device_offset and type_data points to the inode */
+
+ show_pad_info.line=0;
+ wmove (show_pad,0,0);
+ ch_ptr=file_info.buffer;
+
+ last_offset=file_system_info.block_size-1;
+
+ if (file_info.file_offset+last_offset > file_info.file_length-1)
+ last_offset=file_info.file_length-1-file_info.file_offset;
+
+ while ( (offset <= last_offset) && l<SHOW_PAD_LINES) {
+
+ if (cols==SHOW_PAD_COLS-1) {
+ wprintw (show_pad,"\n");
+ l++;cols=0;
+ }
+
+
+ if (file_info.offset_in_block==offset)
+ wattrset (show_pad,A_REVERSE);
+
+ if (*ch_ptr >= ' ' && *ch_ptr <= 'z')
+ wprintw (show_pad,"%c",*ch_ptr);
+
+
+ else {
+ if (*ch_ptr == 0xa) {
+ wprintw (show_pad,"\n");
+ l++;cols=0;
+ }
+
+ else if (*ch_ptr == 0x9)
+ wprintw (show_pad," ");
+
+ else
+ wprintw (show_pad,".");
+ }
+
+ if (file_info.offset_in_block==offset) {
+ wattrset (show_pad,A_NORMAL);
+ show_pad_info.line=l-l % show_pad_info.display_lines;
+ }
+
+
+ offset++;cols++;ch_ptr++;
+ }
+
+ wprintw (show_pad,"\n");
+ show_pad_info.max_line=l;
+
+ refresh_show_pad ();
+
+ show_status ();
+}
+
+void show_status (void)
+
+{
+ long inode_num;
+
+ werase (show_win);wmove (show_win,0,0);
+ wprintw (show_win,"File contents. Block %ld. ",file_info.global_block_num);
+ wprintw (show_win,"File block %ld of %ld. ",file_info.block_num,file_info.blocks_count-1);
+ wprintw (show_win,"File Offset %ld of %ld.",file_info.file_offset,file_info.file_length-1);
+
+ wmove (show_win,1,0);
+ inode_num=inode_offset_to_inode_num (file_info.inode_offset);
+ wprintw (show_win,"File inode %ld. Indirection level %ld.",inode_num,file_info.level);
+
+ refresh_show_win ();
+}
+
+void type_file___remember (char *command_line)
+
+{
+ int found=0;
+ long entry_num;
+ char *ptr,buffer [80];
+ struct struct_descriptor *descriptor_ptr;
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");wrefresh (command_win);
+ return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+ entry_num=remember_lifo.entries_count++;
+ if (entry_num>REMEMBER_COUNT-1) {
+ entry_num=0;
+ remember_lifo.entries_count--;
+ }
+
+ descriptor_ptr=first_type;
+ while (descriptor_ptr!=NULL && !found) {
+ if (strcmp (descriptor_ptr->name,"ext2_inode")==0)
+ found=1;
+ else
+ descriptor_ptr=descriptor_ptr->next;
+ }
+
+
+ remember_lifo.offset [entry_num]=device_offset;
+ remember_lifo.type [entry_num]=descriptor_ptr;
+ strcpy (remember_lifo.name [entry_num],buffer);
+
+ wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",descriptor_ptr->name,device_offset,buffer);
+ wrefresh (command_win);
+}
+
+void type_file___set (char *command_line)
+
+{
+ unsigned char tmp;
+ char *ptr,buffer [80],*ch_ptr;
+ int mode=HEX;
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+ if (strcasecmp (buffer,"text")==0) {
+ mode=TEXT;
+ strcpy (buffer,ptr);
+ }
+
+ else if (strcasecmp (buffer,"hex")==0) {
+ mode=HEX;
+ ptr=parse_word (ptr,buffer);
+ }
+
+ if (*buffer==0) {
+ wprintw (command_win,"Error - Data not specified\n");refresh_command_win ();return;
+ }
+
+ if (mode==HEX) {
+ do {
+ tmp=(unsigned char) strtol (buffer,NULL,16);
+ file_info.buffer [file_info.offset_in_block]=tmp;
+ file_info.offset_in_block++;
+ ptr=parse_word (ptr,buffer);
+ if (file_info.offset_in_block==file_system_info.block_size) {
+ if (*ptr) {
+ wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
+ refresh_command_win ();
+ }
+ file_info.offset_in_block--;
+ }
+ } while (*buffer) ;
+ }
+
+ else {
+ ch_ptr=buffer;
+ while (*ch_ptr) {
+ tmp=(unsigned char) *ch_ptr++;
+ file_info.buffer [file_info.offset_in_block]=tmp;
+ file_info.offset_in_block++;
+ if (file_info.offset_in_block==file_system_info.block_size) {
+ if (*ch_ptr) {
+ wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
+ refresh_command_win ();
+ }
+ file_info.offset_in_block--;
+ }
+ }
+ }
+
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_file___writedata (char *command_line)
+
+{
+ low_write (file_info.buffer,file_system_info.block_size,file_info.global_block_offset);
+ return;
+}
+
+long file_block_to_global_block (long file_block,struct struct_file_info *file_info_ptr)
+
+{
+ long last_direct,last_indirect,last_dindirect;
+
+ last_direct=EXT2_NDIR_BLOCKS-1;
+ last_indirect=last_direct+file_system_info.block_size/4;
+ last_dindirect=last_indirect+(file_system_info.block_size/4)*(file_system_info.block_size/4);
+
+ if (file_block <= last_direct) {
+ file_info_ptr->level=0;
+ return (file_info_ptr->inode_ptr->i_block [file_block]);
+ }
+
+ if (file_block <= last_indirect) {
+ file_info_ptr->level=1;
+ file_block=file_block-last_direct-1;
+ return (return_indirect (file_info_ptr->inode_ptr->i_block [EXT2_IND_BLOCK],file_block));
+ }
+
+ if (file_block <= last_dindirect) {
+ file_info_ptr->level=2;
+ file_block=file_block-last_indirect-1;
+ return (return_dindirect (file_info_ptr->inode_ptr->i_block [EXT2_DIND_BLOCK],file_block));
+ }
+
+ file_info_ptr->level=3;
+ file_block=file_block-last_dindirect-1;
+ return (return_tindirect (file_info_ptr->inode_ptr->i_block [EXT2_TIND_BLOCK],file_block));
+}
+
+long return_indirect (long table_block,long block_num)
+
+{
+ long block_table [EXT2_MAX_BLOCK_SIZE/4];
+
+ low_read ((char *) block_table,file_system_info.block_size,table_block*file_system_info.block_size);
+ return (block_table [block_num]);
+}
+
+long return_dindirect (long table_block,long block_num)
+
+{
+ long f_indirect;
+
+ f_indirect=block_num/(file_system_info.block_size/4);
+ f_indirect=return_indirect (table_block,f_indirect);
+ return (return_indirect (f_indirect,block_num%(file_system_info.block_size/4)));
+}
+
+long return_tindirect (long table_block,long block_num)
+
+{
+ long s_indirect;
+
+ s_indirect=block_num/((file_system_info.block_size/4)*(file_system_info.block_size/4));
+ s_indirect=return_indirect (table_block,s_indirect);
+ return (return_dindirect (s_indirect,block_num%((file_system_info.block_size/4)*(file_system_info.block_size/4))));
+}
diff --git a/ext2ed/general_com.c b/ext2ed/general_com.c
new file mode 100644
index 0000000..aa274e3
--- /dev/null
+++ b/ext2ed/general_com.c
@@ -0,0 +1,907 @@
+/*
+
+/usr/src/ext2ed/general_com.c
+
+A part of the extended file system 2 disk editor.
+
+---------------------
+General user commands
+---------------------
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+#include "../version.h"
+
+void help (char *command_line)
+
+{
+ int i,max_line=0;
+ char argument [80],*ptr;
+
+ werase (show_pad);wmove (show_pad,0,0);
+
+ ptr=parse_word (command_line,argument);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,argument);
+ if (*argument!=0) {
+ detailed_help (argument);
+ return;
+ }
+ }
+
+ if (current_type!=NULL) {
+
+ wprintw (show_pad,"Type %s specific commands:\n",current_type->name);max_line++;
+
+ if (current_type->type_commands.last_command==-1) {
+ wprintw (show_pad,"\nnone\n");max_line+=2;
+ }
+ else
+ for (i=0;i<=current_type->type_commands.last_command;i++) {
+ if (i%5==0) {
+ wprintw (show_pad,"\n");max_line++;
+ }
+ wprintw (show_pad,"%-13s",current_type->type_commands.names [i]);
+ if (i%5!=4)
+ wprintw (show_pad,"; ");
+ }
+
+ wprintw (show_pad,"\n\n");max_line+=2;
+ }
+
+ if (ext2_commands.last_command != -1) {
+ wprintw (show_pad,"ext2 filesystem general commands: \n");max_line++;
+ for (i=0;i<=ext2_commands.last_command;i++) {
+ if (i%5==0) {
+ wprintw (show_pad,"\n");max_line++;
+ }
+ wprintw (show_pad,"%-13s",ext2_commands.names [i]);
+ if (i%5!=4)
+ wprintw (show_pad,"; ");
+
+ }
+ wprintw (show_pad,"\n\n");max_line+=2;
+ }
+
+ wprintw (show_pad,"General commands: \n");
+
+ for (i=0;i<=general_commands.last_command;i++) {
+ if (i%5==0) {
+ wprintw (show_pad,"\n");max_line++;
+ }
+ wprintw (show_pad,"%-13s",general_commands.names [i]);
+ if (i%5!=4)
+ wprintw (show_pad,"; ");
+ }
+
+ wprintw (show_pad,"\n\n");max_line+=2;
+
+ wprintw (show_pad,"EXT2ED ver %s (%s)\n",E2FSPROGS_VERSION, E2FSPROGS_DATE);
+ wprintw (show_pad,"Copyright (C) 1995 Gadi Oxman\n");
+ wprintw (show_pad,"Reviewed 2001 Christian Bac\n");
+ wprintw (show_pad,"Modified and enhanced by Theodore Ts'o, 2002\n");
+ wprintw (show_pad,"EXT2ED is hereby placed under the terms of the GNU General Public License.\n\n");
+ wprintw (show_pad,"EXT2ED was programmed as a student project in the software laboratory\n");
+ wprintw (show_pad,"of the faculty of electrical engineering in the\n");
+ wprintw (show_pad,"Technion - Israel Institute of Technology\n");
+ wprintw (show_pad,"with the guide of Avner Lottem and Dr. Ilana David.\n");
+
+ max_line+=10;
+
+ show_pad_info.line=0;show_pad_info.max_line=max_line;
+
+ werase (show_win);wmove (show_win,0,0);
+ wprintw (show_win,"EXT2ED help");
+
+ refresh_show_win ();
+ refresh_show_pad ();
+}
+
+void detailed_help (char *text)
+
+{
+ int i;
+
+ if (current_type != NULL)
+ for (i=0;i<=current_type->type_commands.last_command;i++) {
+ if (strcmp (current_type->type_commands.names [i],text)==0) {
+ wprintw (show_pad,"%s - %s\n",text,current_type->type_commands.descriptions [i]);
+ refresh_show_pad ();return;
+ }
+ }
+
+ for (i=0;i<=ext2_commands.last_command;i++) {
+ if (strcmp (ext2_commands.names [i],text)==0) {
+ wprintw (show_pad,"%s - %s\n",text,ext2_commands.descriptions [i]);
+ refresh_show_pad ();return;
+ }
+ }
+
+ for (i=0;i<=general_commands.last_command;i++) {
+ if (strcmp (general_commands.names [i],text)==0) {
+ wprintw (show_pad,"%s - %s\n",text,general_commands.descriptions [i]);
+ refresh_show_pad ();return;
+ }
+ }
+
+ if (strcmp ("quit",text)==0) {
+ wprintw (show_pad,"quit - Exists EXT2ED");
+ refresh_show_pad ();return;
+ }
+
+ wprintw (show_pad,"Error - Command %s not available now\n",text);
+ refresh_show_pad ();return;
+}
+
+
+
+void set_device (char *command_line)
+
+{
+ char *ptr,new_device [80];
+
+ ptr=parse_word (command_line,new_device);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Device name not specified\n");
+ refresh_command_win ();return;
+ }
+ parse_word (ptr,new_device);
+ check_mounted (new_device);
+ if (mounted && !AllowMountedRead) {
+ wprintw (command_win,"Error - Filesystem is mounted, aborting\n");
+ wprintw (command_win,"You may wish to use the AllowMountedRead on configuration option\n");
+ refresh_command_win ();return;
+ }
+
+ if (mounted && AllowMountedRead) {
+ wprintw (command_win,"Warning - Filesystem is mounted. Displayed data may be unreliable.\n");
+ refresh_command_win ();
+ }
+
+ if (device_handle!=NULL)
+ fclose (device_handle);
+
+ if ( (device_handle=fopen (new_device,"rb"))==NULL) {
+ wprintw (command_win,"Error - Can not open device %s\n",new_device);refresh_command_win ();
+ return;
+ }
+ else {
+ strcpy (device_name,new_device);
+ write_access=0; /* Write access disabled */
+ current_type=NULL; /* There is no type now */
+ remember_lifo.entries_count=0; /* Empty Object memory */
+ free_user_commands (&ext2_commands); /* Free filesystem specific objects */
+ free_struct_descriptors ();
+ if (!set_file_system_info ()) { /* Error while getting info --> abort */
+ free_user_commands (&ext2_commands);
+ free_struct_descriptors ();
+ fclose (device_handle);
+ device_handle=NULL; /* Notice that our device is still not set up */
+ device_offset=-1;
+ return;
+ }
+ if (*AlternateDescriptors) /* Check if user defined objects exist */
+ set_struct_descriptors (AlternateDescriptors);
+ dispatch ("setoffset 0");
+ dispatch ("help"); /* Show help screen */
+ wprintw (command_win,"Device changed to %s",device_name);refresh_command_win ();
+ }
+}
+
+void set_offset (char *command_line)
+
+{
+ long mult=1;
+ long new_offset;
+ char *ptr,new_offset_buffer [80];
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (command_line,new_offset_buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (ptr,new_offset_buffer);
+
+ if (strcmp (new_offset_buffer,"block")==0) {
+ mult=file_system_info.block_size;
+ ptr=parse_word (ptr,new_offset_buffer);
+ }
+
+ if (strcmp (new_offset_buffer,"type")==0) {
+ if (current_type==NULL) {
+ wprintw (command_win,"Error - No type set\n");refresh_command_win ();
+ return;
+ }
+
+ mult=current_type->length;
+ ptr=parse_word (ptr,new_offset_buffer);
+ }
+
+ if (*new_offset_buffer==0) {
+ wprintw (command_win,"Error - No offset specified\n");refresh_command_win ();
+ return;
+ }
+
+ if (new_offset_buffer [0]=='+') {
+ if (device_offset==-1) {
+ wprintw (command_win,"Error - Select a fixed offset first\n");refresh_command_win ();
+ return;
+ }
+ new_offset=device_offset+atol (new_offset_buffer+1)*mult;
+ }
+
+ else if (new_offset_buffer [0]=='-') {
+ if (device_offset==-1) {
+ wprintw (command_win,"Error - Select a fixed offset first\n");refresh_command_win ();
+ return;
+ }
+ new_offset=device_offset-atol (new_offset_buffer+1)*mult;
+ if (new_offset<0) new_offset=0;
+ }
+
+ else
+ new_offset=atol (new_offset_buffer)*mult;
+
+ if ( (fseek (device_handle,new_offset,SEEK_SET))==-1) {
+ wprintw (command_win,"Error - Failed to seek to offset %ld in device %s\n",new_offset,device_name);
+ refresh_command_win ();
+ return;
+ };
+ device_offset=new_offset;
+ wprintw (command_win,"Device offset changed to %ld\n",device_offset);refresh_command_win ();
+ load_type_data ();
+ type_data.offset_in_block=0;
+}
+
+void set_int(short len, void *ptr, char *name, char *value)
+{
+ char *char_ptr;
+ short *short_ptr;
+ long *long_ptr;
+ long v;
+ char *tmp;
+
+ v = strtol(value, &tmp, 0);
+ if (*tmp) {
+ wprintw( command_win, "Bad value - %s\n", value);
+ return;
+ }
+ switch (len) {
+ case 1:
+ char_ptr = (char *) ptr;
+ *char_ptr = v;
+ break;
+ case 2:
+ short_ptr = (short *) ptr;
+ *short_ptr = v;
+ break;
+ case 4:
+ long_ptr = (long *) ptr;
+ *long_ptr = v;
+ break;
+ default:
+ wprintw (command_win,
+ "set_int: unsupported length: %d\n", len);
+ return;
+ }
+ wprintw (command_win, "Variable %s set to %s\n",
+ name, value);
+}
+
+void set_uint(short len, void *ptr, char *name, char *value)
+{
+ unsigned char *char_ptr;
+ unsigned short *short_ptr;
+ unsigned long *long_ptr;
+ unsigned long v;
+ char *tmp;
+
+ v = strtoul(value, &tmp, 0);
+ if (*tmp) {
+ wprintw( command_win, "Bad value - %s\n", value);
+ return;
+ }
+ switch (len) {
+ case 1:
+ char_ptr = (unsigned char *) ptr;
+ *char_ptr = v;
+ break;
+ case 2:
+ short_ptr = (unsigned short *) ptr;
+ *short_ptr = v;
+ break;
+ case 4:
+ long_ptr = (unsigned long *) ptr;
+ *long_ptr = v;
+ break;
+ default:
+ wprintw (command_win,
+ "set_uint: unsupported length: %d\n", len);
+ return;
+ }
+ wprintw (command_win, "Variable %s set to %s\n",
+ name, value);
+}
+
+void set_char(short len, void *ptr, char *name, char *value)
+{
+ if (strlen(value)+1 > len) {
+ wprintw( command_win, "Value %s too big for field\n",
+ name, len);
+ return;
+ }
+ memset(ptr, 0, len);
+ strcpy((char *) ptr, value);
+ wprintw (command_win, "Variable %s set to %s\n",
+ name, value);
+}
+
+
+void set (char *command_line)
+
+{
+ unsigned short *int_ptr;
+ unsigned char *char_ptr;
+ unsigned long *long_ptr,offset=0;
+ int i,len, found=0;
+ char *ptr,buffer [80],variable [80],value [80];
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ if (current_type==NULL) {
+ hex_set (command_line);
+ return;
+ }
+
+ ptr=parse_word (command_line,buffer);
+ if (ptr==NULL || *ptr==0) {
+ wprintw (command_win,"Error - Missing arguments\n");refresh_command_win ();
+ return;
+ }
+ parse_word (ptr,buffer);
+ ptr=strchr (buffer,'=');
+ if (ptr==NULL) {
+ wprintw (command_win,"Error - Bad syntax\n");refresh_command_win ();return;
+ }
+ strncpy (variable,buffer,ptr-buffer);variable [ptr-buffer]=0;
+ strcpy (value,++ptr);
+
+ if (current_type==NULL) {
+ wprintw (command_win,"Sorry, not yet supported\n");refresh_command_win ();return;
+ }
+
+ for (i=0;i<current_type->fields_num && !found;i++) {
+ if (strcmp (current_type->field_names [i],variable)==0) {
+ found=1;
+ ptr=type_data.u.buffer+offset;
+ len = current_type->field_lengths [i];
+ switch (current_type->field_types [i]) {
+ case FIELD_TYPE_INT:
+ set_int(len, ptr, variable, value);
+ break;
+ case FIELD_TYPE_UINT:
+ set_uint(len, ptr, variable, value);
+ break;
+ case FIELD_TYPE_CHAR:
+ set_char(len, ptr, variable, value);
+ break;
+ default:
+ wprintw (command_win,
+ "set: unhandled type %d\n",
+ current_type->field_types [i]);
+ break;
+ }
+ refresh_command_win ();
+ }
+ offset+=current_type->field_lengths [i];
+ }
+ if (found)
+ dispatch ("show");
+ else {
+ wprintw (command_win,"Error - Variable %s not found\n",variable);
+ refresh_command_win ();
+ }
+}
+
+void hex_set (char *command_line)
+
+{
+ unsigned char tmp;
+ char *ptr,buffer [80],*ch_ptr;
+ int mode=HEX;
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+ if (strcasecmp (buffer,"text")==0) {
+ mode=TEXT;
+ strcpy (buffer,ptr);
+ }
+
+ else if (strcasecmp (buffer,"hex")==0) {
+ mode=HEX;
+ ptr=parse_word (ptr,buffer);
+ }
+
+ if (*buffer==0) {
+ wprintw (command_win,"Error - Data not specified\n");refresh_command_win ();return;
+ }
+
+ if (mode==HEX) {
+ do {
+ tmp=(unsigned char) strtol (buffer,NULL,16);
+ type_data.u.buffer [type_data.offset_in_block]=tmp;
+ type_data.offset_in_block++;
+ ptr=parse_word (ptr,buffer);
+ if (type_data.offset_in_block==file_system_info.block_size) {
+ if (*ptr) {
+ wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
+ refresh_command_win ();
+ }
+ type_data.offset_in_block--;
+ }
+ } while (*buffer) ;
+ }
+
+ else {
+ ch_ptr=buffer;
+ while (*ch_ptr) {
+ tmp=(unsigned char) *ch_ptr++;
+ type_data.u.buffer [type_data.offset_in_block]=tmp;
+ type_data.offset_in_block++;
+ if (type_data.offset_in_block==file_system_info.block_size) {
+ if (*ch_ptr) {
+ wprintw (command_win,"Error - Ending offset outside block, only partial string changed\n");
+ refresh_command_win ();
+ }
+ type_data.offset_in_block--;
+ }
+ }
+ }
+
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+
+
+void set_type (char *command_line)
+
+{
+ struct struct_descriptor *descriptor_ptr;
+ char *ptr,buffer [80],tmp_buffer [80];
+ short found=0;
+
+ if (!load_type_data ())
+ return;
+
+ ptr=parse_word (command_line,buffer);
+ parse_word (ptr,buffer);
+
+ if (strcmp (buffer,"none")==0 || strcmp (buffer,"hex")==0) {
+ wprintw (command_win,"Data will be shown as hex dump\n");refresh_command_win ();
+ current_type=NULL;
+ sprintf (tmp_buffer,"show");dispatch (tmp_buffer);
+ return;
+ }
+
+ descriptor_ptr=first_type;
+ while (descriptor_ptr!=NULL && !found) {
+ if (strcmp (descriptor_ptr->name,buffer)==0)
+ found=1;
+ else
+ descriptor_ptr=descriptor_ptr->next;
+ }
+ if (found) {
+ wprintw (command_win,"Structure type set to %s\n",buffer);refresh_command_win ();
+ current_type=descriptor_ptr;
+ sprintf (tmp_buffer,"show");dispatch (tmp_buffer);
+ }
+ else {
+ wprintw (command_win,"Error - %s is not a valid type\n",buffer);refresh_command_win ();
+ }
+}
+
+void show_int(short len, void *ptr)
+{
+ long temp;
+ char *format;
+
+ switch (len) {
+ case 1:
+ temp = *((char *) ptr);
+ format = "%3d (0x%02x)\n";
+ break;
+ case 2:
+ temp = *((short *) ptr);
+ format = "%d (0x%x)\n";
+ break;
+ case 4:
+ temp = *((long *) ptr);
+ format = "%d\n";
+ break;
+ default:
+ wprintw (show_pad, "unimplemented\n");
+ return;
+ }
+ wprintw(show_pad, format, temp, temp);
+}
+
+void show_uint(short len, void *ptr)
+{
+ unsigned long temp;
+ char *format;
+
+ switch (len) {
+ case 1:
+ temp = *((unsigned char *) ptr);
+ temp = temp & 0xFF;
+ format = "%3u (0x%02x)\n";
+ break;
+ case 2:
+ temp = *((unsigned short *) ptr);
+ temp = temp & 0xFFFF;
+ format = "%u (0x%x)\n";
+ break;
+ case 4:
+ temp = (unsigned long) *((unsigned long *) ptr);
+ format = "%u\n";
+ break;
+ default:
+ wprintw (show_pad, "unimplemented\n");
+ return;
+ }
+ wprintw(show_pad, format, temp, temp);
+}
+
+void show_char(short len, void *ptr)
+{
+ unsigned char *cp = (unsigned char *) ptr;
+ unsigned char ch;
+ int i,j;
+
+ wprintw(show_pad, "\"");
+
+ for (i=0; i < len; i++) {
+ ch = *cp++;
+ if (ch == 0) {
+ for (j=i+1; j < len; j++)
+ if (cp[j-i])
+ break;
+ if (j == len)
+ break;
+ }
+ if (ch > 128) {
+ wprintw(show_pad, "M-");
+ ch -= 128;
+ }
+ if ((ch < 32) || (ch == 0x7f)) {
+ wprintw(show_pad, "^");
+ ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
+ }
+ wprintw(show_pad, "%c", ch);
+ }
+
+ wprintw(show_pad, "\"\n");
+}
+
+
+
+void show (char *command_line)
+
+{
+ unsigned int i,l,len,temp_int;
+ unsigned long offset=0,temp_long;
+ unsigned char temp_char,*ch_ptr;
+ void *ptr;
+
+ if (device_handle==NULL)
+ return;
+
+ show_pad_info.line=0;
+
+ if (current_type==NULL) {
+ wmove (show_pad,0,0);
+ ch_ptr=type_data.u.buffer;
+ for (l=0;l<file_system_info.block_size/16;l++) {
+ wprintw (show_pad,"%08ld : ",offset);
+ for (i=0;i<16;i++) {
+ if (type_data.offset_in_block==offset+i)
+ wattrset (show_pad,A_REVERSE);
+
+ if (ch_ptr [i]>=' ' && ch_ptr [i]<='z')
+ wprintw (show_pad,"%c",ch_ptr [i]);
+ else
+ wprintw (show_pad,".");
+ if (type_data.offset_in_block==offset+i)
+ wattrset (show_pad,A_NORMAL);
+ }
+ wprintw (show_pad," ");
+ for (i=0;i<16;i++) {
+ if (type_data.offset_in_block==offset+i)
+ wattrset (show_pad,A_REVERSE);
+
+ wprintw (show_pad,"%02x",ch_ptr [i]);
+
+ if (type_data.offset_in_block==offset+i) {
+ wattrset (show_pad,A_NORMAL);
+ show_pad_info.line=l-l % show_pad_info.display_lines;
+ }
+
+ wprintw (show_pad," ");
+ }
+ wprintw (show_pad,"\n");
+ offset+=16;
+ ch_ptr+=16;
+ }
+ show_pad_info.max_line=l-1;show_pad_info.max_col=COLS-1;
+ refresh_show_pad ();show_info ();
+ }
+ else {
+ wmove (show_pad,0,0);l=0;
+ for (i=0;i<current_type->fields_num;i++) {
+ wprintw (show_pad,"%-20s = ",current_type->field_names [i]);
+ ptr=type_data.u.buffer+offset;
+ len = current_type->field_lengths[i];
+ switch (current_type->field_types[i]) {
+ case FIELD_TYPE_INT:
+ show_int(len, ptr);
+ break;
+ case FIELD_TYPE_UINT:
+ show_uint(len, ptr);
+ break;
+ case FIELD_TYPE_CHAR:
+ show_char(len, ptr);
+ break;
+ default:
+ wprintw (show_pad, "unimplemented\n");
+ break;
+ }
+ offset+=len;
+ l++;
+ }
+ current_type->length=offset;
+ show_pad_info.max_line=l-1;
+ refresh_show_pad ();show_info ();
+ }
+}
+
+void next (char *command_line)
+
+{
+ long offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ if (current_type!=NULL) {
+ sprintf (buffer,"setoffset type +%ld",offset);
+ dispatch (buffer);
+ return;
+ }
+
+ if (type_data.offset_in_block+offset < file_system_info.block_size) {
+ type_data.offset_in_block+=offset;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
+ }
+}
+
+void prev (char *command_line)
+
+{
+ long offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ offset*=atol (buffer);
+ }
+
+ if (current_type!=NULL) {
+ sprintf (buffer,"setoffset type -%ld",offset);
+ dispatch (buffer);
+ return;
+ }
+
+ if (type_data.offset_in_block-offset >= 0) {
+ type_data.offset_in_block-=offset;
+ sprintf (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Offset out of block\n");refresh_command_win ();
+ }
+}
+
+void pgdn (char *commnad_line)
+
+{
+ show_pad_info.line+=show_pad_info.display_lines;
+ refresh_show_pad ();refresh_show_win ();
+}
+
+void pgup (char *command_line)
+
+{
+ show_pad_info.line-=show_pad_info.display_lines;
+ refresh_show_pad ();refresh_show_win ();
+}
+
+void redraw (char *command_line)
+
+{
+ redraw_all ();
+ dispatch ("show");
+}
+
+void remember (char *command_line)
+
+{
+ long entry_num;
+ char *ptr,buffer [80];
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+ entry_num=remember_lifo.entries_count++;
+ if (entry_num>REMEMBER_COUNT-1) {
+ entry_num=0;
+ remember_lifo.entries_count--;
+ }
+
+ remember_lifo.offset [entry_num]=device_offset;
+ remember_lifo.type [entry_num]=current_type;
+ strcpy (remember_lifo.name [entry_num],buffer);
+
+ if (current_type!=NULL)
+ wprintw (command_win,"Object %s in Offset %ld remembered as %s\n",current_type->name,device_offset,buffer);
+ else
+ wprintw (command_win,"Offset %ld remembered as %s\n",device_offset,buffer);
+
+ refresh_command_win ();
+}
+
+void recall (char *command_line)
+
+{
+ char *ptr,buffer [80];
+ long entry_num;
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr==0) {
+ wprintw (command_win,"Error - Argument not specified\n");refresh_command_win ();
+ return;
+ }
+
+ ptr=parse_word (ptr,buffer);
+
+
+ for (entry_num=remember_lifo.entries_count-1;entry_num>=0;entry_num--) {
+ if (strcmp (remember_lifo.name [entry_num],buffer)==0)
+ break;
+ }
+
+ if (entry_num==-1) {
+ wprintw (command_win,"Error - Can not recall %s\n",buffer);refresh_command_win ();
+ return;
+ }
+
+ sprintf (buffer,"setoffset %ld",remember_lifo.offset [entry_num]);dispatch (buffer);
+ if (remember_lifo.type [entry_num] != NULL) {
+ sprintf (buffer,"settype %s",remember_lifo.type [entry_num]->name);dispatch (buffer);
+ }
+
+ else {
+ sprintf (buffer,"settype none");dispatch (buffer);
+ }
+
+ wprintw (command_win,"Object %s in Offset %ld recalled\n",current_type->name,device_offset);
+ refresh_command_win ();
+}
+
+void enable_write (char *command_line)
+
+{
+ FILE *fp;
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ if (!AllowChanges) {
+ wprintw (command_win,"Sorry, write access is not allowed\n");
+ return;
+ }
+
+ if (mounted) {
+ wprintw (command_win,"Error - Filesystem is mounted\n");
+ return;
+ }
+
+ if ( (fp=fopen (device_name,"r+b"))==NULL) {
+ wprintw (command_win,"Error - Can not open device %s for reading and writing\n",device_name);refresh_command_win ();
+ return;
+ }
+ fclose (device_handle);
+ device_handle=fp;write_access=1;
+ wprintw (command_win,"Write access enabled - Be careful\n");refresh_command_win ();
+}
+
+void disable_write (char *command_line)
+
+{
+ FILE *fp;
+
+ if (device_handle==NULL) {
+ wprintw (command_win,"Error - No device opened\n");refresh_command_win ();
+ return;
+ }
+
+ if ( (fp=fopen (device_name,"rb"))==NULL) {
+ wprintw (command_win,"Error - Can not open device %s\n",device_name);refresh_command_win ();
+ return;
+ }
+
+ fclose (device_handle);
+ device_handle=fp;write_access=0;
+ wprintw (command_win,"Write access disabled\n");refresh_command_win ();
+}
+
+void write_data (char *command_line)
+
+{
+ write_type_data ();
+}
diff --git a/ext2ed/group_com.c b/ext2ed/group_com.c
new file mode 100644
index 0000000..aaed16e
--- /dev/null
+++ b/ext2ed/group_com.c
@@ -0,0 +1,183 @@
+/*
+
+/usr/src/ext2ed/group_com.c
+
+A part of the extended file system 2 disk editor.
+
+General user commands
+
+First written on: April 17 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+void type_ext2_group_desc___next (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",group_info.group_num+entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_group_desc___prev (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",group_info.group_num-entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_group_desc___entry (char *command_line)
+
+{
+ long group_num;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ group_num=atol (buffer);
+
+ if (group_num < 0 || group_num >= file_system_info.groups_count) {
+ wprintw (command_win,"Error - Entry number out of bounds\n");refresh_command_win ();return;
+ }
+
+ device_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ sprintf (buffer,"setoffset %ld",device_offset);dispatch (buffer);
+ strcpy (buffer,"show");dispatch (buffer);
+ group_info.group_num=group_num;
+}
+
+
+void type_ext2_group_desc___gocopy (char *command_line)
+
+{
+ unsigned long copy_num,offset;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ copy_num=atol (buffer);
+
+ offset=file_system_info.first_group_desc_offset+copy_num*file_system_info.super_block.s_blocks_per_group*file_system_info.block_size;
+
+ if (offset > file_system_info.file_system_size) {
+ wprintw (command_win,"Error - Copy number out of bounds\n");refresh_command_win ();return;
+ }
+
+ group_info.copy_num=copy_num;
+ device_offset=offset+group_info.group_num*sizeof (struct ext2_group_desc);
+
+ sprintf (buffer,"setoffset %ld",device_offset);dispatch (buffer);
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+
+void type_ext2_group_desc___show (char *command_line)
+
+{
+ long group_num,temp;
+
+ temp=(device_offset-file_system_info.first_group_desc_offset) % (file_system_info.super_block.s_blocks_per_group*file_system_info.block_size);
+ group_num=temp/sizeof (struct ext2_group_desc);
+
+ show (command_line);
+
+ wmove (show_win,1,0);wprintw (show_win,"\n");wmove (show_win,2,0);
+ wprintw (show_win,"Group %ld of %ld ",group_num,file_system_info.groups_count-1);
+ wprintw (show_win,"in copy %ld ",group_info.copy_num);
+ if (group_info.copy_num==0) wprintw (show_win,"(Main copy)");
+ wprintw (show_win,"\n");refresh_show_win ();
+
+ if (group_num==0) {
+ wprintw (command_win,"Reached first group descriptor\n");
+ wrefresh (command_win);
+ }
+
+ if (group_num==file_system_info.groups_count-1) {
+ wprintw (command_win,"Reached last group descriptor\n");
+ wrefresh (command_win);
+ }
+}
+
+void type_ext2_group_desc___inode (char *command_line)
+
+{
+ long inode_offset;
+ char buffer [80];
+
+ inode_offset=type_data.u.t_ext2_group_desc.bg_inode_table;
+ sprintf (buffer,"setoffset block %ld",inode_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_inode");dispatch (buffer);
+}
+
+void type_ext2_group_desc___blockbitmap (char *command_line)
+
+{
+ long block_bitmap_offset;
+ char buffer [80];
+
+ block_bitmap_info.entry_num=0;
+ block_bitmap_info.group_num=group_info.group_num;
+
+ block_bitmap_offset=type_data.u.t_ext2_group_desc.bg_block_bitmap;
+ sprintf (buffer,"setoffset block %ld",block_bitmap_offset);dispatch (buffer);
+ sprintf (buffer,"settype block_bitmap");dispatch (buffer);
+}
+
+void type_ext2_group_desc___inodebitmap (char *command_line)
+
+{
+ long inode_bitmap_offset;
+ char buffer [80];
+
+ inode_bitmap_info.entry_num=0;
+ inode_bitmap_info.group_num=group_info.group_num;
+
+ inode_bitmap_offset=type_data.u.t_ext2_group_desc.bg_inode_bitmap;
+ sprintf (buffer,"setoffset block %ld",inode_bitmap_offset);dispatch (buffer);
+ sprintf (buffer,"settype inode_bitmap");dispatch (buffer);
+}
+
+void type_ext2_group_desc___setactivecopy (char *command_line)
+
+{
+ struct ext2_group_desc gd;
+
+ gd=type_data.u.t_ext2_group_desc;
+ dispatch ("gocopy 0");
+ type_data.u.t_ext2_group_desc=gd;
+ dispatch ("show");
+}
diff --git a/ext2ed/init.c b/ext2ed/init.c
new file mode 100644
index 0000000..4e58431
--- /dev/null
+++ b/ext2ed/init.c
@@ -0,0 +1,633 @@
+/*
+
+/usr/src/ext2ed/init.c
+
+A part of the extended file system 2 disk editor.
+
+--------------------------------
+Various initialization routines.
+--------------------------------
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_READLINE
+#include <readline.h>
+#endif
+#include <signal.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "ext2ed.h"
+
+char lines_s [80],cols_s [80];
+
+void signal_handler (void);
+
+void prepare_to_close (void)
+
+{
+ close_windows ();
+ if (device_handle!=NULL)
+ fclose (device_handle);
+ free_user_commands (&general_commands);
+ free_user_commands (&ext2_commands);
+ free_struct_descriptors ();
+}
+
+int init (void)
+
+{
+ printf ("Initializing ...\n");
+
+ if (!process_configuration_file ()) {
+ fprintf (stderr,"Error - Unable to complete configuration. Quitting.\n");
+ return (0);
+ };
+
+ general_commands.last_command=-1; /* No commands whatsoever meanwhile */
+ ext2_commands.last_command=-1;
+ add_general_commands (); /* Add the general commands, available always */
+ device_handle=NULL; /* Notice that our device is still not set up */
+ device_offset=-1;
+ current_type=NULL; /* No filesystem specific types yet */
+
+ remember_lifo.entries_count=0; /* Object memory is empty */
+
+ init_windows (); /* Initialize the NCURSES interface */
+ init_readline (); /* Initialize the READLINE interface */
+ init_signals (); /* Initialize the signal handlers */
+ write_access=0; /* Write access disabled */
+
+ strcpy (last_command_line,"help"); /* Show the help screen to the user */
+ dispatch ("help");
+ return (1); /* Success */
+}
+
+void add_general_commands (void)
+
+{
+ add_user_command (&general_commands,"help","EXT2ED help system",help);
+ add_user_command (&general_commands,"set","Changes a variable in the current object",set);
+ add_user_command (&general_commands,"setdevice","Selects the filesystem block device (e.g. /dev/hda1)",set_device);
+ add_user_command (&general_commands,"setoffset","Moves asynchronously in the filesystem",set_offset);
+ add_user_command (&general_commands,"settype","Tells EXT2ED how to interpret the current object",set_type);
+ add_user_command (&general_commands,"show","Displays the current object",show);
+ add_user_command (&general_commands,"pgup","Scrolls data one page up",pgup);
+ add_user_command (&general_commands,"pgdn","Scrolls data one page down",pgdn);
+ add_user_command (&general_commands,"redraw","Redisplay the screen",redraw);
+ add_user_command (&general_commands,"remember","Saves the current position and data information",remember);
+ add_user_command (&general_commands,"recall","Gets back to the saved object position",recall);
+ add_user_command (&general_commands,"enablewrite","Enters Read/Write mode - Allows changing the filesystem",enable_write);
+ add_user_command (&general_commands,"disablewrite","Enters read only mode",disable_write);
+ add_user_command (&general_commands,"writedata","Write data back to disk",write_data);
+ add_user_command (&general_commands,"next","Moves to the next byte in hex mode",next);
+ add_user_command (&general_commands,"prev","Moves to the previous byte in hex mode",prev);
+}
+
+void add_ext2_general_commands (void)
+
+{
+ add_user_command (&ext2_commands,"super","Moves to the superblock of the filesystem",type_ext2___super);
+ add_user_command (&ext2_commands,"group","Moves to the first group descriptor",type_ext2___group);
+ add_user_command (&ext2_commands,"cd","Moves to the directory specified",type_ext2___cd);
+}
+
+int set_struct_descriptors (char *file_name)
+
+{
+ FILE *fp;
+ char current_line [500],current_word [50],*ch;
+ char variable_name [50],variable_type [20];
+ struct struct_descriptor *current_descriptor;
+
+ if ( (fp=fopen (file_name,"rt"))==NULL) {
+ wprintw (command_win,"Error - Failed to open descriptors file %s\n",file_name);
+ refresh_command_win (); return (0);
+ };
+
+ while (!feof (fp)) {
+ fgets (current_line,500,fp);
+ if (feof (fp)) break;
+ ch=parse_word (current_line,current_word);
+ if (strcmp (current_word,"struct")==0) {
+ ch=parse_word (ch,current_word);
+ current_descriptor=add_new_descriptor (current_word);
+
+ while (strchr (current_line,'{')==NULL) {
+ fgets (current_line,500,fp);
+ if (feof (fp)) break;
+ };
+ if (feof (fp)) break;
+
+ fgets (current_line,500,fp);
+
+ while (strchr (current_line,'}')==NULL) {
+ while (strchr (current_line,';')==NULL) {
+ fgets (current_line,500,fp);
+ if (strchr (current_line,'}')!=NULL) break;
+ };
+ if (strchr (current_line,'}') !=NULL) break;
+ ch=parse_word (current_line,variable_type);
+ ch=parse_word (ch,variable_name);
+ while (variable_name [strlen (variable_name)-1]!=';') {
+ strcpy (variable_type,variable_name);
+ ch=parse_word (ch,variable_name);
+ };
+ variable_name [strlen (variable_name)-1]=0;
+ add_new_variable (current_descriptor,variable_type,variable_name);
+ fgets (current_line,500,fp);
+ };
+ };
+ };
+
+ fclose (fp);
+ return (1);
+}
+
+void free_struct_descriptors (void)
+
+{
+ struct struct_descriptor *ptr,*next;
+
+ ptr=first_type;
+ while (ptr!=NULL) {
+ next=ptr->next;
+ free_user_commands (&ptr->type_commands);
+ free (ptr);
+ ptr=next;
+ }
+ first_type=last_type=current_type=NULL;
+}
+
+void free_user_commands (struct struct_commands *ptr)
+
+{
+ int i;
+
+ for (i=0;i<=ptr->last_command;i++) {
+ free (ptr->names [i]);
+ free (ptr->descriptions [i]);
+ }
+
+ ptr->last_command=-1;
+}
+
+struct struct_descriptor *add_new_descriptor (char *name)
+
+{
+ struct struct_descriptor *ptr;
+
+ ptr = malloc (sizeof (struct struct_descriptor));
+ if (ptr == NULL) {
+ printf ("Error - Can not allocate memory - Quitting\n");
+ exit (1);
+ }
+ memset(ptr, 0, sizeof(struct struct_descriptor));
+ ptr->prev = ptr->next = NULL;
+ strcpy (ptr->name,name);
+ ptr->length=0;
+ ptr->fields_num=0;
+ if (first_type==NULL) {
+ first_type = last_type = ptr;
+ } else {
+ ptr->prev = last_type; last_type->next = ptr; last_type=ptr;
+ }
+ ptr->type_commands.last_command=-1;
+ fill_type_commands (ptr);
+ return (ptr);
+}
+
+struct type_table {
+ char *name;
+ int field_type;
+ int len;
+};
+
+struct type_table type_table[] = {
+ { "long", FIELD_TYPE_INT, 4 },
+ { "short", FIELD_TYPE_INT, 2 },
+ { "char", FIELD_TYPE_CHAR, 1 },
+ { "__u32", FIELD_TYPE_UINT, 4 },
+ { "__s32", FIELD_TYPE_INT, 4 },
+ { "__u16", FIELD_TYPE_UINT, 2 },
+ { "__s16", FIELD_TYPE_INT, 2 },
+ { "__u8", FIELD_TYPE_UINT, 1 },
+ { "__s8", FIELD_TYPE_INT, 1 },
+ { 0, 0, 0 }
+};
+
+void add_new_variable (struct struct_descriptor *ptr,char *v_type,char *v_name)
+
+{
+ short len=1;
+ char field_type=FIELD_TYPE_INT;
+ struct type_table *p;
+
+ strcpy (ptr->field_names [ptr->fields_num],v_name);
+ ptr->field_positions [ptr->fields_num]=ptr->length;
+
+ for (p = type_table; p->name; p++) {
+ if (strcmp(v_type, p->name) == 0) {
+ len = p->len;
+ field_type = p->field_type;
+ break;
+ }
+ }
+ if (p->name == 0) {
+ if (strncmp(v_type, "char[", 5) == 0) {
+ len = atoi(v_type+5);
+ field_type = FIELD_TYPE_CHAR;
+ } else {
+ printf("Unknown type %s for field %s\n", v_type, v_name);
+ exit(1);
+ }
+ }
+
+ ptr->field_lengths [ptr->fields_num] = len;
+ ptr->field_types [ptr->fields_num] = field_type;
+
+ ptr->length+=len;
+ ptr->fields_num++;
+}
+
+void fill_type_commands (struct struct_descriptor *ptr)
+
+/*
+
+Set specific type user commands.
+
+*/
+
+{
+
+ if (strcmp ((ptr->name),"file")==0) {
+ add_user_command (&ptr->type_commands,"show","Shows file data",type_file___show);
+ add_user_command (&ptr->type_commands,"inode","Returns to the inode of the current file",type_file___inode);
+ add_user_command (&ptr->type_commands,"display","Specifies data format - text or hex",type_file___display);
+ add_user_command (&ptr->type_commands,"next","Pass to next byte",type_file___next);
+ add_user_command (&ptr->type_commands,"prev","Pass to the previous byte",type_file___prev);
+ add_user_command (&ptr->type_commands,"offset","Pass to a specified byte in the current block",type_file___offset);
+ add_user_command (&ptr->type_commands,"nextblock","Pass to next file block",type_file___nextblock);
+ add_user_command (&ptr->type_commands,"prevblock","Pass to the previous file block",type_file___prevblock);
+ add_user_command (&ptr->type_commands,"block","Specify which file block to edit",type_file___block);
+ add_user_command (&ptr->type_commands,"remember","Saves the file\'s inode position for later reference",type_file___remember);
+ add_user_command (&ptr->type_commands,"set","Sets the current byte",type_file___set);
+ add_user_command (&ptr->type_commands,"writedata","Writes the current block to the disk",type_file___writedata);
+ }
+
+ if (strcmp ((ptr->name),"ext2_inode")==0) {
+ add_user_command (&ptr->type_commands,"show","Shows inode data",type_ext2_inode___show);
+ add_user_command (&ptr->type_commands,"next","Move to next inode in current block group",type_ext2_inode___next);
+ add_user_command (&ptr->type_commands,"prev","Move to next inode in current block group",type_ext2_inode___prev);
+ add_user_command (&ptr->type_commands,"group","Move to the group descriptors of the current inode table",type_ext2_inode___group);
+ add_user_command (&ptr->type_commands,"entry","Move to a specified entry in the current inode table",type_ext2_inode___entry);
+ add_user_command (&ptr->type_commands,"file","Display file data of the current inode",type_ext2_inode___file);
+ add_user_command (&ptr->type_commands,"dir","Display directory data of the current inode",type_ext2_inode___dir);
+ }
+
+ if (strcmp ((ptr->name),"dir")==0) {
+ add_user_command (&ptr->type_commands,"show","Shows current directory data",type_dir___show);
+ add_user_command (&ptr->type_commands,"inode","Returns to the inode of the current directory",type_dir___inode);
+ add_user_command (&ptr->type_commands,"next","Pass to the next directory entry",type_dir___next);
+ add_user_command (&ptr->type_commands,"prev","Pass to the previous directory entry",type_dir___prev);
+ add_user_command (&ptr->type_commands,"followinode","Follows the inode specified in this directory entry",type_dir___followinode);
+ add_user_command (&ptr->type_commands,"remember","Remember the inode of the current directory entry",type_dir___remember);
+ add_user_command (&ptr->type_commands,"cd","Changes directory relative to the current directory",type_dir___cd);
+ add_user_command (&ptr->type_commands,"entry","Moves to a specified entry in the current directory",type_dir___entry);
+ add_user_command (&ptr->type_commands,"writedata","Writes the current entry to the disk",type_dir___writedata);
+ add_user_command (&ptr->type_commands,"set","Changes a variable in the current directory entry",type_dir___set);
+ }
+
+ if (strcmp ((ptr->name),"ext2_super_block")==0) {
+ add_user_command (&ptr->type_commands,"show","Displays the super block data",type_ext2_super_block___show);
+ add_user_command (&ptr->type_commands,"gocopy","Move to another backup copy of the superblock",type_ext2_super_block___gocopy);
+ add_user_command (&ptr->type_commands,"setactivecopy","Copies the current superblock to the main superblock",type_ext2_super_block___setactivecopy);
+ }
+
+ if (strcmp ((ptr->name),"ext2_group_desc")==0) {
+ add_user_command (&ptr->type_commands,"next","Pass to the next block group descriptor",type_ext2_group_desc___next);
+ add_user_command (&ptr->type_commands,"prev","Pass to the previous group descriptor",type_ext2_group_desc___prev);
+ add_user_command (&ptr->type_commands,"entry","Pass to a specific group descriptor",type_ext2_group_desc___entry);
+ add_user_command (&ptr->type_commands,"show","Shows the current group descriptor",type_ext2_group_desc___show);
+ add_user_command (&ptr->type_commands,"inode","Pass to the inode table of the current group block",type_ext2_group_desc___inode);
+ add_user_command (&ptr->type_commands,"gocopy","Move to another backup copy of the group descriptor",type_ext2_group_desc___gocopy);
+ add_user_command (&ptr->type_commands,"blockbitmap","Show the block allocation bitmap of the current group block",type_ext2_group_desc___blockbitmap);
+ add_user_command (&ptr->type_commands,"inodebitmap","Show the inode allocation bitmap of the current group block",type_ext2_group_desc___inodebitmap);
+ add_user_command (&ptr->type_commands,"setactivecopy","Copies the current group descriptor to the main table",type_ext2_super_block___setactivecopy);
+ }
+
+ if (strcmp ((ptr->name),"block_bitmap")==0) {
+ add_user_command (&ptr->type_commands,"show","Displays the block allocation bitmap",type_ext2_block_bitmap___show);
+ add_user_command (&ptr->type_commands,"entry","Moves to a specific bit",type_ext2_block_bitmap___entry);
+ add_user_command (&ptr->type_commands,"next","Moves to the next bit",type_ext2_block_bitmap___next);
+ add_user_command (&ptr->type_commands,"prev","Moves to the previous bit",type_ext2_block_bitmap___prev);
+ add_user_command (&ptr->type_commands,"allocate","Allocates the current block",type_ext2_block_bitmap___allocate);
+ add_user_command (&ptr->type_commands,"deallocate","Deallocates the current block",type_ext2_block_bitmap___deallocate);
+ }
+
+ if (strcmp ((ptr->name),"inode_bitmap")==0) {
+ add_user_command (&ptr->type_commands,"show","Displays the inode allocation bitmap",type_ext2_inode_bitmap___show);
+ add_user_command (&ptr->type_commands,"entry","Moves to a specific bit",type_ext2_inode_bitmap___entry);
+ add_user_command (&ptr->type_commands,"next","Moves to the next bit",type_ext2_inode_bitmap___next);
+ add_user_command (&ptr->type_commands,"prev","Moves to the previous bit",type_ext2_inode_bitmap___prev);
+ add_user_command (&ptr->type_commands,"allocate","Allocates the current inode",type_ext2_inode_bitmap___allocate);
+ add_user_command (&ptr->type_commands,"deallocate","Deallocates the current inode",type_ext2_inode_bitmap___deallocate);
+ }
+
+}
+
+void add_user_command (struct struct_commands *ptr,char *name,char *description,PF callback)
+
+{
+ int num;
+
+ num=ptr->last_command;
+ if (num+1==MAX_COMMANDS_NUM) {
+ printf ("Internal Error - Can't add command %s\n",name);
+ return;
+ }
+
+ ptr->last_command=++num;
+
+ ptr->names [num]=(char *) malloc (strlen (name)+1);
+ strcpy (ptr->names [num],name);
+
+ if (*description!=0) {
+ ptr->descriptions [num]=(char *) malloc (strlen (description)+1);
+ strcpy (ptr->descriptions [num],description);
+ }
+
+ ptr->callback [num]=callback;
+}
+
+int set_file_system_info (void)
+
+{
+ int ext2_detected=0;
+ struct ext2_super_block *sb;
+
+ file_system_info.super_block_offset=1024;
+ file_system_info.file_system_size=DefaultTotalBlocks*DefaultBlockSize;
+
+ low_read ((char *) &file_system_info.super_block,sizeof (struct ext2_super_block),file_system_info.super_block_offset);
+
+ sb=&file_system_info.super_block;
+
+ if (sb->s_magic == EXT2_SUPER_MAGIC)
+ ext2_detected=1;
+
+ if (ext2_detected)
+ wprintw (command_win,"Detected extended 2 file system on device %s\n",device_name);
+ else
+ wprintw (command_win,"Warning - Extended 2 filesystem not detected on device %s\n",device_name);
+
+ if (!ext2_detected && !ForceExt2)
+ wprintw (command_win,"You may wish to use the configuration option ForceExt2 on\n");
+
+ if (ForceExt2 && !ext2_detected)
+ wprintw (command_win,"Forcing extended 2 filesystem\n");
+
+ if (ForceDefault || !ext2_detected)
+ wprintw (command_win,"Forcing default parameters\n");
+
+ refresh_command_win ();
+
+ if (ext2_detected || ForceExt2) {
+ add_ext2_general_commands ();
+ if (!set_struct_descriptors (Ext2Descriptors))
+ return (0);
+ }
+
+ if (!ForceDefault && ext2_detected) {
+
+ file_system_info.block_size=EXT2_MIN_BLOCK_SIZE << sb->s_log_block_size;
+ if (file_system_info.block_size == EXT2_MIN_BLOCK_SIZE)
+ file_system_info.first_group_desc_offset=2*EXT2_MIN_BLOCK_SIZE;
+ else
+ file_system_info.first_group_desc_offset=file_system_info.block_size;
+ file_system_info.groups_count = ext2fs_div64_ceil(ext2fs_blocks_count(sb),
+ sb->s_blocks_per_group);
+
+ file_system_info.inodes_per_block=file_system_info.block_size/sizeof (struct ext2_inode);
+ file_system_info.blocks_per_group=sb->s_inodes_per_group/file_system_info.inodes_per_block;
+ file_system_info.no_blocks_in_group=sb->s_blocks_per_group;
+ file_system_info.file_system_size=(ext2fs_blocks_count(sb)-1)*file_system_info.block_size;
+ }
+
+ else {
+ file_system_info.file_system_size=DefaultTotalBlocks*DefaultBlockSize;
+ file_system_info.block_size=DefaultBlockSize;
+ file_system_info.no_blocks_in_group=DefaultBlocksInGroup;
+ }
+
+ if (file_system_info.file_system_size > 2147483647) {
+ wprintw (command_win,"Sorry, filesystems bigger than 2 GB are currently not supported\n");
+ return (0);
+ }
+ return (1);
+}
+
+void init_readline (void)
+
+{
+#ifdef HAVE_READLINE
+ rl_completion_entry_function=(Function *) complete_command;
+#endif
+}
+
+void init_signals (void)
+
+{
+ signal (SIGWINCH, signal_SIGWINCH_handler); /* Catch SIGWINCH */
+ signal (SIGTERM, signal_SIGTERM_handler);
+ signal (SIGSEGV, signal_SIGSEGV_handler);
+
+}
+
+void signal_SIGWINCH_handler (int sig_num)
+
+{
+ redraw_request=1; /* We will handle it in main.c */
+
+ /* Reset signal handler */
+ signal (SIGWINCH, signal_SIGWINCH_handler);
+
+}
+
+void signal_SIGTERM_handler (int sig_num)
+
+{
+ prepare_to_close ();
+ printf ("Terminated due to signal %d\n",sig_num);
+ exit (1);
+}
+
+void signal_SIGSEGV_handler (int sig_num)
+
+{
+ prepare_to_close ();
+ printf ("Killed by signal %d!\n",sig_num);
+ exit (1);
+}
+
+int process_configuration_file (void)
+
+{
+ char buffer [300];
+ char option [80],value [80];
+ FILE *fp;
+
+ strcpy (buffer, ROOT_SYSCONFDIR);
+ strcat (buffer,"/ext2ed.conf");
+
+ if ((fp=fopen (buffer,"rt"))==NULL) {
+ fprintf (stderr,"Error - Unable to open configuration file %s\n",buffer);
+ return (0);
+ }
+
+ while (get_next_option (fp,option,value)) {
+ if (strcasecmp (option,"Ext2Descriptors")==0) {
+ strcpy (Ext2Descriptors,value);
+ }
+
+ else if (strcasecmp (option,"AlternateDescriptors")==0) {
+ strcpy (AlternateDescriptors,value);
+ }
+
+ else if (strcasecmp (option,"LogFile")==0) {
+ strcpy (LogFile,value);
+ }
+
+ else if (strcasecmp (option,"LogChanges")==0) {
+ if (strcasecmp (value,"on")==0)
+ LogChanges = 1;
+ else if (strcasecmp (value,"off")==0)
+ LogChanges = 0;
+ else {
+ fprintf (stderr,"Error - Illegal value: %s %s\n",option,value);
+ fclose (fp);return (0);
+ }
+ }
+
+ else if (strcasecmp (option,"AllowChanges")==0) {
+ if (strcasecmp (value,"on")==0)
+ AllowChanges = 1;
+ else if (strcasecmp (value,"off")==0)
+ AllowChanges = 0;
+ else {
+ fprintf (stderr,"Error - Illegal value: %s %s\n",option,value);
+ fclose (fp);return (0);
+ }
+ }
+
+ else if (strcasecmp (option,"AllowMountedRead")==0) {
+ if (strcasecmp (value,"on")==0)
+ AllowMountedRead = 1;
+ else if (strcasecmp (value,"off")==0)
+ AllowMountedRead = 0;
+ else {
+ fprintf (stderr,"Error - Illegal value: %s %s\n",option,value);
+ fclose (fp);return (0);
+ }
+ }
+
+ else if (strcasecmp (option,"ForceExt2")==0) {
+ if (strcasecmp (value,"on")==0)
+ ForceExt2 = 1;
+ else if (strcasecmp (value,"off")==0)
+ ForceExt2 = 0;
+ else {
+ fprintf (stderr,"Error - Illegal value: %s %s\n",option,value);
+ fclose (fp);return (0);
+ }
+ }
+
+ else if (strcasecmp (option,"DefaultBlockSize")==0) {
+ DefaultBlockSize = atoi (value);
+ }
+
+ else if (strcasecmp (option,"DefaultTotalBlocks")==0) {
+ DefaultTotalBlocks = strtoul (value,NULL,10);
+ }
+
+ else if (strcasecmp (option,"DefaultBlocksInGroup")==0) {
+ DefaultBlocksInGroup = strtoul (value,NULL,10);
+ }
+
+ else if (strcasecmp (option,"ForceDefault")==0) {
+ if (strcasecmp (value,"on")==0)
+ ForceDefault = 1;
+ else if (strcasecmp (value,"off")==0)
+ ForceDefault = 0;
+ else {
+ fprintf (stderr,"Error - Illegal value: %s %s\n",option,value);
+ fclose (fp);return (0);
+ }
+ }
+
+ else {
+ fprintf (stderr,"Error - Unknown option: %s\n",option);
+ fclose (fp);return (0);
+ }
+ }
+
+ printf ("Configuration completed\n");
+ fclose (fp);
+ return (1);
+}
+
+int get_next_option (FILE *fp,char *option,char *value)
+
+{
+ char *ptr;
+ char buffer [600];
+
+ if (feof (fp)) return (0);
+ do{
+ if (feof (fp)) return (0);
+ fgets (buffer,500,fp);
+ } while (buffer [0]=='#' || buffer [0]=='\n');
+
+ ptr=parse_word (buffer,option);
+ ptr=parse_word (ptr,value);
+ return (1);
+}
+
+void check_mounted (char *name)
+
+{
+ FILE *fp;
+ char *ptr;
+ char current_line [500],current_word [200];
+
+ mounted=0;
+
+ if ( (fp=fopen ("/etc/mtab","rt"))==NULL) {
+ wprintw (command_win,"Error - Failed to open /etc/mtab. Assuming filesystem is mounted.\n");
+ refresh_command_win ();mounted=1;return;
+ };
+
+ while (!feof (fp)) {
+ fgets (current_line,500,fp);
+ if (feof (fp)) break;
+ ptr=parse_word (current_line,current_word);
+ if (strcasecmp (current_word,name)==0) {
+ mounted=1;fclose (fp);return;
+ }
+ };
+
+ fclose (fp);
+
+ return;
+}
diff --git a/ext2ed/inode_com.c b/ext2ed/inode_com.c
new file mode 100644
index 0000000..2d3dd6d
--- /dev/null
+++ b/ext2ed/inode_com.c
@@ -0,0 +1,437 @@
+/*
+
+/usr/src/ext2ed/inode_com.c
+
+A part of the extended file system 2 disk editor.
+
+Commands relevant to ext2_inode type.
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2ed.h"
+
+void type_ext2_inode___prev (char *command_line)
+
+{
+
+ char *ptr,buffer [80];
+
+ long group_num,group_offset,entry_num,block_num,first_entry,last_entry;
+ long inode_num,mult=1;
+ struct ext2_group_desc desc;
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ mult=atol (buffer);
+ }
+
+ block_num=device_offset/file_system_info.block_size;
+
+ group_num=inode_offset_to_group_num (device_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ entry_num=(device_offset-desc.bg_inode_table*file_system_info.block_size)/sizeof (struct ext2_inode);
+
+ first_entry=0;last_entry=file_system_info.super_block.s_inodes_per_group-1;
+ inode_num=0;
+
+ if (entry_num-mult+1>0) {
+ device_offset-=sizeof (struct ext2_inode)*mult;
+ entry_num-=mult;
+
+ sprintf (buffer,"setoffset %ld",device_offset);dispatch (buffer);
+ strcpy (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Entry out of limits\n");refresh_command_win ();
+ }
+
+ if (entry_num==0) {
+ wprintw (command_win,"Reached first inode in current group descriptor\n");
+ refresh_command_win ();
+ }
+}
+
+void type_ext2_inode___next (char *command_line)
+
+{
+
+ char *ptr,buffer [80];
+
+ long group_num,group_offset,entry_num,block_num,first_entry,last_entry;
+ long inode_num,mult=1;
+ struct ext2_group_desc desc;
+
+ ptr=parse_word (command_line,buffer);
+
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ mult=atol (buffer);
+ }
+
+
+ block_num=device_offset/file_system_info.block_size;
+
+ group_num=inode_offset_to_group_num (device_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ entry_num=(device_offset-desc.bg_inode_table*file_system_info.block_size)/sizeof (struct ext2_inode);
+
+ first_entry=0;last_entry=file_system_info.super_block.s_inodes_per_group-1;
+ inode_num=0;
+
+ if (entry_num+mult-1<last_entry) {
+ device_offset+=sizeof (struct ext2_inode)*mult;
+ entry_num+=mult;
+
+ sprintf (buffer,"setoffset %ld",device_offset);dispatch (buffer);
+ strcpy (buffer,"show");dispatch (buffer);
+ }
+
+ else {
+ wprintw (command_win,"Error - Entry out of limits\n");refresh_command_win ();
+ }
+
+ if (entry_num==last_entry) {
+ wprintw (command_win,"Reached last inode in current group descriptor\n");
+ refresh_command_win ();
+ }
+}
+
+
+void type_ext2_inode___show (char *command_line)
+
+{
+ struct ext2_inode *inode_ptr;
+
+ unsigned short temp;
+ int i;
+
+ long group_num,group_offset,entry_num,block_num,first_entry,last_entry,inode_num;
+ struct ext2_group_desc desc;
+
+ block_num=device_offset/file_system_info.block_size;
+
+ group_num=inode_offset_to_group_num (device_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ entry_num=(device_offset-desc.bg_inode_table*file_system_info.block_size)/sizeof (struct ext2_inode);
+ first_entry=0;last_entry=file_system_info.super_block.s_inodes_per_group-1;
+ inode_num=group_num*file_system_info.super_block.s_inodes_per_group+1;
+ inode_num+=entry_num;
+
+ inode_ptr=&type_data.u.t_ext2_inode;
+
+ show (command_line);
+
+ wmove (show_pad,0,40);wprintw (show_pad,"octal = %06o ",inode_ptr->i_mode);
+ for (i=6;i>=0;i-=3) {
+ temp=inode_ptr->i_mode & 0x1ff;
+ temp=temp >> i;
+ if (temp & 4)
+ wprintw (show_pad,"r");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & 2)
+ wprintw (show_pad,"w");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & 1)
+ wprintw (show_pad,"x");
+ else
+ wprintw (show_pad,"-");
+ }
+ wmove (show_pad,3,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_inode.i_atime));
+ wmove (show_pad,4,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_inode.i_ctime));
+ wmove (show_pad,5,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_inode.i_mtime));
+ wmove (show_pad,6,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_inode.i_dtime));
+
+ wmove (show_pad,10,40);
+ temp=inode_ptr->i_flags;
+
+ if (temp & EXT2_SECRM_FL)
+ wprintw (show_pad,"s");
+ else
+ wprintw (show_pad,"-");
+
+
+ if (temp & EXT2_UNRM_FL)
+ wprintw (show_pad,"u");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & EXT2_COMPR_FL)
+ wprintw (show_pad,"c");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & EXT2_SYNC_FL)
+ wprintw (show_pad,"S");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & EXT2_IMMUTABLE_FL)
+ wprintw (show_pad,"i");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & EXT2_APPEND_FL)
+ wprintw (show_pad,"a");
+ else
+ wprintw (show_pad,"-");
+
+ if (temp & EXT2_NODUMP_FL)
+ wprintw (show_pad,"d");
+ else
+ wprintw (show_pad,"-");
+
+ refresh_show_pad ();
+
+ wmove (show_win,1,0);
+
+ wprintw (show_win,"Inode %ld of %ld. Entry %ld of %ld in group descriptor %ld.\n"
+ ,inode_num,file_system_info.super_block.s_inodes_count,entry_num,last_entry,group_num);
+
+ wprintw (show_win,"Inode type: ");
+
+ if (inode_num < EXT2_GOOD_OLD_FIRST_INO) {
+ switch (inode_num) {
+ case EXT2_BAD_INO:
+ wprintw (show_win,"Bad blocks inode - ");
+ break;
+ case EXT2_ROOT_INO:
+ wprintw (show_win,"Root inode - ");
+ break;
+ case EXT4_USR_QUOTA_INO:
+ wprintw (show_win,"User quota inode - ");
+ break;
+ case EXT4_GRP_QUOTA_INO:
+ wprintw (show_win,"Group quota inode - ");
+ break;
+ case EXT2_BOOT_LOADER_INO:
+ wprintw (show_win,"Boot loader inode - ");
+ break;
+ case EXT2_UNDEL_DIR_INO:
+ wprintw (show_win,"Undelete directory inode - ");
+ break;
+ default:
+ wprintw (show_win,"Reserved inode - ");
+ break;
+ }
+ }
+ if (type_data.u.t_ext2_inode.i_mode==0)
+ wprintw (show_win,"Free. ");
+
+ if (S_ISREG (type_data.u.t_ext2_inode.i_mode))
+ wprintw (show_win,"File. ");
+
+ if (S_ISDIR (type_data.u.t_ext2_inode.i_mode))
+ wprintw (show_win,"Directory. ");
+
+ if (S_ISLNK (type_data.u.t_ext2_inode.i_mode)) {
+ wprintw (show_win,"Symbolic link. ");
+ wmove (show_pad,12,40);
+
+ if (inode_ptr->i_size <= 60)
+ wprintw (show_pad,"-> %s",(char *) &type_data.u.t_ext2_inode.i_block [0]);
+ else
+ wprintw (show_pad,"Slow symbolic link\n");
+ refresh_show_pad ();
+ }
+
+ if (S_ISCHR (type_data.u.t_ext2_inode.i_mode))
+ wprintw (show_win,"Character device.");
+
+ if (S_ISBLK (type_data.u.t_ext2_inode.i_mode))
+ wprintw (show_win,"Block device. ");
+
+ wprintw (show_win,"\n");refresh_show_win ();
+
+ if (entry_num==last_entry) {
+ wprintw (command_win,"Reached last inode in current group descriptor\n");
+ refresh_command_win ();
+ }
+
+ if (entry_num==first_entry) {
+ wprintw (command_win,"Reached first inode in current group descriptor\n");
+ refresh_command_win ();
+ }
+
+}
+
+void type_ext2_inode___entry (char *command_line)
+
+{
+ char *ptr,buffer [80];
+
+ long group_num,group_offset,entry_num,block_num,wanted_entry;
+ struct ext2_group_desc desc;
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) return;
+ ptr=parse_word (ptr,buffer);
+ wanted_entry=atol (buffer);
+
+ block_num=device_offset/file_system_info.block_size;
+
+ group_num=inode_offset_to_group_num (device_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ entry_num=(device_offset-desc.bg_inode_table*file_system_info.block_size)/sizeof (struct ext2_inode);
+
+ if (wanted_entry > entry_num) {
+ sprintf (buffer,"next %ld",wanted_entry-entry_num);
+ dispatch (buffer);
+ }
+
+ else if (wanted_entry < entry_num) {
+ sprintf (buffer,"prev %ld",entry_num-wanted_entry);
+ dispatch (buffer);
+ }
+}
+
+void type_ext2_inode___group (char *command_line)
+
+{
+ char buffer [80];
+
+ long group_num,group_offset;
+
+ group_num=inode_offset_to_group_num (device_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ sprintf (buffer,"setoffset %ld",group_offset);dispatch (buffer);
+ sprintf (buffer,"settype ext2_group_desc");dispatch (buffer);
+}
+
+void type_ext2_inode___file (char *command_line)
+
+{
+ char buffer [80];
+
+ if (!S_ISREG (type_data.u.t_ext2_inode.i_mode)) {
+ wprintw (command_win,"Error - Inode type is not file\n");refresh_command_win ();
+ return;
+ }
+
+ if (!init_file_info ()) {
+ wprintw (command_win,"Error - Unable to show file\n");refresh_command_win ();
+ return;
+ }
+
+ sprintf (buffer,"settype file");dispatch (buffer);
+}
+
+void type_ext2_inode___dir (char *command_line)
+
+{
+ char buffer [80];
+
+ if (!S_ISDIR (type_data.u.t_ext2_inode.i_mode)) {
+ wprintw (command_win,"Error - Inode type is not directory\n");refresh_command_win ();
+ return;
+ }
+
+/* It is very important to init first_file_info first, as search_dir_entries relies on it */
+
+ if (!init_dir_info (&first_file_info)) {
+ wprintw (command_win,"Error - Unable to show directory\n");refresh_command_win ();
+ return;
+ }
+
+ file_info=first_file_info;
+
+ sprintf (buffer,"settype dir");dispatch (buffer);
+}
+
+long inode_offset_to_group_num (long inode_offset)
+
+{
+ int found=0;
+ struct ext2_group_desc desc;
+
+ long block_num,group_offset,group_num;
+
+ block_num=inode_offset/file_system_info.block_size;
+
+ group_offset=file_system_info.first_group_desc_offset;
+ group_num=(group_offset-file_system_info.first_group_desc_offset)/sizeof (struct ext2_group_desc);
+
+ while (!found && group_num>=0 && group_num<file_system_info.groups_count) {
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+ if (block_num>=desc.bg_inode_table && block_num<desc.bg_inode_table+file_system_info.blocks_per_group)
+ found=1;
+ else
+ group_offset+=sizeof (struct ext2_group_desc);
+ group_num=(group_offset-file_system_info.first_group_desc_offset)/sizeof (struct ext2_group_desc);
+ }
+
+ if (!found)
+ return (-1);
+
+ return (group_num);
+}
+
+
+
+long int inode_offset_to_inode_num (long inode_offset)
+
+{
+ long group_num,group_offset,entry_num,block_num,first_entry,last_entry,inode_num;
+ struct ext2_group_desc desc;
+
+ block_num=inode_offset/file_system_info.block_size;
+
+ group_num=inode_offset_to_group_num (inode_offset);
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ entry_num=(inode_offset-desc.bg_inode_table*file_system_info.block_size)/sizeof (struct ext2_inode);
+ first_entry=0;last_entry=file_system_info.super_block.s_inodes_per_group-1;
+ inode_num=group_num*file_system_info.super_block.s_inodes_per_group+1;
+ inode_num+=entry_num;
+
+ return (inode_num);
+}
+
+long int inode_num_to_inode_offset (long inode_num)
+
+{
+ long group_num,group_offset,inode_offset,inode_entry;
+ struct ext2_group_desc desc;
+
+ inode_num--;
+
+ group_num=inode_num/file_system_info.super_block.s_inodes_per_group;
+ inode_entry=inode_num%file_system_info.super_block.s_inodes_per_group;
+ group_offset=file_system_info.first_group_desc_offset+group_num*sizeof (struct ext2_group_desc);
+ low_read ((char *) &desc,sizeof (struct ext2_group_desc),group_offset);
+
+ inode_offset=desc.bg_inode_table*file_system_info.block_size+inode_entry*sizeof (struct ext2_inode);
+
+ return (inode_offset);
+}
diff --git a/ext2ed/inodebitmap_com.c b/ext2ed/inodebitmap_com.c
new file mode 100644
index 0000000..157807a
--- /dev/null
+++ b/ext2ed/inodebitmap_com.c
@@ -0,0 +1,215 @@
+/*
+
+/usr/src/ext2ed/inodebitmap_com.c
+
+A part of the extended file system 2 disk editor.
+
+-------------------------
+Handles the inode bitmap.
+-------------------------
+
+Please refer to the documentation in blockbitmap_com.c - Those two files are almost equal.
+
+First written on: July 25 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ext2ed.h"
+
+
+void type_ext2_inode_bitmap___entry (char *command_line)
+
+{
+ unsigned long entry_num;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ entry_num=atol (buffer);
+
+ if (entry_num >= file_system_info.super_block.s_inodes_per_group) {
+ wprintw (command_win,"Error - Entry number out of bounds\n");refresh_command_win ();return;
+ }
+
+ inode_bitmap_info.entry_num=entry_num;
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_ext2_inode_bitmap___next (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",inode_bitmap_info.entry_num+entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_inode_bitmap___prev (char *command_line)
+
+{
+ long entry_offset=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ entry_offset=atol (buffer);
+ }
+
+ sprintf (buffer,"entry %ld",inode_bitmap_info.entry_num-entry_offset);
+ dispatch (buffer);
+}
+
+void type_ext2_inode_bitmap___allocate (char *command_line)
+
+{
+ long entry_num,num=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ num=atol (buffer);
+ }
+
+ entry_num=inode_bitmap_info.entry_num;
+ if (num > file_system_info.super_block.s_inodes_per_group-entry_num) {
+ wprintw (command_win,"Error - There aren't that much inodes in the group\n");
+ refresh_command_win ();return;
+ }
+
+ while (num) {
+ allocate_inode (entry_num);
+ num--;entry_num++;
+ }
+
+ dispatch ("show");
+}
+
+void type_ext2_inode_bitmap___deallocate (char *command_line)
+
+{
+ long entry_num,num=1;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr!=0) {
+ ptr=parse_word (ptr,buffer);
+ num=atol (buffer);
+ }
+
+ entry_num=inode_bitmap_info.entry_num;
+ if (num > file_system_info.super_block.s_inodes_per_group-entry_num) {
+ wprintw (command_win,"Error - There aren't that much inodes in the group\n");
+ refresh_command_win ();return;
+ }
+
+ while (num) {
+ deallocate_inode (entry_num);
+ num--;entry_num++;
+ }
+
+ dispatch ("show");
+}
+
+
+void allocate_inode (long entry_num)
+
+{
+ unsigned char bit_mask=1;
+ int byte_offset,j;
+
+ byte_offset=entry_num/8;
+ for (j=0;j<entry_num%8;j++)
+ bit_mask*=2;
+ type_data.u.buffer [byte_offset] |= bit_mask;
+}
+
+void deallocate_inode (long entry_num)
+
+{
+ unsigned char bit_mask=1;
+ int byte_offset,j;
+
+ byte_offset=entry_num/8;
+ for (j=0;j<entry_num%8;j++)
+ bit_mask*=2;
+ bit_mask^=0xff;
+
+ type_data.u.buffer [byte_offset] &= bit_mask;
+}
+
+void type_ext2_inode_bitmap___show (char *command_line)
+
+{
+ int i,j;
+ unsigned char *ptr;
+ unsigned long inode_num,entry_num;
+
+ ptr=type_data.u.buffer;
+ show_pad_info.line=0;show_pad_info.max_line=-1;
+
+ wmove (show_pad,0,0);
+ for (i=0,entry_num=0;i<file_system_info.super_block.s_inodes_per_group/8;i++,ptr++) {
+ for (j=1;j<=128;j*=2) {
+ if (entry_num==inode_bitmap_info.entry_num) {
+ wattrset (show_pad,A_REVERSE);
+ show_pad_info.line=show_pad_info.max_line-show_pad_info.display_lines/2;
+ }
+
+ if ((*ptr) & j)
+ wprintw (show_pad,"1");
+ else
+ wprintw (show_pad,"0");
+
+ if (entry_num==inode_bitmap_info.entry_num)
+ wattrset (show_pad,A_NORMAL);
+
+ entry_num++;
+ }
+ wprintw (show_pad," ");
+ if (i%8==7) {
+ wprintw (show_pad,"\n");
+ show_pad_info.max_line++;
+ }
+ }
+
+ if (i%8!=7) {
+ wprintw (show_pad,"\n");
+ show_pad_info.max_line++;
+ }
+
+ refresh_show_pad ();
+ show_info ();
+ wmove (show_win,1,0);wprintw (show_win,"Inode bitmap of block group %ld\n",inode_bitmap_info.group_num);
+
+ inode_num=1+inode_bitmap_info.entry_num+inode_bitmap_info.group_num*file_system_info.super_block.s_inodes_per_group;
+ wprintw (show_win,"Status of inode %ld - ",inode_num);
+ ptr=type_data.u.buffer+inode_bitmap_info.entry_num/8;
+ j=1;
+ for (i=inode_bitmap_info.entry_num % 8;i>0;i--)
+ j*=2;
+ if ((*ptr) & j)
+ wprintw (show_win,"Allocated\n");
+ else
+ wprintw (show_win,"Free\n");
+ refresh_show_win ();
+}
diff --git a/ext2ed/main.c b/ext2ed/main.c
new file mode 100644
index 0000000..9d33a8e
--- /dev/null
+++ b/ext2ed/main.c
@@ -0,0 +1,548 @@
+/*
+
+/usr/src/ext2ed/main.c
+
+A part of the extended file system 2 disk editor.
+
+------------
+Main program
+------------
+
+This file mostly contains:
+
+1. A list of global variables used through the entire program.
+2. The parser, which asks the command line from the user.
+3. The dispatcher, which analyzes the command line and calls the appropriate handler function.
+4. A command pattern matcher which is used along with the readline completion feature.
+5. A function which tells the user that an internal error has occurred.
+
+First written on: March 30 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#ifdef HAVE_READLINE
+#include <readline.h>
+#include <history.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else
+extern int optind;
+extern char *optarg;
+#endif
+
+#include "ext2ed.h"
+
+/* Global variables */
+
+/*
+
+Configuration file options
+
+The following variables will be set by init.c to the values selected in the user configuration file.
+They are initialized below to some logical defaults.
+
+*/
+
+
+char Ext2Descriptors [200]="ext2.descriptors"; /* The location of the ext2 filesystem object definition */
+char AlternateDescriptors [200]=""; /* We allow the user to define additional structures */
+char LogFile [200]="ext2ed.log"; /* The location of the log file - Each write will be logged there */
+int LogChanges=1; /* 1 enables logging, 0 disables logging */
+int AllowChanges=0; /* When set, the enablewrite command will fail */
+int AllowMountedRead=0; /* Behavior when trying to open a mounted filesystem read-only */
+int ForceExt2=0; /* When set, ext2 autodetection is overridden */
+int DefaultBlockSize=1024;
+unsigned long DefaultTotalBlocks=2097151;
+unsigned long DefaultBlocksInGroup=8192; /* The default values are used when an ext2 filesystem is not */
+int ForceDefault=0; /* detected, or ForceDefault is set */
+
+char last_command_line [80]; /* A simple one command cache, in addition to the readline history */
+
+char device_name [80]; /* The location of the filesystem */
+FILE *device_handle=NULL; /* This is passed to the fopen / fread ... commands */
+long device_offset; /* The current position in the filesystem */
+ /* Note that we have a 2 GB limitation */
+
+int mounted=0; /* This is set when we find that the filesystem is mounted */
+
+struct struct_commands general_commands,ext2_commands; /* Used to define the general and ext2 commands */
+struct struct_descriptor *first_type,*last_type,*current_type; /* Used to access the double linked list */
+struct struct_type_data type_data; /* The current data is sometimes stored here */
+struct struct_file_system_info file_system_info; /* Essential information on the filesystem */
+struct struct_file_info file_info,first_file_info; /* Used by file_com.c to access files */
+struct struct_group_info group_info; /* Used by group_com.c */
+struct struct_super_info super_info; /* Used by super_com.c */
+struct struct_remember_lifo remember_lifo; /* A circular memory of objects */
+struct struct_block_bitmap_info block_bitmap_info; /* Used by blockbitmap_com.c */
+struct struct_inode_bitmap_info inode_bitmap_info; /* Used by inodebitmap_com.c */
+
+int redraw_request=0; /* Is set by a signal handler to handle terminal */
+ /* screen size change. */
+
+
+/*
+ * We just call the parser to get commands from the user. We quit when
+ * parser returns.
+ */
+int main (int argc, char **argv)
+{
+ int write_priv = 0;
+ int c;
+ char *buf;
+
+ if (!init ())
+ return (1);
+ while ((c = getopt (argc, argv, "w")) != EOF) {
+ switch (c) {
+ case 'w':
+ write_priv++;
+ break;
+ }
+ }
+ if (optind < argc) {
+ buf = malloc(strlen(argv[optind]) + 32);
+ if (!buf) {
+ fprintf(stderr, "Couldn't allocate filename buffer\n");
+ exit(1);
+ }
+ strcpy(buf, "set_device ");
+ strcat(buf, argv[optind]);
+ set_device(buf);
+ free(buf);
+ if (write_priv) {
+ wprintw (command_win,"\n");
+ enable_write("enable_write");
+ }
+ }
+ parser (); /* Get and parse user commands */
+ prepare_to_close(); /* Do some cleanup */
+ printf("Quitting ...\n");
+ return(0);
+}
+
+
+/*
+ * Read a character from the command window
+ */
+int command_read_key()
+{
+ int key = 0;
+
+ while (!key) {
+ if (redraw_request) {
+ redraw_all();
+ redraw_request=0;
+ }
+ key = wgetch(command_win);
+ switch (key) {
+ case 0x1A:
+ key = 0;
+ kill(getpid(), SIGTSTP);
+ break;
+
+ case KEY_NPAGE:
+ pgdn("");
+ refresh_command_win ();
+ break;
+
+ case KEY_PPAGE:
+ pgup("");
+ refresh_command_win ();
+ break;
+ case ERR:
+ key = 0;
+ break;
+
+ case KEY_BACKSPACE:
+ key = '\b';
+ }
+ if ((key < 32 && key != '\b' && key != '\n') ||
+ (key > 127))
+ key = 0;
+ }
+ return key;
+}
+
+#ifdef HAVE_READLINE
+int rl_getc_replacement(FILE *f)
+{
+ int key = command_read_key();
+
+ if (key == '\b') {
+ if (rl_point > 0)
+ wprintw(command_win, "\b \b");
+ } else
+ wprintw(command_win, "%c", key);
+ return key;
+}
+
+/*
+ * This function asks the user for a command and calls the dispatcher
+ * function, dispatch, to analyze it. We use the readline library
+ * function readline to read the command, hence all the usual readline
+ * keys are available. The new command is saved both in the
+ * readline's history and in our tiny one-command cache, so that only
+ * the enter key is needed to retype it.
+ */
+void parser (void)
+{
+ char *ptr,command_line [80];
+ int quit=0;
+
+#if 0
+ noecho();
+ cbreak();
+ keypad(command_win, 1);
+ wtimeout(command_win, 100);
+
+ rl_getc_function = rl_getc_replacement;
+#endif
+
+ while (!quit) {
+ /* Terminal screen size has changed */
+ if (redraw_request) {
+ redraw_all();
+ redraw_request=0;
+ }
+
+ wmove (command_win,0,0);
+ wclrtoeol (command_win);
+ wprintw (command_win,"ext2ed > ");
+ refresh_command_win ();
+
+ /*
+ * The ncurses library optimizes cursor movement by
+ * keeping track of the cursor position. However, by
+ * using the readline library I'm breaking its
+ * assumptions. The double -1 arguments tell ncurses
+ * to disable cursor movement optimization this
+ * time.
+ */
+ mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0);
+
+ /* echo (); */
+ ptr=readline ("ext2ed > ");
+ /* noecho (); */
+
+ /*
+ * Readline allocated the buffer - Copy the string
+ * and free the allocated buffer
+ * XXX WHY???
+ */
+ strcpy (command_line,ptr);
+ free (ptr);
+
+ if (*command_line != 0)
+ add_history (command_line);
+
+ /* If only enter was pressed, recall the last command */
+ if (*command_line==0)
+ strcpy (command_line,last_command_line);
+
+ /* Emulate readline's actions for ncurses */
+ mvcur (-1,-1,LINES-COMMAND_WIN_LINES,0);
+ werase (command_win);
+ wprintw (command_win,"ext2ed > ");
+ wprintw (command_win,command_line);
+ wprintw (command_win,"\n");
+ refresh_command_win ();
+
+ /* Save this command in our tiny cache */
+ strcpy (last_command_line,command_line);
+
+ /* And call dispatch to do the actual job */
+ quit=dispatch (command_line);
+ }
+}
+#else
+void read_line(char * foo) {
+ char * chptr = foo;
+ int ch;
+ int done = 0;
+
+ while (!done && (ch = command_read_key())) {
+ switch (ch) {
+ case '\n':
+ done = 1;
+ break;
+
+ case '\b':
+ if (chptr > foo) {
+ wprintw(command_win, "\b \b");
+ chptr--;
+ }
+ break;
+
+ default:
+ if (ch > 256)
+ break;
+ if (ch == '\n') break;
+ *chptr++ = ch;
+ wprintw(command_win, "%c", ch);
+ break;
+ }
+ }
+ *chptr = '\0';
+}
+
+void parser (void)
+{
+ char command_line [80];
+ int quit=0;
+
+ noecho();
+ cbreak();
+ wtimeout(command_win, 100);
+ keypad(command_win, 1);
+
+ while (!quit) {
+ /* Terminal screen size has changed */
+ if (redraw_request) {
+ redraw_all();
+ redraw_request=0;
+ }
+
+ wmove (command_win,0,0);wclrtoeol (command_win);
+
+ wmove(command_win, 0, 0);
+ wprintw(command_win, "ext2ed > ");
+ read_line(command_line);
+
+ /* If only enter was pressed, recall the last command */
+ if (*command_line==0)
+ strcpy (command_line,last_command_line);
+
+ mvcur (-1,-1,LINES-COMMAND_WIN_LINES + 1,0);
+
+ strcpy (last_command_line,command_line); /* Save this command in our tiny cache */
+
+ /* And call dispatch to do the actual job */
+ quit=dispatch (command_line);
+ }
+}
+#endif
+
+
+/*
+ * This is a very important function. Its task is to receive a command
+ * name and link it to a C function. There are three types of commands:
+ *
+ * 1. General commands - Always available and accessed through
+ * general_commands.
+ * 2. Ext2 specific commands - Available when editing an ext2
+ * filesystem, accessed through ext2_commands.
+ * 3. Type specific commands - Those are changing according to the
+ * current type. The global variable current_type points to the
+ * current object definition (of type struct_descriptor). In it, the
+ * struct_commands entry contains the type specific commands links.
+ *
+ * Overriding is an important feature - Much like in C++ : The same
+ * command name can dispatch to different functions. The overriding
+ * priority is 3,2,1; That is - A type specific command will always
+ * override a general command. This is used through the program to
+ * allow fine tuned operation.
+ *
+ * When an handling function is found, it is called along with the
+ * command line that was passed to us. The handling function is then
+ * free to interpret the arguments in its own style.
+ */
+int dispatch (char *command_line)
+{
+ int i,found=0;
+
+ char command [80];
+
+ parse_word (command_line,command);
+
+ if (strcasecmp (command,"quit")==0) return (1);
+
+ /* 1. Search for type specific commands FIRST - Allows
+ overriding of a general command */
+
+ if (current_type != NULL)
+ for (i=0;
+ i<=current_type->type_commands.last_command && !found;
+ i++) {
+ if (strcasecmp (command,current_type->type_commands.names [i])==0) {
+ (*current_type->type_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+ /* 2. Now search for ext2 filesystem general commands */
+
+ if (!found)
+ for (i=0;i<=ext2_commands.last_command && !found;i++) {
+ if (strcasecmp (command,ext2_commands.names [i])==0) {
+ (*ext2_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+
+ /* 3. If not found, search the general commands */
+
+ if (!found)
+ for (i=0;i<=general_commands.last_command && !found;i++) {
+ if (strcasecmp (command,general_commands.names [i])==0) {
+ (*general_commands.callback [i]) (command_line);
+ found=1;
+ }
+ }
+
+ /* 4. If not found, issue an error message and return */
+
+ if (!found) {
+ wprintw (command_win,"Error: Unknown command\n");
+ refresh_command_win ();
+ }
+
+ return (0);
+}
+
+
+/*
+ *
+ * This function copies the next word in source to the variable dest,
+ * ignoring whitespaces. It returns a pointer to the next word in
+ * source. It is used to split the command line into command and arguments.
+ */
+char *parse_word (char *source,char *dest)
+{
+ char ch,*source_ptr,*target_ptr;
+
+ if (*source==0) {
+ *dest=0;
+ return (source);
+ };
+
+ source_ptr=source;target_ptr=dest;
+ do {
+ ch=*source_ptr++;
+ } while (! (ch>' ' && ch<='z') && ch!=0);
+
+ while (ch>' ' && ch<='z') {
+ *target_ptr++=ch;
+ ch=*source_ptr++;
+ }
+
+ *target_ptr=0;
+
+ source_ptr--;
+ do {
+ ch=*source_ptr++;
+ } while (! (ch>' ' && ch<='z') && ch!=0);
+
+ return (--source_ptr);
+}
+
+/*
+ * text is the partial command entered by the user; We assume that it
+ * is a part of a command - I didn't write code for smarter completion.
+ *
+ * The state variable is an index which tells us how many possible
+ * completions we already returned to readline.
+ *
+ * We return only one possible completion or (char *) NULL if there
+ * are no more completions. This function will be called by readline
+ * over and over until we tell it to stop.
+ *
+ * While scanning for possible completions, we use the same priority
+ * definition which was used in dispatch.
+ */
+#if HAVE_READLINE
+char *complete_command (char *text,int state)
+{
+ int state_index=-1;
+ int i,len;
+
+ len=strlen (text);
+
+ /* Is the command type specific ? */
+
+ if (current_type != NULL)
+ for (i=0;i<=current_type->type_commands.last_command;i++) {
+ if (strncmp (current_type->type_commands.names [i],text,len)==0) {
+ state_index++;
+ if (state==state_index) {
+ return (dupstr (current_type->type_commands.names [i]));
+ }
+ }
+ }
+
+ /* No, perhaps ext2 specific command then ? */
+
+ for (i=0;i<=ext2_commands.last_command;i++) {
+ if (strncmp (ext2_commands.names [i],text,len)==0) {
+ state_index++;
+ if (state==state_index)
+ return (dupstr (ext2_commands.names [i]));
+ }
+ }
+
+
+ /* Check for a general command */
+
+ for (i=0;i<=general_commands.last_command;i++) {
+ if (strncmp (general_commands.names [i],text,len)==0) {
+ state_index++;
+ if (state==state_index)
+ return (dupstr (general_commands.names [i]));
+ }
+ }
+
+ /* quit is handled differently */
+
+ if (strncmp ("quit",text,len)==0) {
+ state_index++;
+ if (state==state_index)
+ return (dupstr ("quit"));
+ }
+
+ /* No more completions */
+
+ return ((char *) NULL);
+}
+#endif
+
+
+/*
+ * Nothing special - Just allocates enough space and copy the string.
+ */
+char *dupstr (char *src)
+{
+ char *ptr;
+
+ ptr=(char *) malloc (strlen (src)+1);
+ if (!ptr)
+ return NULL;
+ strcpy (ptr,src);
+ return (ptr);
+}
+
+#ifdef DEBUG
+/*
+ * This function reports an internal error. It is almost not used. One
+ * place in which I do check for internal errors is disk.c.
+ *
+ * We just report the error, and try to continue ...
+ */
+void internal_error (char *description,char *source_name,char *function_name)
+{
+ wprintw (command_win,"Internal error - Found by source: %s.c , function: %s\n",source_name,function_name);
+ wprintw (command_win,"\t%s\n",description);
+ wprintw (command_win,"Press enter to (hopefully) continue\n");
+ refresh_command_win ();getch ();werase (command_win);
+}
+
+#endif
diff --git a/ext2ed/super_com.c b/ext2ed/super_com.c
new file mode 100644
index 0000000..a998970
--- /dev/null
+++ b/ext2ed/super_com.c
@@ -0,0 +1,156 @@
+/*
+
+/usr/src/ext2ed/super_com.c
+
+A part of the extended file system 2 disk editor.
+
+----------------------
+Handles the superblock
+----------------------
+
+First written on: April 9 1995
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "ext2ed.h"
+
+void type_ext2_super_block___show (char *command_line)
+
+{
+ struct ext2_super_block *super;
+ super=&type_data.u.t_ext2_super_block;
+
+ show (command_line);
+
+ if (ext2fs_blocks_count(super) != 0) {
+ wmove (show_pad,2,40);wprintw (show_pad,"%2.2f%%",100*(float) ext2fs_r_blocks_count(super)/ (float) ext2fs_blocks_count(super));
+ wmove (show_pad,3,40);wprintw (show_pad,"%2.2f%%",100*(float) ext2fs_free_blocks_count(super)/ (float) ext2fs_blocks_count(super));
+ }
+
+ if (super->s_inodes_count != 0) {
+ wmove (show_pad,4,40);wprintw (show_pad,"%2.2f%%",100*(float) super->s_free_inodes_count/ (float) super->s_inodes_count);
+ }
+
+ wmove (show_pad,6,40);
+ switch (super->s_log_block_size) {
+ case 0: wprintw (show_pad,"1024 bytes");break;
+ case 1: wprintw (show_pad,"2048 bytes");break;
+ case 2: wprintw (show_pad,"4096 bytes");break;
+ }
+ wmove (show_pad,11,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_super_block.s_mtime));
+ wmove (show_pad,12,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_super_block.s_wtime));
+ wmove (show_pad,19,40);wprintw (show_pad,"%s",ctime ((time_t *) &type_data.u.t_ext2_super_block.s_lastcheck));
+ wmove (show_pad,15,40);
+
+ switch (type_data.u.t_ext2_super_block.s_magic) {
+ case EXT2_SUPER_MAGIC:
+ wprintw (show_pad,"ext2 >= 0.2B");
+ break;
+ case EXT2_PRE_02B_MAGIC:
+ wprintw (show_pad,"ext2 < 0.2B (not supported)");
+ break;
+ default:
+ wprintw (show_pad,"Unknown");
+ break;
+ }
+
+ wmove (show_pad,16,40);
+ if (type_data.u.t_ext2_super_block.s_state & 0x1)
+ wprintw (show_pad,"clean ");
+ else
+ wprintw (show_pad,"not clean ");
+
+ if (type_data.u.t_ext2_super_block.s_state & 0x2)
+ wprintw (show_pad,"with errors ");
+ else
+ wprintw (show_pad,"with no errors");
+
+ wmove (show_pad,17,40);
+
+ switch (type_data.u.t_ext2_super_block.s_errors) {
+ case EXT2_ERRORS_CONTINUE:
+ wprintw (show_pad,"Continue");
+ break;
+ case EXT2_ERRORS_RO:
+ wprintw (show_pad,"Remount read only");
+ break;
+ case EXT2_ERRORS_PANIC:
+ wprintw (show_pad,"Issue kernel panic");
+ break;
+ default:
+ wprintw (show_pad,"Unknown");
+ break;
+ }
+
+ wmove (show_pad,21,40);
+
+ switch (type_data.u.t_ext2_super_block.s_creator_os) {
+
+ case EXT2_OS_LINUX:
+ wprintw (show_pad,"Linux :-)");
+ break;
+
+ case EXT2_OS_HURD:
+ wprintw (show_pad,"Hurd");
+ break;
+
+ default:
+ wprintw (show_pad,"Unknown");
+ break;
+ }
+
+ refresh_show_pad ();
+
+ wmove (show_win,1,0);wprintw (show_win,"\n");wmove (show_win,2,0);
+ wprintw (show_win,"Superblock copy %ld ",super_info.copy_num);
+ if (super_info.copy_num==0)
+ wprintw (show_win,"(main copy)");
+ wprintw (show_win,"\n");
+ refresh_show_win ();
+}
+
+void type_ext2_super_block___gocopy (char *command_line)
+
+{
+ unsigned long copy_num,offset;
+ char *ptr,buffer [80];
+
+ ptr=parse_word (command_line,buffer);
+ if (*ptr==0) {
+ wprintw (command_win,"Error - No argument specified\n");refresh_command_win ();return;
+ }
+ ptr=parse_word (ptr,buffer);
+
+ copy_num=atol (buffer);
+
+ offset=file_system_info.super_block_offset+copy_num*file_system_info.no_blocks_in_group*file_system_info.block_size;
+
+ if (offset > file_system_info.file_system_size) {
+ wprintw (command_win,"Error - Copy number out of bounds\n");refresh_command_win ();return;
+ }
+
+ super_info.copy_num=copy_num;
+ device_offset=offset;
+
+ sprintf (buffer,"setoffset %ld",device_offset);dispatch (buffer);
+ strcpy (buffer,"show");dispatch (buffer);
+}
+
+void type_ext2_super_block___setactivecopy (char *command_line)
+
+{
+ struct ext2_super_block sb;
+
+ sb=type_data.u.t_ext2_super_block;
+ dispatch ("gocopy 0");
+ type_data.u.t_ext2_super_block=sb;
+ dispatch ("show");
+}
diff --git a/ext2ed/win.c b/ext2ed/win.c
new file mode 100644
index 0000000..23d0da1
--- /dev/null
+++ b/ext2ed/win.c
@@ -0,0 +1,232 @@
+/*
+
+/usr/src/ext2ed/win.c
+
+A part of the extended file system 2 disk editor.
+
+--------------------------------------------------------
+Window management - Interfacing with the ncurses library
+--------------------------------------------------------
+
+First written on: April 17 1995
+Modified on : April 05 2001 Christian.Bac@int-evry.fr
+it looks like readline does not like that initscr decides to set the tty to
+noecho.
+
+Copyright (C) 1995 Gadi Oxman
+
+*/
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "ext2ed.h"
+#include "../version.h"
+
+struct struct_pad_info show_pad_info;
+WINDOW *title_win,*show_win,*command_win,*mt_win1,*mt_win2,*show_pad;
+
+/* to remember configuration after initscr
+ * and modify it
+ */
+struct termios termioInit, termioCurrent;
+
+void draw_title_win (void)
+{
+ char title_string [128];
+
+ werase(title_win);
+ box (title_win,0,0);
+ sprintf (title_string,"EXT2ED - Extended-2 File System editor ver %s (%s)", E2FSPROGS_VERSION, E2FSPROGS_DATE);
+ wmove (title_win,TITLE_WIN_LINES/2,(COLS-strlen (title_string))/2);
+ wprintw (title_win,title_string);
+ wrefresh(title_win);
+}
+
+void setup_show_win(void)
+{
+ wbkgdset (show_win,A_REVERSE);werase (show_win);
+ show_pad_info.line=0;
+ show_pad_info.col=0;
+ show_pad_info.display_lines=LINES-TITLE_WIN_LINES-SHOW_WIN_LINES-COMMAND_WIN_LINES-2;
+ show_pad_info.display_cols=COLS;
+ show_pad_info.max_line=show_pad_info.display_lines-1;show_pad_info.max_col=show_pad_info.display_cols-1;
+ show_pad_info.disable_output=0;
+}
+
+void init_windows (void)
+{
+ initscr ();
+ tcgetattr(0,&termioInit); /* save initial config */
+ termioCurrent = termioInit;
+ termioCurrent.c_lflag |= ECHO; /* set echo on */
+ tcsetattr(0,TCSANOW,&termioCurrent);
+
+ if (LINES<TITLE_WIN_LINES+SHOW_WIN_LINES+COMMAND_WIN_LINES+3) {
+ printf ("Sorry, your terminal screen is too small\n");
+ printf ("Error - Can not initialize windows\n");
+ exit (1);
+ }
+
+ title_win=newwin (TITLE_WIN_LINES,COLS,0,0);
+ show_win=newwin (SHOW_WIN_LINES,COLS,TITLE_WIN_LINES,0);
+ show_pad=newpad (SHOW_PAD_LINES,SHOW_PAD_COLS);
+ mt_win1=newwin (1,COLS,TITLE_WIN_LINES+SHOW_WIN_LINES,0);
+ mt_win2=newwin (1,COLS,LINES-COMMAND_WIN_LINES-1,0);
+ command_win=newwin (COMMAND_WIN_LINES,COLS,LINES-COMMAND_WIN_LINES,0);
+
+ if (title_win==NULL || show_win==NULL || show_pad==NULL || command_win==NULL) {
+ printf ("Error - Not enough memory - Can not initialize windows\n");exit (1);
+ }
+
+ draw_title_win();
+
+ setup_show_win();
+
+ scrollok (command_win,TRUE);
+
+ refresh_title_win ();
+ refresh_show_win ();
+ refresh_show_pad();
+ refresh_command_win ();
+ wrefresh(mt_win1);
+ wrefresh(mt_win2);
+}
+
+void refresh_title_win (void)
+{
+ wrefresh (title_win);
+}
+
+void refresh_show_win (void)
+{
+ int current_page,total_pages;
+
+ current_page=show_pad_info.line/show_pad_info.display_lines+1;
+ if (show_pad_info.line%show_pad_info.display_lines)
+ current_page++;
+ total_pages=show_pad_info.max_line/show_pad_info.display_lines+1;
+
+ wmove (show_win,2,COLS-18);
+ wprintw (show_win,"Page %d of %d\n",current_page,total_pages);
+
+ wmove (show_win,2,COLS-18);
+ wrefresh (show_win);
+}
+
+
+void refresh_show_pad (void)
+
+{
+ int left,top,right,bottom,i;
+
+ if (show_pad_info.disable_output)
+ return;
+
+ if (show_pad_info.max_line < show_pad_info.display_lines-1) {
+ for (i=show_pad_info.max_line+1;i<show_pad_info.display_lines;i++) {
+ wmove (show_pad,i,0);wprintw (show_pad,"\n");
+ }
+ }
+ left=0;right=show_pad_info.display_cols-1;
+ top=TITLE_WIN_LINES+SHOW_WIN_LINES+1;bottom=top+show_pad_info.display_lines-1;
+
+ if (show_pad_info.line > show_pad_info.max_line-show_pad_info.display_lines+1)
+ show_pad_info.line=show_pad_info.max_line-show_pad_info.display_lines+1;
+
+ if (show_pad_info.line < 0)
+ show_pad_info.line=0;
+
+#ifdef OLD_NCURSES
+ prefresh (show_pad,show_pad_info.line,show_pad_info.col,top,left,show_pad_info.display_lines-1,show_pad_info.display_cols-1);
+#else
+ prefresh (show_pad,show_pad_info.line,show_pad_info.col,top,left,top+show_pad_info.display_lines-1,left+show_pad_info.display_cols-1);
+#endif
+}
+
+void refresh_command_win (void)
+{
+ wrefresh (command_win);
+}
+
+void close_windows (void)
+{
+// echo ();
+ tcsetattr(0,TCSANOW,&termioInit);
+
+ delwin (title_win);
+ delwin (command_win);
+ delwin (show_win);
+ delwin (show_pad);
+
+ endwin ();
+}
+
+void show_info (void)
+{
+ int block_num,block_offset;
+
+ block_num=device_offset/file_system_info.block_size;
+ block_offset=device_offset%file_system_info.block_size;
+
+ wmove (show_win,0,0);
+ wprintw (show_win,"Offset %-3ld in block %ld. ",block_offset,block_num);
+ if (current_type != NULL)
+ wprintw (show_win,"Type: %s\n",current_type->name);
+ else
+ wprintw (show_win,"Type: %s\n","none");
+
+ refresh_show_win ();
+}
+
+
+void redraw_all (void)
+{
+ int min_lines = TITLE_WIN_LINES+SHOW_WIN_LINES+COMMAND_WIN_LINES+3;
+ struct winsize ws;
+ int save_col, save_lines;
+
+ /* get the size of the terminal connected to stdout */
+ ioctl(1, TIOCGWINSZ, &ws);
+ /*
+ * Do it again because GDB doesn't stop before the first ioctl
+ * call, we want an up-to-date size when we're
+ * single-stepping.
+ */
+ if (ioctl(1, TIOCGWINSZ, &ws) == 0) {
+ if (ws.ws_row < min_lines)
+ ws.ws_row = min_lines;
+ if ((ws.ws_row != LINES) || (ws.ws_col != COLS)) {
+ wmove (show_win,2,COLS-18);
+ wclrtoeol(show_win);
+ wrefresh(show_win);
+ resizeterm(ws.ws_row, ws.ws_col);
+ wresize(title_win, TITLE_WIN_LINES,COLS);
+ wresize(show_win, SHOW_WIN_LINES,COLS);
+ wresize(command_win, COMMAND_WIN_LINES,COLS);
+ wresize(mt_win1, 1,COLS);
+ wresize(mt_win2, 1,COLS);
+ mvwin(mt_win2, LINES-COMMAND_WIN_LINES-1,0);
+ mvwin(command_win, LINES-COMMAND_WIN_LINES,0);
+ draw_title_win();
+ show_pad_info.display_lines=LINES-TITLE_WIN_LINES-SHOW_WIN_LINES-COMMAND_WIN_LINES-2;
+ show_pad_info.display_cols=COLS;
+ }
+ }
+ clearok(title_win, 1);
+ clearok(show_win, 1);
+ clearok(command_win, 1);
+ clearok(mt_win1, 1);
+ clearok(mt_win2, 1);
+ wrefresh(mt_win1);
+ wrefresh(mt_win2);
+ refresh_show_pad();
+ refresh_show_win();
+ refresh_title_win ();
+ refresh_command_win ();
+}