diff options
author | Jeff Mahoney <jeffm@suse.com> | 2012-10-11 11:26:22 -0400 |
---|---|---|
committer | Jeff Mahoney <jeffm@suse.com> | 2012-10-11 11:26:22 -0400 |
commit | e7102bdb66ffebd7d55ac54c46124706e15a4233 (patch) | |
tree | 2a455f22a881b0c33605e887a461c63d5ecd65bb | |
download | reiserfsprogs-e7102bdb66ffebd7d55ac54c46124706e15a4233.tar.gz |
Initial commit -- seeded with reiserfsprogs 3.x.0jv3.x.0j
77 files changed, 33513 insertions, 0 deletions
@@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..5a06c97 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,120 @@ +2001-03-31 + * reiserfsck + empty lost directories do not get linked into /lost+found +2001-03-28 + * reiserfsck + --nolog option is added + +2001-03-26 + * called 3.x.0j + * reiserfsck + with -o it tries to fix "objectid sharing" problem + * reiserfsprogs.spec (Anthon van der Neut) + rpm can be built as non-root + link from reiserfsck to fsck.reiserfs + rpm -e reiserfsprogs should now work w/o rmdir of / +2001-03-13 + * reiserfsck + --quiet option is added + --fix-bogus-files option is added to fix transprently + corrupted modes and to fix sizes which are longer that + real file size + directory item verifying changed + -u has been replaced with -b to pass reiserfsck the list + of block to build tree off + -c is added to have pass 0 to save bitmap fo leaves found + +2001-03-10 + * called 3.x.0h + * configure.in + RPM_OPT_FLASG is added to CFLAGS (Anthon van der Neut) + * reiserfsck + -u option is added. It should save time when repeating + --rebuild-tree + hash hits statistic is added on pass 0 + +2001-03-07 + * reiserfsck + -V option to print version and exit added + --fix-fixable changed: directory's sd_size and st_blocks + corrections, removing of entries pointing to nowhere + + * resize_reiserfs + man page is added + +2001-03-05 + * resize_reiserfs + Marcel Waldvogel <mwa@arlq.wustl.edu>'s user interface and + error messages improvements + +2001-03-01 + * mkreiserfs + -q option is added (Larry Auton <lda@winux.com>) + * reiserfsck + --fix-fixable changed: bitmap correction commented + out. Correction of broken slots of indirect items and + corrections of dir entry state added + +2001-02-23 + * called 3.x.0e + * reiserfsck + not tested on 2.2 + is now able to work with regular file (2.4.x is needed for that) + lost+found fixed: it now first links directories then + files. Still not good as it can not pull out deep directory + +2001-02-19 + * called 3.x.0c + * reiserfsck + --fix-fixable option is added. So far it only repairs bitmaps + and free block count when they mismatch + * library + reiserfs_find/add_entry added + +2001-02-05 + * mkreiserfs + can make filesystem with 1 data block + 3.6 format is now default + +2001-01-20 + * portability + Zam ran the reiserfsprogs on alpha + * resizer + Zam managed to resize few partitions. + * reiserfsck + pass0 deletes items which are out of order, tries to fix + items with zeroed k_objectid or k_dir_id and to throw + items which are transparently out of order and tries to + fix "." and ".." of directory items. Pass0 corrects also + corruptions in directory items + * man pages: + get included into dist when doing 'make dist' + * mkreisrefs + explains what is mininal size of reiserfs partition which + can be created + +2001-01-12 + * reiserfsck: + --interactive option is added + * debugreiserfs: + few bugs fixed + +2001-01-07 + * reiserfs library: + started with reiserfs_open, reiserfs_close, bitmap tools + * reiserfsck: + filesystem mounted read-only can be checked + number of options decreased + journal replay fixed + pass 0 added. + fsck can be stopped after the tree is built. (should safe time when debugging) + a lot of work still left + * debugreiserfs: + metadata packing changed + added a feature to intentionally corrupt filesystem (should be useful for fsck debugging) + * resizer: + not updated yet + + * man pages: + updated for all three progs + @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..2e88813 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = include lib reiserfscore mkreiserfs fsck debugreiserfs resize_reiserfs + +EXTRA_DIST = version.h reiserfsprogs.spec + diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..5f9f214 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,318 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = . + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +SUBDIRS = include lib reiserfscore mkreiserfs fsck debugreiserfs resize_reiserfs + +EXTRA_DIST = version.h reiserfsprogs.spec +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +DIST_COMMON = README AUTHORS COPYING ChangeLog INSTALL Makefile.am \ +Makefile.in NEWS aclocal.m4 configure configure.in install-sh missing \ +mkinstalldirs + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$@ CONFIG_HEADERS= $(SHELL) ./config.status + +$(ACLOCAL_M4): configure.in + cd $(srcdir) && $(ACLOCAL) + +config.status: $(srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck +$(srcdir)/configure: $(srcdir)/configure.in $(ACLOCAL_M4) $(CONFIGURE_DEPENDENCIES) + cd $(srcdir) && $(AUTOCONF) + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. + +@SET_MAKE@ + +all-recursive install-data-recursive install-exec-recursive \ +installdirs-recursive install-recursive uninstall-recursive \ +check-recursive installcheck-recursive info-recursive dvi-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $(MAKEFLAGS); amf=$$2; \ + dot_seen=no; \ + rev=''; list='$(SUBDIRS)'; for subdir in $$list; do \ + rev="$$subdir $$rev"; \ + test "$$subdir" = "." && dot_seen=yes; \ + done; \ + test "$$dot_seen" = "no" && rev=". $$rev"; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + -rm -rf $(distdir) + GZIP=$(GZIP_ENV) $(TAR) zxf $(distdir).tar.gz + mkdir $(distdir)/=build + mkdir $(distdir)/=inst + dc_install_base=`cd $(distdir)/=inst && pwd`; \ + cd $(distdir)/=build \ + && ../configure --srcdir=.. --prefix=$$dc_install_base \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) dist + -rm -rf $(distdir) + @banner="$(distdir).tar.gz is ready for distribution"; \ + dashes=`echo "$$banner" | sed s/./=/g`; \ + echo "$$dashes"; \ + echo "$$banner"; \ + echo "$$dashes" +dist: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +dist-all: distdir + -chmod -R a+r $(distdir) + GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir) + -rm -rf $(distdir) +distdir: $(DISTFILES) + -rm -rf $(distdir) + mkdir $(distdir) + -chmod 777 $(distdir) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done + for subdir in $(SUBDIRS); do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + chmod 777 $(distdir)/$$subdir; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir=../$(distdir) distdir=../$(distdir)/$$subdir distdir) \ + || exit 1; \ + fi; \ + done +info-am: +info: info-recursive +dvi-am: +dvi: dvi-recursive +check-am: all-am +check: check-recursive +installcheck-am: +installcheck: installcheck-recursive +install-exec-am: +install-exec: install-exec-recursive + +install-data-am: +install-data: install-data-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-recursive +uninstall-am: +uninstall: uninstall-recursive +all-am: Makefile +all-redirect: all-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: installdirs-recursive +installdirs-am: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-recursive + +clean-am: clean-tags clean-generic mostlyclean-am + +clean: clean-recursive + +distclean-am: distclean-tags distclean-generic clean-am + +distclean: distclean-recursive + -rm -f config.status + +maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-recursive + -rm -f config.status + +.PHONY: install-data-recursive uninstall-data-recursive \ +install-exec-recursive uninstall-exec-recursive installdirs-recursive \ +uninstalldirs-recursive all-recursive check-recursive \ +installcheck-recursive info-recursive dvi-recursive \ +mostlyclean-recursive distclean-recursive clean-recursive \ +maintainer-clean-recursive tags tags-recursive mostlyclean-tags \ +distclean-tags clean-tags maintainer-clean-tags distdir info-am info \ +dvi-am dvi check check-am installcheck-am installcheck install-exec-am \ +install-exec install-data-am install-data install-am install \ +uninstall-am uninstall all-redirect all-am all installdirs-am \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: @@ -0,0 +1,157 @@ +[LICENSING] + +ReiserFS is hereby licensed under the GNU General +Public License version 2. + +Source code files that contain the phrase "licensing governed by +reiserfs/README" are "governed files" throughout this file. Governed +files are licensed under the GPL. The portions of them owned by Hans +Reiser, or authorized to be licensed by him, have been in the past, +and likely will be in the future, licensed to other parties under +other licenses. If you add your code to governed files, and don't +want it to be owned by Hans Reiser, put your copyright label on that +code so the poor blight and his customers can keep things straight. +All portions of governed files not labeled otherwise are owned by Hans +Reiser, and by adding your code to it, widely distributing it to +others or sending us a patch, and leaving the sentence in stating that +licensing is governed by the statement in this file, you accept this. +It will be a kindness if you identify whether Hans Reiser is allowed +to license code labeled as owned by you on your behalf other than +under the GPL, because he wants to know if it is okay to do so and put +a check in the mail to you (for non-trivial improvements) when he +makes his next sale. He makes no guarantees as to the amount if any, +though he feels motivated to motivate contributors, and you can surely +discuss this with him before or after contributing. You have the +right to decline to allow him to license your code contribution other +than under the GPL. + +Further licensing options are available for commercial and/or other +interests directly from Hans Reiser: hans@reiser.to. If you interpret +the GPL as not allowing those additional licensing options, you read +it wrongly, and Richard Stallman agrees with me, when carefully read +you can see that those restrictions on additional terms do not apply +to the owner of the copyright, and my interpretation of this shall +govern for this license. + +Finally, nothing in this license shall be interpreted to allow you to +fail to fairly credit me, or to remove my credits, without my +permission, unless you are an end user not redistributing to others. +If you have doubts about how to properly do that, or about what is +fair, ask. (Last I spoke with him Richard was contemplating how best +to address the fair crediting issue in the next GPL version.) + +[END LICENSING] + +Reiserfs is a file system based on balanced tree algorithms, which is +described at http://www.namesys.com . + +Stop reading here. Go there, then return. + +Send bug reports to yura@namesys.botik.ru. + +mkreiserfs and other utilities are in reiserfs/utils, or wherever your +Linux provider put them. There is some disagreement about how useful +it is for users to get their fsck and mkreiserfs out of sync with the +version of reiserfs that is in their kernel, with many important +distributors wanting them out of sync.:-) Please try to remember to +recompile and reinstall fsck and mkreiserfs with every update of +reiserfs, this is a common source of confusion. Note that some of the +utilities cannot be compiled without accessing the balancing code +which is in the kernel code, and relocating the utilities may require +you to specify where that code can be found. + +Yes, if you update your reiserfs kernel module you do have to +recompile your kernel, most of the time. The errors you get will be +quite cryptic if your forget to do so. + +Real users, as opposed to folks who want to hack and then understand +what went wrong, will want REISERFS_CHECK off. + +Hideous Commercial Pitch: Spread your development costs across other OS +vendors. Select from the best in the world, not the best in your +building, by buying from third party OS component suppliers. Leverage +the software component development power of the internet. Be the most +aggressive in taking advantage of the commercial possibilities of +decentralized internet development, and add value through your branded +integration that you sell as an operating system. Let your competitors +be the ones to compete against the entire internet by themselves. Be +hip, get with the new economic trend, before your competitors do. Send +email to hans@reiser.to. + +To understand the code, after reading the website, start reading the +code by reading reiserfs_fs.h first. + +Hans Reiser was the project initiator, primary architect, source of all +funding for the first 5.5 years, and one of the programmers. He owns +the copyright. + +Vladimir Saveljev was one of the programmers, and he worked long hours +writing the cleanest code. He always made the effort to be the best he +could be, and to make his code the best that it could be. What resulted +was quite remarkable. I don't think that money can ever motivate someone +to work the way he did, he is one of the most selfless men I know. + +Yura helps with benchmarking, coding hashes, and block pre-allocation +code. + +Anatoly Pinchuk is a former member of our team who worked closely with +Vladimir throughout the project's development. He wrote a quite +substantial portion of the total code. He realized that there was a +space problem with packing tails of files for files larger than a node +that start on a node aligned boundary (there are reasons to want to node +align files), and he invented and implemented indirect items and +unformatted nodes as the solution. + +Konstantin Shvachko, with the help of the Russian version of a VC, +tried to put me in a position where I was forced into giving control +of the project to him. (Fortunately, as the person paying the money +for all salaries from my dayjob I owned all copyrights, and you can't +really force takeovers of sole proprietorships.) This was something +curious, because he never really understood the value of our project, +why we should do what we do, or why innovation was possible in +general, but he was sure that he ought to be controlling it. Every +innovation had to be forced past him while he was with us. He added +two years to the time required to complete reiserfs, and was a net +loss for me. Mikhail Gilula was a brilliant innovator who also left +in a destructive way that erased the value of his contributions, and +that he was shown much generosity just makes it more painful. + +Grigory Zaigralin was an extremely effective system administrator for +our group. + +Igor Krasheninnikov was wonderful at hardware procurement, repair, and +network installation. + +Jeremy Fitzhardinge wrote the teahash.c code, and he gives credit to a +textbook he got the algorithm from in the code. Note that his analysis +of how we could use the hashing code in making 32 bit NFS cookies work +was probably more important than the actual algorithm. Colin Plumb also +contributed to it. + +Chris Mason dived right into our code, and in just a few months produced +the journaling code that dramatically increased the value of ReiserFS. +He is just an amazing programmer. + +Igor Zagorovsky is writing much of the new item handler and extent code +for our next major release. + +Alexander Zarochentcev (sometimes known as zam, or sasha), wrote the +resizer, and is hard at work on implementing allocate on flush. SGI +implemented allocate on flush before us for XFS, and generously took +the time to convince me we should do it also. They are great people, +and a great company. + +Yuri Shevchuk and Nikita Danilov are doing squid cache optimization. + +Vitaly Fertman is doing fsck. + +SuSE, IntegratedLinux.com, Ecila, MP3.com, bigstorage.com, and the +Alpha PC Company made it possible for me to not have a day job +anymore, and to dramatically increase our staffing. Ecila funded +hypertext feature development, MP3.com funded journaling, SuSE funded +core development, IntegratedLinux.com funded squid web cache +appliances, bigstorage.com funded HSM, and the alpha PC company funded +the alpha port. Many of these tasks were helped by sponsors other +than the ones just named. SuSE has helped in much more than just +funding.... + diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..9f8add8 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,104 @@ +dnl aclocal.m4 generated automatically by aclocal 1.4 + +dnl Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# Do all the work for Automake. This macro actually does too much -- +# some checks are only needed if your package does certain things. +# But this isn't really a big deal. + +# serial 1 + +dnl Usage: +dnl AM_INIT_AUTOMAKE(package,version, [no-define]) + +AC_DEFUN(AM_INIT_AUTOMAKE, +[AC_REQUIRE([AC_PROG_INSTALL]) +PACKAGE=[$1] +AC_SUBST(PACKAGE) +VERSION=[$2] +AC_SUBST(VERSION) +dnl test to see if srcdir already configured +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) +fi +ifelse([$3],, +AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) +AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])) +AC_REQUIRE([AM_SANITY_CHECK]) +AC_REQUIRE([AC_ARG_PROGRAM]) +dnl FIXME This is truly gross. +missing_dir=`cd $ac_aux_dir && pwd` +AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) +AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) +AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) +AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) +AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) +AC_REQUIRE([AC_PROG_MAKE_SET])]) + +# +# Check to make sure that the build environment is sane. +# + +AC_DEFUN(AM_SANITY_CHECK, +[AC_MSG_CHECKING([whether build environment is sane]) +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "[$]*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "[$]*" != "X $srcdir/configure conftestfile" \ + && test "[$]*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken +alias in your environment]) + fi + + test "[$]2" = conftestfile + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +rm -f conftest* +AC_MSG_RESULT(yes)]) + +dnl AM_MISSING_PROG(NAME, PROGRAM, DIRECTORY) +dnl The program must properly implement --version. +AC_DEFUN(AM_MISSING_PROG, +[AC_MSG_CHECKING(for working $2) +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if ($2 --version) < /dev/null > /dev/null 2>&1; then + $1=$2 + AC_MSG_RESULT(found) +else + $1="$3/missing $2" + AC_MSG_RESULT(missing) +fi +AC_SUBST($1)]) + diff --git a/configure b/configure new file mode 100755 index 0000000..59577f1 --- /dev/null +++ b/configure @@ -0,0 +1,2066 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_default_prefix=/ + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=version.h + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:558: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether build environment is sane""... $ac_c" 1>&6 +echo "configure:611: checking whether build environment is sane" >&5 +# Just in case +sleep 1 +echo timestamp > conftestfile +# Do `set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + set X `ls -Lt $srcdir/configure conftestfile 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t $srcdir/configure conftestfile` + fi + if test "$*" != "X $srcdir/configure conftestfile" \ + && test "$*" != "X conftestfile $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + { echo "configure: error: ls -t appears to fail. Make sure there is not a broken +alias in your environment" 1>&2; exit 1; } + fi + + test "$2" = conftestfile + ) +then + # Ok. + : +else + { echo "configure: error: newly created file is older than distributed files! +Check your system clock" 1>&2; exit 1; } +fi +rm -f conftest* +echo "$ac_t""yes" 1>&6 +if test "$program_transform_name" = s,x,x,; then + program_transform_name= +else + # Double any \ or $. echo might interpret backslashes. + cat <<\EOF_SED > conftestsed +s,\\,\\\\,g; s,\$,$$,g +EOF_SED + program_transform_name="`echo $program_transform_name|sed -f conftestsed`" + rm -f conftestsed +fi +test "$program_prefix" != NONE && + program_transform_name="s,^,${program_prefix},; $program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s,\$\$,${program_suffix},; $program_transform_name" + +# sed with no file args requires a program. +test "$program_transform_name" = "" && program_transform_name="s,x,x," + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:668: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +PACKAGE=reiserfsprogs + +VERSION=3.x.0j + +if test "`cd $srcdir && pwd`" != "`pwd`" && test -f $srcdir/config.status; then + { echo "configure: error: source directory already configured; run "make distclean" there first" 1>&2; exit 1; } +fi +cat >> confdefs.h <<EOF +#define PACKAGE "$PACKAGE" +EOF + +cat >> confdefs.h <<EOF +#define VERSION "$VERSION" +EOF + + + +missing_dir=`cd $ac_aux_dir && pwd` +echo $ac_n "checking for working aclocal""... $ac_c" 1>&6 +echo "configure:714: checking for working aclocal" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (aclocal --version) < /dev/null > /dev/null 2>&1; then + ACLOCAL=aclocal + echo "$ac_t""found" 1>&6 +else + ACLOCAL="$missing_dir/missing aclocal" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoconf""... $ac_c" 1>&6 +echo "configure:727: checking for working autoconf" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoconf --version) < /dev/null > /dev/null 2>&1; then + AUTOCONF=autoconf + echo "$ac_t""found" 1>&6 +else + AUTOCONF="$missing_dir/missing autoconf" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working automake""... $ac_c" 1>&6 +echo "configure:740: checking for working automake" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (automake --version) < /dev/null > /dev/null 2>&1; then + AUTOMAKE=automake + echo "$ac_t""found" 1>&6 +else + AUTOMAKE="$missing_dir/missing automake" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working autoheader""... $ac_c" 1>&6 +echo "configure:753: checking for working autoheader" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (autoheader --version) < /dev/null > /dev/null 2>&1; then + AUTOHEADER=autoheader + echo "$ac_t""found" 1>&6 +else + AUTOHEADER="$missing_dir/missing autoheader" + echo "$ac_t""missing" 1>&6 +fi + +echo $ac_n "checking for working makeinfo""... $ac_c" 1>&6 +echo "configure:766: checking for working makeinfo" >&5 +# Run test in a subshell; some versions of sh will print an error if +# an executable is not found, even if stderr is redirected. +# Redirect stdin to placate older versions of autoconf. Sigh. +if (makeinfo --version) < /dev/null > /dev/null 2>&1; then + MAKEINFO=makeinfo + echo "$ac_t""found" 1>&6 +else + MAKEINFO="$missing_dir/missing makeinfo" + echo "$ac_t""missing" 1>&6 +fi + + + + + +PRESET_CFLAGS=$CFLAGS +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:786: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:816: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:867: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:899: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 910 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:915: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:941: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:946: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <<EOF +#ifdef __GNUC__ + yes; +#endif +EOF +if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:955: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:974: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +if test -z $PRESET_CFLAGS; then + CFLAGS="${RPM_OPT_FLAGS} -g -O2 -Wall" +fi + +# Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:1012: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_RANLIB="ranlib" + break + fi + done + IFS="$ac_save_ifs" + test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB=":" +fi +fi +RANLIB="$ac_cv_prog_RANLIB" +if test -n "$RANLIB"; then + echo "$ac_t""$RANLIB" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:1042: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext <<EOF +#line 1057 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1063: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext <<EOF +#line 1074 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1080: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext <<EOF +#line 1091 "configure" +#include "confdefs.h" +#include <assert.h> +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1097: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1122: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1127 "configure" +#include "confdefs.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1135: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1152 "configure" +#include "confdefs.h" +#include <string.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext <<EOF +#line 1170 "configure" +#include "confdefs.h" +#include <stdlib.h> +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext <<EOF +#line 1191 "configure" +#include "confdefs.h" +#include <ctype.h> +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1202: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in fcntl.h limits.h malloc.h sys/ioctl.h unistd.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1229: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1234 "configure" +#include "confdefs.h" +#include <$ac_hdr> +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1239: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <<EOF +#define $ac_tr_hdr 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:1267: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1272 "configure" +#include "confdefs.h" + +int main() { + +/* Ultrix mips cc rejects this. */ +typedef int charset[2]; const charset x; +/* SunOS 4.1.1 cc rejects this. */ +char const *const *ccp; +char **p; +/* NEC SVR4.0.2 mips cc rejects this. */ +struct point {int x, y;}; +static struct point const zero = {0,0}; +/* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in an arm + of an if-expression whose if-part is not a constant expression */ +const char *g = "string"; +ccp = &g + (g ? g-g : 0); +/* HPUX 7.0 cc rejects these. */ +++ccp; +p = (char**) ccp; +ccp = (char const *const *) p; +{ /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; +} +{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; +} +{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; +} +{ /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1321: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for inline""... $ac_c" 1>&6 +echo "configure:1342: checking for inline" >&5 +if eval "test \"`echo '$''{'ac_cv_c_inline'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat > conftest.$ac_ext <<EOF +#line 1349 "configure" +#include "confdefs.h" + +int main() { +} $ac_kw foo() { +; return 0; } +EOF +if { (eval echo configure:1356: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_inline=$ac_kw; break +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 +fi +rm -f conftest* +done + +fi + +echo "$ac_t""$ac_cv_c_inline" 1>&6 +case "$ac_cv_c_inline" in + inline | yes) ;; + no) cat >> confdefs.h <<\EOF +#define inline +EOF + ;; + *) cat >> confdefs.h <<EOF +#define inline $ac_cv_c_inline +EOF + ;; +esac + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1382: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1387 "configure" +#include "confdefs.h" +#include <sys/types.h> +#if STDC_HEADERS +#include <stdlib.h> +#include <stddef.h> +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking for st_rdev in struct stat""... $ac_c" 1>&6 +echo "configure:1415: checking for st_rdev in struct stat" >&5 +if eval "test \"`echo '$''{'ac_cv_struct_st_rdev'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1420 "configure" +#include "confdefs.h" +#include <sys/types.h> +#include <sys/stat.h> +int main() { +struct stat s; s.st_rdev; +; return 0; } +EOF +if { (eval echo configure:1428: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_struct_st_rdev=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_struct_st_rdev=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_struct_st_rdev" 1>&6 +if test $ac_cv_struct_st_rdev = yes; then + cat >> confdefs.h <<\EOF +#define HAVE_ST_RDEV 1 +EOF + +fi + + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:1451: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext <<EOF +#line 1457 "configure" +#include "confdefs.h" +#include <sgtty.h> +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext <<EOF +#line 1475 "configure" +#include "confdefs.h" +#include <termio.h> +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo $ac_n "checking for 8-bit clean memcmp""... $ac_c" 1>&6 +echo "configure:1497: checking for 8-bit clean memcmp" >&5 +if eval "test \"`echo '$''{'ac_cv_func_memcmp_clean'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_memcmp_clean=no +else + cat > conftest.$ac_ext <<EOF +#line 1505 "configure" +#include "confdefs.h" + +main() +{ + char c0 = 0x40, c1 = 0x80, c2 = 0x81; + exit(memcmp(&c0, &c2, 1) < 0 && memcmp(&c1, &c2, 1) < 0 ? 0 : 1); +} + +EOF +if { (eval echo configure:1515: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + ac_cv_func_memcmp_clean=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_func_memcmp_clean=no +fi +rm -fr conftest* +fi + +fi + +echo "$ac_t""$ac_cv_func_memcmp_clean" 1>&6 +test $ac_cv_func_memcmp_clean = no && LIBOBJS="$LIBOBJS memcmp.${ac_objext}" + +echo $ac_n "checking for strftime""... $ac_c" 1>&6 +echo "configure:1533: checking for strftime" >&5 +if eval "test \"`echo '$''{'ac_cv_func_strftime'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1538 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char strftime(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_strftime) || defined (__stub___strftime) +choke me +#else +strftime(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1561: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_strftime=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_strftime=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'strftime`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +else + echo "$ac_t""no" 1>&6 +# strftime is in -lintl on SCO UNIX. +echo $ac_n "checking for strftime in -lintl""... $ac_c" 1>&6 +echo "configure:1583: checking for strftime in -lintl" >&5 +ac_lib_var=`echo intl'_'strftime | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lintl $LIBS" +cat > conftest.$ac_ext <<EOF +#line 1591 "configure" +#include "confdefs.h" +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char strftime(); + +int main() { +strftime() +; return 0; } +EOF +if { (eval echo configure:1602: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_STRFTIME 1 +EOF + +LIBS="-lintl $LIBS" +else + echo "$ac_t""no" 1>&6 +fi + +fi + +echo $ac_n "checking for vprintf""... $ac_c" 1>&6 +echo "configure:1629: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1634 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char vprintf(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1657: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:1681: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1686 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1709: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_func in strerror strstr strtol +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1736: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <<EOF +#line 1741 "configure" +#include "confdefs.h" +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func(); below. */ +#include <assert.h> +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1764: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <<EOF +#define $ac_tr_func 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi +done + + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS <<EOF +#! /bin/sh +# Generated automatically by configure. +# Run this file to recreate the current configuration. +# This directory was configured as follows, +# on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "include/Makefile mkreiserfs/Makefile resize_reiserfs/Makefile fsck/Makefile lib/Makefile Makefile reiserfscore/Makefile debugreiserfs/Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS <<EOF + +# Protect against being on the right side of a sed subst in config.status. +sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g; + s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@PACKAGE@%$PACKAGE%g +s%@VERSION@%$VERSION%g +s%@ACLOCAL@%$ACLOCAL%g +s%@AUTOCONF@%$AUTOCONF%g +s%@AUTOMAKE@%$AUTOMAKE%g +s%@AUTOHEADER@%$AUTOHEADER%g +s%@MAKEINFO@%$MAKEINFO%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CC@%$CC%g +s%@RANLIB@%$RANLIB%g +s%@CPP@%$CPP%g +s%@LIBOBJS@%$LIBOBJS%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <<EOF + +CONFIG_FILES=\${CONFIG_FILES-"include/Makefile mkreiserfs/Makefile resize_reiserfs/Makefile fsck/Makefile lib/Makefile Makefile reiserfscore/Makefile debugreiserfs/Makefile"} +EOF +cat >> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +EOF +cat >> $CONFIG_STATUS <<EOF + +EOF +cat >> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + + + + + + + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..476aee4 --- /dev/null +++ b/configure.in @@ -0,0 +1,48 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(version.h) + +AM_INIT_AUTOMAKE(reiserfsprogs, 3.x.0j) + +dnl We install in /sbin, the utils are to be available on boot +AC_PREFIX_DEFAULT(/) + +PRESET_CFLAGS=$CFLAGS +AC_PROG_CC +if test -z $PRESET_CFLAGS; then +dnl CFLAGS="${RPM_OPT_FLAGS} $CFLAGS -Wall" + CFLAGS="${RPM_OPT_FLAGS} -g -O2 -Wall" +fi + +dnl Checks for programs. +AC_PROG_RANLIB +dnl AC_PROG_AWK +dnl AC_PROG_INSTALL +dnl AC_PROG_LN_S + +dnl Checks for libraries. + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h limits.h malloc.h sys/ioctl.h unistd.h) + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_STRUCT_ST_RDEV + +dnl Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MEMCMP +AC_FUNC_STRFTIME +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(strerror strstr strtol) + +AC_OUTPUT(include/Makefile mkreiserfs/Makefile resize_reiserfs/Makefile fsck/Makefile lib/Makefile Makefile reiserfscore/Makefile debugreiserfs/Makefile) + + + + + + + diff --git a/debugreiserfs/Makefile.am b/debugreiserfs/Makefile.am new file mode 100644 index 0000000..a5b1124 --- /dev/null +++ b/debugreiserfs/Makefile.am @@ -0,0 +1,10 @@ +sbin_PROGRAMS = debugreiserfs unpack + +debugreiserfs_SOURCES = debugreiserfs.c pack.c +unpack_SOURCES = unpack.c debugreiserfs.h +man_MANS = debugreiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include diff --git a/debugreiserfs/Makefile.in b/debugreiserfs/Makefile.in new file mode 100644 index 0000000..c73aa68 --- /dev/null +++ b/debugreiserfs/Makefile.in @@ -0,0 +1,339 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +sbin_PROGRAMS = debugreiserfs unpack + +debugreiserfs_SOURCES = debugreiserfs.c pack.c +unpack_SOURCES = unpack.c debugreiserfs.h +man_MANS = debugreiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +PROGRAMS = $(sbin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +debugreiserfs_OBJECTS = debugreiserfs.o pack.o +debugreiserfs_LDADD = $(LDADD) +debugreiserfs_DEPENDENCIES = ../lib/libmisc.a ../reiserfscore/libcore.a +debugreiserfs_LDFLAGS = +unpack_OBJECTS = unpack.o +unpack_LDADD = $(LDADD) +unpack_DEPENDENCIES = ../lib/libmisc.a ../reiserfscore/libcore.a +unpack_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +man8dir = $(mandir)/man8 +MANS = $(man_MANS) + +NROFF = nroff +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(debugreiserfs_SOURCES) $(unpack_SOURCES) +OBJECTS = $(debugreiserfs_OBJECTS) $(unpack_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps debugreiserfs/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +debugreiserfs: $(debugreiserfs_OBJECTS) $(debugreiserfs_DEPENDENCIES) + @rm -f debugreiserfs + $(LINK) $(debugreiserfs_LDFLAGS) $(debugreiserfs_OBJECTS) $(debugreiserfs_LDADD) $(LIBS) + +unpack: $(unpack_OBJECTS) $(unpack_DEPENDENCIES) + @rm -f unpack + $(LINK) $(unpack_LDFLAGS) $(unpack_OBJECTS) $(unpack_LDADD) $(LIBS) + +install-man8: + $(mkinstalldirs) $(DESTDIR)$(man8dir) + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \ + done + +uninstall-man8: + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \ + rm -f $(DESTDIR)$(man8dir)/$$inst; \ + done +install-man: $(MANS) + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-man8 +uninstall-man: + @$(NORMAL_UNINSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-man8 + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = debugreiserfs + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +debugreiserfs.o: debugreiserfs.c debugreiserfs.h ../include/io.h \ + ../include/misc.h ../include/reiserfs_lib.h \ + ../include/reiserfs_fs.h ../version.h +pack.o: pack.c debugreiserfs.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h +unpack.o: unpack.c debugreiserfs.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-sbinPROGRAMS +install-exec: install-exec-am + +install-data-am: install-man +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-sbinPROGRAMS uninstall-man +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) $(MANS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man8 + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-sbinPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \ +clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ +install-sbinPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile install-man8 uninstall-man8 \ +install-man uninstall-man tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/debugreiserfs/debugreiserfs.8 b/debugreiserfs/debugreiserfs.8 new file mode 100644 index 0000000..9de473f --- /dev/null +++ b/debugreiserfs/debugreiserfs.8 @@ -0,0 +1,68 @@ +.\" -*- nroff -*- +.\" Copyright 1996-2001 Hans Reiser. +.\" +.TH DEBUGREISERFS 8 "March 2001" "Reiserfsprogs 3.x.0j" +.SH NAME +debugreiserfs +.SH SYNOPSIS +.B debugreiserfs +[ +.B -jdcmos +] [ +.B -b \fIblocknumber +] [ +.B -p \fIfilename.bmp +] [ +.B -P \fIfilename.bmp +] +.I device +.SH DESCRIPTION +It helps sometimes to solve problems with reiserfs filesystems. Being +called w/o options it prints super block of reiserfs filesystem found +on the \fIdevice\fR. +.TP +.I device +is the special file corresponding to the device (e.g /dev/hdXX for +IDE disk partition or /dev/sdXX for SCSI disk partition). +.SH OPTIONS +.TP +\fB-j +print contents of journal +.TP +\fB-d +print formatted nodes of the filesystem +.TP +\fB-c +print contents of direct items +.TP +\fB-m +print contents of bitmap (not very useful) +.TP +\fB-o +print objectid map (not very useful) +.TP +\fB-s +scans the partition and prints a line when any kind of reiserfs formatted nodes found +.TP +\fB-b \fIblocknumber +print specified block of the filesystem +.TP +\fB-p \fIfilename +\fB-P \fIfilename + +Makes \fBdebugreiserfs\fR to find filesystem metadata +These exist to help reiserfsck debugging. If reiserfsck fails - +you may extract filesystem metadata with \fBdebugreiserfs\fR -p filename /dev/xxx |gzip +-c > xxx.gz. We download that data and make the filesystem similar +to your with gunzip -c xxx.gz | unpack /dev/xxx (unpack is included +into reiserfsprogs package). This usually allows to reproduce and +debug the problem quickly. When data file is not too large. +-P will cause \fBdebugreiserfs\fR to find and pack metadata from all the device whereas with -p it will only go through blocks marked used in filesystem bitmaps + +.SH AUTHOR +This version of \fBdebugreiserfs\fR has been written by Hans Reiser <reiser@idiom.com>. +.SH BUGS +There are probably few of them. Please, report bugs to Hans Reiser <reiser@idiom.com>. +.SH SEE ALSO +.BR reiserfsck (8), +.BR mkreiserfs (8) diff --git a/debugreiserfs/debugreiserfs.c b/debugreiserfs/debugreiserfs.c new file mode 100644 index 0000000..ad6abf8 --- /dev/null +++ b/debugreiserfs/debugreiserfs.c @@ -0,0 +1,1044 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ +#include "debugreiserfs.h" + +reiserfs_filsys_t fs; + +#define print_usage_and_exit() die ("Usage: %s [-b block-to-print][-idc] device\n\ +-i Causes to print all items of a leaf\n\ +-d content of directory items\n\ +-c content of direct items\n\ +-m bitmap blocks\n\ +-t\n\ +-C\n\ +-p\n\ +-s \n\ +-n scan for name\n\ +-p [filename]\n\ +-P [filename]\n\ +..etc\n", argv[0]); + + + +#if 1 +struct reiserfs_fsstat { + int nr_internals; + int nr_leaves; + int nr_files; + int nr_directories; + int nr_unformatted; +} g_stat_info; +#endif + + +int mode = DO_DUMP; + +/* + * options + */ +int opt_print_regular_file_content = 0;/* -c */ +int opt_print_details = 0; /* -d */ +int opt_print_leaf_items = 0; /* -i */ +int opt_print_objectid_map = 0; /* -o */ +int opt_print_block_map = 0; /* -m */ +int opt_print_journal; /* -j */ + +/* when you want print one block specify -b # */ +int opt_block_to_print = -1; + +/* when you want to corrupt block specify -C # */ +int opt_block_to_corrupt = -1; + +int opt_pack = 0; +int opt_quiet = 0; + +int print_mode (void) +{ + int mode = 0; + + if (opt_print_leaf_items == 1) + mode |= PRINT_LEAF_ITEMS; + if (opt_print_details == 1) + mode |= (PRINT_LEAF_ITEMS | PRINT_ITEM_DETAILS); + if (opt_print_regular_file_content == 1) + mode |= (PRINT_LEAF_ITEMS | PRINT_DIRECT_ITEMS); + return mode; +} + + +static void print_disk_tree (reiserfs_filsys_t fs, int block_nr) +{ + struct buffer_head * bh; + + bh = bread (fs->s_dev, block_nr, fs->s_blocksize); + if (!bh) { + die ("Could not read block %d\n", block_nr); + } + if (is_internal_node (bh)) { + int i; + struct disk_child * dc; + + g_stat_info.nr_internals ++; + print_block (stdout, fs, bh, print_mode (), -1, -1); + + dc = B_N_CHILD (bh, 0); + for (i = 0; i <= B_NR_ITEMS (bh); i ++, dc ++) + print_disk_tree (fs, dc->dc_block_number); + + } else if (is_leaf_node (bh)) { + g_stat_info.nr_leaves ++; + print_block (stdout, fs, bh, print_mode (), -1, -1); + } else { + print_block (stdout, fs, bh, print_mode (), -1, -1); + die ("print_disk_tree: bad block type"); + } + brelse (bh); +} + + + +void pack_one_block (reiserfs_filsys_t fs, unsigned long block); +static void print_one_block (reiserfs_filsys_t fs, int block) +{ + struct buffer_head * bh; + + if (test_bit (block % (fs->s_blocksize * 8), + SB_AP_BITMAP (fs)[block / (fs->s_blocksize * 8)]->b_data)) + fprintf (stderr, "%d is used in true bitmap\n", block); + else + fprintf (stderr, "%d is free in true bitmap\n", block); + + bh = bread (fs->s_dev, block, fs->s_blocksize); + if (!bh) { + printf ("print_one_block: bread fialed\n"); + return; + } + + if (opt_pack) { + pack_one_block (fs, bh->b_blocknr); + brelse (bh); + return; + } + + if (who_is_this (bh->b_data, fs->s_blocksize) != THE_UNKNOWN) + print_block (stdout, fs, bh, PRINT_LEAF_ITEMS | PRINT_ITEM_DETAILS | + (opt_print_regular_file_content == 1 ? PRINT_DIRECT_ITEMS : 0), -1, -1); + else + printf ("Looks like unformatted\n"); + brelse (bh); + return; +} + + +static void corrupt_clobber_hash (char * name, struct item_head * ih, + struct reiserfs_de_head * deh) +{ + printf ("\tCorrupting deh_offset of entry \"%s\" of [%u %u]\n", name, + ih->ih_key.k_dir_id, ih->ih_key.k_objectid); + deh->deh_offset = 700; +} + + +/* this reads list of desired corruptions from stdin and perform the + corruptions. Format of that list: + A hash_code + C name objectid - 'C'ut entry 'name' from directory item with 'objectid' + H name objectid - clobber 'H'hash of entry 'name' of directory 'objectid' + I item_num pos_in_item make pos_in_item-th slot of indirect item to point out of device + O item_num - destroy item 'O'rder - make 'item_num'-th to have key bigger than 'item_num' + 1-th item + D item_num - 'D'elete item_num-th item + S item_num value - change file size (item_num-th item must be stat data) + F item_num value - change sd_first_direct_byte of stat data + J item_num objectid + E name objectid new - change entry's deh_objectid to new + P - print the block +*/ +static void do_corrupt_one_block (reiserfs_filsys_t fs, int block) +{ + struct buffer_head * bh; + int i, j; + struct item_head * ih; + int item_num; + char * line = 0; + int n = 0; + char code, name [100]; + __u32 objectid, new_objectid; + int value; + int hash_code; + int pos_in_item; + + if (test_bit (block % (fs->s_blocksize * 8), + SB_AP_BITMAP (fs)[block / (fs->s_blocksize * 8)]->b_data)) + fprintf (stderr, "%d is used in true bitmap\n", block); + else + fprintf (stderr, "%d is free in true bitmap\n", block); + + bh = bread (fs->s_dev, block, fs->s_blocksize); + if (!bh) { + printf ("corrupt_one_block: bread fialed\n"); + return; + } + + if (who_is_this (bh->b_data, fs->s_blocksize) != THE_LEAF) { + printf ("Can not corrupt not a leaf node\n"); + brelse (bh); + return; + } + + printf ("Corrupting block %lu..\n", bh->b_blocknr); + + while (getline (&line, &n, stdin) != -1) { + switch (line[0]) { + case '#': + case '\n': + continue; + case '?': + printf ("A hash_code - reset hAsh code in super block\n" + "C name objectid - Cut entry 'name' from directory item with 'objectid'\n" + "H name objectid - clobber Hash of entry 'name' of directory 'objectid'\n" + "I item_num pos_in_item make pos_in_tem-th slot of Indirect item to point out of device\n" + "O item_num - destroy item Order - make 'item_num'-th to have key bigger than 'item_num' + 1-th item\n" + "D item_num - Delete item_num-th item\n" + "S item_num value - change file Size (item_num-th item must be stat data)\n" + "F item_num value - change sd_First_direct_byte of stat data\n" + "J item_num objectid - set 'obJectid' of 'item_num'-th item\n" + "E name objectid objectid - set deh_objectid of an entry to objectid\n"); + + continue; + + case 'P': + print_block (stderr, fs, bh, 3, -1, -1); + break; + + case 'A': + /* corrupt hash record in super block */ + if (sscanf (line, "%c %d\n", &code, &hash_code) != 2) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'C': /* cut entry */ + case 'H': /* make hash wrong */ + if (sscanf (line, "%c %s %u\n", &code, name, &objectid) != 3) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'J': /* set objectid : used to simulate objectid sharing problem */ + if (sscanf (line, "%c %d %d\n", &code, &item_num, &objectid) != 3) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'E': /* set objectid : used to simulate objectid sharing problem */ + if (sscanf (line, "%c %s %u %d\n", &code, name, &objectid, &new_objectid) != 4) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'I': /* break unformatted node pointer */ + if (sscanf (line, "%c %d %d\n", &code, &item_num, &pos_in_item) != 3) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'D': /* delete item */ + case 'O': /* make item out of order */ + if (sscanf (line, "%c %d\n", &code, &item_num) != 2) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + + case 'S': /* corrupt st_size */ + case 'F': /* st_first_direct_byte */ + if (sscanf (line, "%c %d %d\n", &code, &item_num, &value) != 3) { + printf ("Wrong format \'%c\'\n", line [0]); + continue; + } + break; + } + + if (code == 'A') { + reiserfs_warning (stderr, "Changing %s to %s\n", code2name (rs_hash (fs->s_rs)), + code2name (hash_code)); + set_hash (fs->s_rs, hash_code); + mark_buffer_dirty (fs->s_sbh); + continue; + } + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + struct reiserfs_de_head * deh; + + if (code == 'I' && i == item_num) { + if (!is_indirect_ih (ih) || pos_in_item >= I_UNFM_NUM (ih)) { + reiserfs_warning (stderr, "Not an indirect item or there is " + "not so many unfm ptrs in it\n"); + continue; + } + * ((__u32 *)B_I_PITEM (bh, ih) + pos_in_item) = SB_BLOCK_COUNT(fs) + 100; + mark_buffer_dirty (bh); + goto cont; + } + + if (code == 'J' && i == item_num) { + ih->ih_key.k_objectid = objectid; + mark_buffer_dirty (bh); + goto cont; + } + + if (code == 'S' && i == item_num) { + /* fixme: old stat data only */ + struct stat_data_v1 * sd; + + sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + reiserfs_warning (stderr, "Changing sd_size of %k from %d to %d\n", + &ih->ih_key, sd->sd_size, value); + sd->sd_size = value; + mark_buffer_dirty (bh); + goto cont; + } + + if (code == 'F' && i == item_num) { + /* fixme: old stat data only */ + struct stat_data_v1 * sd; + + sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + reiserfs_warning (stderr, "Changing sd_first_direct_byte of %k from %d to %d\n", + &ih->ih_key, sd->sd_first_direct_byte, value); + sd->sd_first_direct_byte = value; + mark_buffer_dirty (bh); + goto cont; + } + + if (code == 'D' && i == item_num) { + delete_item (fs, bh, item_num); + mark_buffer_dirty (bh); + goto cont; + } + + if (code == 'O' && i == item_num) { + /* destroy item order */ + struct key * key; + if (i == node_item_number (bh) - 1) { + printf ("can not destroy order\n"); + continue; + } + key = &(ih + 1)->ih_key; + ih->ih_key.k_dir_id = key->k_dir_id + 1; + mark_buffer_dirty (bh); + } + + if (ih->ih_key.k_objectid != objectid || !is_direntry_ih (ih)) + continue; + + deh = B_I_DEH (bh, ih); + + for (j = 0; j < ih_entry_count (ih); j ++, deh ++) { + /* look for proper entry */ + if (name_length (ih, deh, j) != strlen (name) || + strncmp (name, name_in_entry (deh, j), strlen (name))) + continue; + + /* ok, required entry found, make a corruption */ + switch (code) { + case 'C': /* cut entry */ + cut_entry (fs, bh, i, j, 1); + mark_buffer_dirty (bh); + + if (!B_IS_IN_TREE (bh)) { + printf ("NOTE: block is deleted from the tree\n"); + exit (0); + } + goto cont; + break; + + case 'H': /* clobber hash */ + corrupt_clobber_hash (name, ih, deh); + goto cont; + break; + + case 'E': /* change entry's deh_objectid */ + deh->deh_objectid = new_objectid; + break; + + default: + printf ("Unknown command found\n"); + } + mark_buffer_dirty (bh); + } + } + cont: + } + free (line); + printf ("Done\n"); + brelse (bh); + return; +} + + +/* this reads stdin and recover file of given key: */ +/* the input has to be in the follwong format: + K dirid objectid + N name + B blocknumber + .. + then recover_file will read every block, look there specified file and put it into +*/ +static void do_recover (reiserfs_filsys_t fs) +{ + char name [100]; + char * line = 0; + int n = 0; + int fd; + struct key key = {0, 0, }; + struct buffer_head * bh; + struct item_head * ih; + unsigned long block; + char code; + loff_t recovered = 0; + int i, j; + reiserfs_bitmap_t bitmap; + int used, not_used; + + bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_fetch_disk_bitmap (bitmap, fs); + /* we check how many blocks recoverd items point to are free or used */ + used = 0; + not_used = 0; + + fd = 0; + while (getline (&line, &n, stdin) != -1) { + if (line [0] == '#' || line [0] == '\n') + continue; + switch (line [0]) { + case 'K': + /* get a key of file which is to be recovered */ + if (sscanf (line, "%c %u %u\n", &code, &key.k_dir_id, &key.k_objectid) != 3) { + die ("recover_file: wrong input K format"); + } + printf ("Recovering file (%u, %u)\n", key.k_dir_id, key.k_objectid); + break; + + case 'N': + /* get a file name */ + recovered = 0; + if (sscanf (line, "%c %s\n", &code, name) != 2) { + die ("recover_file: wrong input N format"); + } + fd = open (name, O_RDWR | O_CREAT | O_EXCL, 0644); + if (fd == -1) + die ("recover_file: could not create file %s: %s", + name,strerror (errno)); + printf ("Recovering file %s..\n", name); + break; + + case 'B': + if (!fd) + die ("recover_file: file name is not specified"); + if (sscanf (line, "%c %lu\n", &code, &block) != 2) { + die ("recover_file: wrong input B format"); + } + bh = bread (fs->s_dev, block, fs->s_blocksize); + if (!bh) { + printf ("reading block %lu failed\n", block); + continue; + } + + printf ("working with block %lu..\n", block); + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + __u32 * indirect; + struct buffer_head * tmp_bh; + + if (!is_indirect_ih (ih) || key.k_dir_id != ih->ih_key.k_dir_id || + key.k_objectid != ih->ih_key.k_objectid) + continue; + + indirect = (__u32 *)B_I_PITEM (bh, ih); + for (j = 0; j < I_UNFM_NUM (ih); j ++) { + block = le32_to_cpu (indirect [j]); + if (!block) + continue; + tmp_bh = bread (fs->s_dev, block, fs->s_blocksize); + if (!tmp_bh) { + printf ("reading block %Lu failed\n", (loff_t)block * fs->s_blocksize); + continue; + } + if (lseek64 (fd, get_offset (&ih->ih_key) + j * fs->s_blocksize - 1, + SEEK_SET) == (loff_t)-1) { + printf ("llseek failed to pos %Ld\n", (loff_t)block * fs->s_blocksize); + brelse (tmp_bh); + continue; + } + if (reiserfs_bitmap_test_bit (bitmap, block)) + used ++; + else + not_used ++; + /*printf ("block of file %Ld gets block %lu\n", + (get_offset (&ih->ih_key) - 1) / fs->s_blocksize + j, block);*/ + if (write (fd, tmp_bh->b_data, tmp_bh->b_size) != tmp_bh->b_size) { + printf ("write failed to pos %Ld\n", (loff_t)block * fs->s_blocksize); + brelse (tmp_bh); + continue; + } + recovered += fs->s_blocksize; + brelse (tmp_bh); + } + } + brelse (bh); + break; + } + } + printf ("recover_file: %Ld bytes recovered of file %s, key %u %u, %d blocks are free and %d are used\n", + recovered, name, key.k_dir_id, key.k_objectid, not_used, used); +} + + +/* debugreiserfs -p or -P compresses reiserfs meta data: super block, journal, + bitmap blocks and blocks looking like leaves. It may save "bitmap" of + blocks they packed in the file of special format. Reiserfsck can then load + "bitmap" saved in that file and build the tree of blocks marked used in + that "bitmap" */ +char * where_to_save; + + +static char * parse_options (int argc, char * argv []) +{ + int c; + char * tmp; + + while ((c = getopt (argc, argv, "b:C:icdmoMp:P:l:jsnrtu:q")) != EOF) { + switch (c) { + case 'b': /* print a single node */ + opt_block_to_print = strtol (optarg, &tmp, 0); + if (*tmp) + die ("parse_options: bad block number"); + break; + case 'C': + mode = DO_CORRUPT; + opt_block_to_corrupt = strtol (optarg, &tmp, 0); + if (*tmp) + die ("parse_options: bad block number"); + break; + + case 'p': + mode = DO_PACK; + if (optarg) + /* save bitmap of packed blocks in the file 'optarg' */ + asprintf (&where_to_save, "%s", optarg); + break; + + case 'P': + /* scan whole device and pack all blocks looking like a leaf */ + mode = DO_PACK_ALL; + fprintf (stderr, "optarg %s\n", optarg); + if (optarg) + /* save bitmap of packed blocks in the file 'optarg' */ + asprintf (&where_to_save, "%s", optarg); + break; + + case 'i': /* print items of a leaf */ + opt_print_leaf_items = 1; break; + + case 'd': /* print directories */ + opt_print_details = 1; break; + + case 'c': /* print contents of a regular file */ + opt_print_regular_file_content = 1; break; + + case 'o': /* print a objectid map */ + opt_print_objectid_map = 1; break; + + case 'm': /* print a block map */ + opt_print_block_map = 1; break; + + case 'M': /* print a block map with details */ + opt_print_block_map = 2; break; + + case 'j': + opt_print_journal = 1; break; /* print journal */ + + case 's': + mode = DO_SCAN; break; /* read the device and print what reiserfs blocks were found */ + + case 'n': + mode = DO_SCAN_FOR_NAME; break; + + case 'r': + mode = DO_RECOVER; break; + + case 't': + mode = DO_TEST; break; + + case 'q': + /* this makes packing to not show speed info during -p or -P */ + opt_quiet = 1; + break; + } + } + if (optind != argc - 1) + /* only one non-option argument is permitted */ + print_usage_and_exit(); + + return argv[optind]; +} + + + +/* print all valid transactions and found dec blocks */ +static void print_journal (struct super_block * s) +{ + struct buffer_head * d_bh, * c_bh; + struct reiserfs_journal_desc * desc ; + struct reiserfs_journal_commit *commit ; + int end_journal; + int start_journal; + int i, j; + int first_desc_block = 0; + int wrapped = 0; + int valid_transactions = 0; + + start_journal = SB_JOURNAL_BLOCK (s); + end_journal = start_journal + JOURNAL_BLOCK_COUNT; + reiserfs_warning (stdout, "Start scanning from %d\n", start_journal); + + d_bh = 0; + desc = 0; + for (i = start_journal; i < end_journal; i ++) { + d_bh = bread (s->s_dev, i, s->s_blocksize); + if (who_is_this (d_bh->b_data, d_bh->b_size) == THE_JDESC) { + int commit_block; + + if (first_desc_block == 0) + /* store where first desc block found */ + first_desc_block = i; + + print_block (stdout, s, d_bh); /* reiserfs_journal_desc structure will be printed */ + desc = (struct reiserfs_journal_desc *)(d_bh->b_data); + + commit_block = d_bh->b_blocknr + desc->j_len + 1; + if (commit_block >= end_journal) { + reiserfs_warning (stdout, "-- wrapped?"); + wrapped = 1; + break; + } + + c_bh = bread (s->s_dev, commit_block, s->s_blocksize); + commit = bh_commit (c_bh); + if (does_desc_match_commit (desc, commit)) { + reiserfs_warning (stdout, "commit block %d (trans_id %ld, j_len %ld) does not match\n", commit_block, + commit->j_trans_id, commit->j_len); + brelse (c_bh) ; + brelse (d_bh); + continue; + } + + valid_transactions ++; + reiserfs_warning (stdout, "(commit block %d) - logged blocks (", commit_block); +#if 1 + for (j = 0; j < desc->j_len; j ++) { + unsigned long block; + + if (j < JOURNAL_TRANS_HALF) + block = le32_to_cpu (desc->j_realblock[j]); + else + block = le32_to_cpu (commit->j_realblock[i - JOURNAL_TRANS_HALF]); + + if (not_journalable (s, block)) + reiserfs_warning (stdout, " xxxx"); + else { + reiserfs_warning (stdout, " %ld", desc->j_realblock[j]); + if (block_of_bitmap (s, desc->j_realblock[j])) + reiserfs_warning (stdout, "(bmp)"); + } + if (j && (j + 1) % 10 == 0) + reiserfs_warning (stdout, "\n"); + } +#endif + reiserfs_warning (stdout, ")\n"); + i += desc->j_len + 1; + brelse (c_bh); + } + brelse (d_bh); + } + + if (wrapped) { + c_bh = bread (s->s_dev, first_desc_block - 1, s->s_blocksize); + commit = bh_commit (c_bh); + if (does_desc_match_commit (desc, commit)) { + reiserfs_warning (stdout, "No! commit block %d (trans_id %ld, j_len %ld) does not match\n", + first_desc_block - 1, commit->j_trans_id, commit->j_len); + } else { + reiserfs_warning (stdout, "Yes! (commit block %d) - logged blocks (\n", first_desc_block - 1); +#if 1 + for (j = 0; j < desc->j_len; j ++) { + unsigned long block; + + if (j < JOURNAL_TRANS_HALF) + block = le32_to_cpu (desc->j_realblock[j]); + else + block = le32_to_cpu (commit->j_realblock[i - JOURNAL_TRANS_HALF]); + + if (not_journalable (s, block)) + reiserfs_warning (stdout, " xxxx"); + else { + reiserfs_warning (stdout, " %ld", desc->j_realblock[j]); + if (block_of_bitmap (s, desc->j_realblock[j])) + reiserfs_warning (stdout, "(bmp)"); + } + } +#endif + reiserfs_warning (stdout, "\n"); + } + brelse (c_bh) ; + brelse (d_bh); + } + + reiserfs_warning (stdout, "%d valid transactions found\n", valid_transactions); + + { + struct buffer_head * bh; + struct reiserfs_journal_header * j_head; + + bh = bread (s->s_dev, SB_JOURNAL_BLOCK (s) + rs_journal_size (s->s_rs), + s->s_blocksize); + j_head = (struct reiserfs_journal_header *)(bh->b_data); + + reiserfs_warning (stdout, "#######################\nJournal header:\n" + "j_last_flush_trans_id %ld\n" + "j_first_unflushed_offset %ld\n" + "j_mount_id %ld\n", j_head->j_last_flush_trans_id, j_head->j_first_unflushed_offset, + j_head->j_mount_id); + brelse (bh); + } +} + + +void pack_partition (reiserfs_filsys_t fs); + +static void do_pack (reiserfs_filsys_t fs) +{ + if (opt_block_to_print != -1) + pack_one_block (fs, opt_block_to_print); + else + pack_partition (fs); + +} + +/* FIXME: statistics does not work */ +static void do_dump_tree (reiserfs_filsys_t fs) +{ + if (opt_block_to_print != -1) { + print_one_block (fs, opt_block_to_print); + return; + } + + print_block (stdout, fs, SB_BUFFER_WITH_SB (fs)); + + if (opt_print_journal) + print_journal (fs); + + if (opt_print_objectid_map == 1) + print_objectid_map (stdout, fs); + + if (opt_print_block_map) + print_bmap (stdout, fs, opt_print_block_map == 1 ? 1 : 0); + + if (opt_print_regular_file_content || opt_print_details || + opt_print_leaf_items) { + print_disk_tree (fs, SB_ROOT_BLOCK (fs)); + + /* print the statistic */ + printf ("File system uses %d internal + %d leaves + %d unformatted nodes = %d blocks\n", + g_stat_info.nr_internals, g_stat_info.nr_leaves, g_stat_info.nr_unformatted, + g_stat_info.nr_internals + g_stat_info.nr_leaves + g_stat_info.nr_unformatted); + } +} + +FILE * log; + +static void look_for_key (struct buffer_head * bh, struct key * key) +{ + int i, j; + struct item_head * ih; + struct reiserfs_de_head * deh; + + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + if ((ih->ih_key.k_dir_id == key->k_dir_id || (int)key->k_dir_id == -1) && + ih->ih_key.k_objectid == key->k_objectid) { + reiserfs_warning (log, "%d-th item of block %lu is item of file %K: %H\n", + i, bh->b_blocknr, key, ih); + } + if (!is_direntry_ih (ih)) + continue; + deh = B_I_DEH (bh, ih); + for (j = 0; j < ih_entry_count (ih); j ++, deh ++) { + if ((deh->deh_dir_id == key->k_dir_id || (int)key->k_dir_id == -1) && + deh->deh_objectid == key->k_objectid) { + reiserfs_warning (log, "dir item %d (%H) of block %lu has " + "entry (%d-th) %.*s pointing to %K\n", + i, ih, bh->b_blocknr, j, + name_length (ih, deh, j), name_in_entry (deh, j), key); + } + } + } + return; +} + + +static void look_for_name (struct buffer_head * bh, char * name) +{ + int i, j; + struct item_head * ih; + struct reiserfs_de_head * deh; + int namelen; + char * p; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + if (!is_direntry_ih (ih)) + continue; + deh = B_I_DEH (bh, ih); + for (j = 0; j < ih_entry_count (ih); j ++, deh ++) { + p = name_in_entry (deh, j); + namelen = name_length (ih, deh, j); + if (namelen == strlen (name) && !strncmp (name, p, namelen)) { + fprintf (log, "block %lu, item %d, entry %d is %s\n", bh->b_blocknr, i, j, name);fflush (log); + } + } + } + return; +} + + + + +static void do_scan (reiserfs_filsys_t fs) +{ + unsigned long i; + struct buffer_head * bh; + int type; + char * answer = 0; + size_t n = 0; + struct key key = {0, 0, }; + unsigned long done, total; + reiserfs_bitmap_t bitmap; + + + bitmap = reiserfs_bitmap_load (".bitmap"); + total = reiserfs_bitmap_ones (bitmap); +/* + bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_fetch_disk_bitmap (bitmap, fs); +*/ + + log = fopen ("scan.log", "w+"); + + if (mode == DO_SCAN_FOR_NAME) { + printf ("What name do you want to look for?"); + getline (&answer, &n, stdin); + answer [strlen (answer) - 1] = 0; + printf ("Looking for name \"%s\"..\n", answer); + key.k_dir_id = 1; + } else { + printf ("What key do you want to find: dirid?"); + getline (&answer, &n, stdin); + key.k_dir_id = atoi (answer); + printf ("objectid?"); + getline (&answer, &n, stdin); + key.k_objectid = atoi (answer); + printf ("looking for (%u %u)\n", key.k_dir_id, key.k_objectid); + } + + done = 0; + for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) { + if (!reiserfs_bitmap_test_bit (bitmap, i)) + continue; + bh = bread (fs->s_dev, i, fs->s_blocksize); + if (!bh) { + printf ("could not read block %lu\n", i); + continue; + } + type = who_is_this (bh->b_data, bh->b_size); + switch (type) { + case THE_JDESC: + if (!key.k_dir_id) + printf ("block %lu is journal descriptor\n", i); + break; + case THE_SUPER: + if (!key.k_dir_id) + printf ("block %lu is reiserfs super block\n", i); + break; + case THE_INTERNAL: + if (!key.k_dir_id) + printf ("block %lu is reiserfs internal node\n", i); + break; + case THE_LEAF: + if (mode == DO_SCAN_FOR_NAME) { + look_for_name (bh, answer); + } else if (key.k_dir_id) { + look_for_key (bh, &key); + } else { + printf ("block %lu is reiserfs leaf node\n", i); + } + break; + } + brelse (bh); + print_how_far (&done, total, 1, 0); + } +} + + + +#if 0 +static void do_test (reiserfs_filsys_t fs, unsigned long block) +{ + struct buffer_head * bh; + + fprintf (stderr, "=========== BLock %lu ============\n", block); + bh = bread (fs->s_dev, block, fs->s_blocksize); + if (!bh) + die ("do_test: bread failed"); + if (is_leaf_bad (bh)) + fprintf (stderr, "\n######### BAD before repairing ############\n"); + else + fprintf (stderr, "\n========= OK before repairing ==================\n"); + print_block (fs, bh, 3, -1, -1); + + fprintf (stderr, "\n>>> repairing >>>>........\n\n"); + + /* function to test */pass0_correct_leaf (fs, bh); + if (is_leaf_bad (bh)) + fprintf (stderr, "\n######### still BAD after repairing ############\n"); + else + fprintf (stderr, "\n========= OK after repairing ==================\n"); + print_block (fs, bh, 3, -1, -1); + brelse (bh); +} +#endif + +static void do_test (reiserfs_filsys_t fs) +{ + struct key root_dir_key = {REISERFS_ROOT_PARENT_OBJECTID, + REISERFS_ROOT_OBJECTID, {{0, 0},}}; + int gen_counter; + + if (reiserfs_find_entry (fs, &root_dir_key, "lost+found", &gen_counter)) + reiserfs_add_entry (fs, &root_dir_key, "lost+found", &root_dir_key, 0); +} + + +/* FIXME: need to open reiserfs filesystem first */ +int main (int argc, char * argv[]) +{ + char * file_name; + int error; + + print_banner ("debugreiserfs"); + + file_name = parse_options (argc, argv); + + fs = reiserfs_open (file_name, O_RDONLY, &error, 0); + if (!fs) { + fprintf (stderr, "\n\ndumpreiserfs: can not open reiserfs on \"%s\": %s\n\n", + file_name, error ? strerror (error) : "there is no one"); + return 0; + } + + switch (mode) { + case DO_PACK: + case DO_PACK_ALL: + do_pack (fs); + break; + + case DO_CORRUPT: + reiserfs_reopen (fs, O_RDWR); + do_corrupt_one_block (fs, opt_block_to_corrupt); + break; + + case DO_DUMP: + do_dump_tree (fs); + break; + + case DO_SCAN: + case DO_SCAN_FOR_NAME: + do_scan (fs); + break; + + case DO_RECOVER: + do_recover (fs); + break; + + case DO_TEST: + { + do_test (fs); + +#if 0 + int i; + int arr[] = {53033}; + + if (opt_block_to_print != -1) { + do_test (fs, opt_block_to_print); + break; + } + +bad blocks found on Joop partition + +53033, +179201, +844702, +844913, +877768, +879067, +907631, +925323, +2241275, +2241343, +2241397, +2241511, +2241553, +2241635, +2241644, +2241654, +2241711, +2241721, +2241727, +2241740, +2241762, +2241766, +2241770, +2241812, +2241820, +2241827, +2241831, +2241878, +2241886, +2241971 + }; + /* blocks containing broken directory items on Joop's filesystem */ + int arr[] = {/*53033,*/ 838396/*, 1597036*//*, 1919589, 2715962*/}; + + + + for (i = 0; i < sizeof (arr) / sizeof (arr[0]); i ++) + do_test (fs, arr[i]); + break; +#endif + + } + } + + reiserfs_close (fs); + return 0; +} diff --git a/debugreiserfs/debugreiserfs.h b/debugreiserfs/debugreiserfs.h new file mode 100644 index 0000000..2d754b5 --- /dev/null +++ b/debugreiserfs/debugreiserfs.h @@ -0,0 +1,135 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <time.h> +#include <malloc.h> +#include <sys/types.h> +#include <asm/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "io.h" +#include "misc.h" +#include "reiserfs_lib.h" +#include "../version.h" + + + +/* + * modes + */ +#define DO_DUMP 1 /* not a real dump, just printing to stdout contents of + tree nodes */ +#define DO_CORRUPT 2 /* used to make filesystem corruption and then test fsck */ +#define DO_SCAN 3 +#define DO_SCAN_FOR_NAME 4 +#define DO_RECOVER 5 +#define DO_TEST 6 +#define DO_PACK 7 /* -p extract meta data of reiserfs filesystem */ +#define DO_PACK_ALL 8 /* -p */ + +extern int opt_quiet; +extern int mode; + + +// the leaf is stored in compact form: +// start magic number +// block number __u32 +// item number __u16 +// struct packed_item +// .. +// end magic number + +/* we store hash code in high byte of 16 bits */ +#define LEAF_START_MAGIC 0xa5 +#define LEAF_END_MAGIC 0x5a + + + +#define FULL_BLOCK_START_MAGIC 0xb6 +#define FULL_BLOCK_END_MAGIC 0x6b +#define UNFORMATTED_BITMAP_START_MAGIC 0xc7 +#define UNFORMATTED_BITMAP_END_MAGIC 0x7c +#define END_MAGIC 0x8d +#define INTERNAL_START_MAGIC +#define INTERNAL_START_MAGIC + + +#define ITEM_START_MAGIC 0x476576 +#define ITEM_END_MAGIC 0x2906504 + +/* flags in packed item mask */ +#define NEW_FORMAT 1 // 0 here means - old format, 1 - new format +#define DIR_ID 2 +#define OBJECT_ID 4 +#define OFFSET_BITS_32 8 +#define OFFSET_BITS_64 16 +#define ENTRY_COUNT 32 // shows whether ih_free_space/ih_entry_count is stored + +#define INDIRECT_ITEM 64 +#define DIRENTRY_ITEM 128 +#define DIRECT_ITEM 256 +#define STAT_DATA_ITEM 512 + +#define ITEM_BODY 1024 +#define WHOLE_INDIRECT 128 +#define WITH_SD_FIRST_DIRECT_BYTE 8192 /* for old stat data first_direct_byte + is stored */ +#define NLINK_BITS_32 8192 /* nlinks stored in 32 bits */ +#define SIZE_BITS_64 16384 /* size has to be stored in 64 bit */ + +struct packed_item { + /*__u16 length;*/ // length of the area we store item in + __u16 mask; // what is stored: dirid, objectid, 32 bit offset or 64 bit offset, type + __u16 item_len; +}; + +#define HAS_DIR_ID 1 +#define HAS_GEN_COUNTER 2 +#define HAS_STATE 4 +#define YURA 8 +#define TEA 16 +#define R5 32 +struct packed_dir_entry { + __u8 mask; + __u16 entrylen; +}; + + +#define fread8(pv) fread (pv, sizeof (__u8), 1, stdin) +#define fread16(pv) fread (pv, sizeof (__u16), 1, stdin) +#define fread32(pv) fread (pv, sizeof (__u32), 1, stdin) +#define fread64(pv) fread (pv, sizeof (__u64), 1, stdin) + +#define fwrite8(pv) {\ +if (fwrite (pv, sizeof (__u8), 1, stdout) != 1)\ + reiserfs_panic ("fwrite8 failed: %m");\ +} +#define fwrite16(pv) {\ +if (fwrite (pv, sizeof (__u16), 1, stdout) != 1)\ + reiserfs_panic ("fwrite16 failed: %m");\ +} +#define fwrite32(pv) {\ +if (fwrite (pv, sizeof (__u32), 1, stdout) != 1)\ + reiserfs_panic ("fwrite32 failed: %m");\ +} +#define fwrite64(pv) {\ +if (fwrite (pv, sizeof (__u64), 1, stdout) != 1)\ + reiserfs_panic ("fwrite64 failed: %m");\ +} +/* +#define fwrite16(pv) fwrite (pv, sizeof (__u16), 1, stdout) +#define fwrite32(pv) fwrite (pv, sizeof (__u32), 1, stdout) +#define fwrite64(pv) fwrite (pv, sizeof (__u64), 1, stdout) +*/ + + +#define BLOCKS_PER_READ 8 +extern char * where_to_save; diff --git a/debugreiserfs/pack.c b/debugreiserfs/pack.c new file mode 100644 index 0000000..e40b204 --- /dev/null +++ b/debugreiserfs/pack.c @@ -0,0 +1,765 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include "debugreiserfs.h" + + + + + +reiserfs_bitmap_t what_to_pack; +reiserfs_bitmap_t what_packed; + +int packed_leaves, bad_leaves, full_blocks, internals, descs, full_of_journal; + + +/* these are to calculate compression */ +unsigned long sent; /* how many bytes sent to stdout */ +unsigned long had_to_be_sent; /* how many bytes were to be sent */ + + + +static void pack_key (struct packed_item * pi, struct item_head * ih) +{ + if (pi->mask & DIR_ID) { + fwrite32 (&ih->ih_key.k_dir_id); + sent += sizeof (__u32); + } + + if (pi->mask & OBJECT_ID) { + fwrite32 (&ih->ih_key.k_objectid); + sent += sizeof (__u32); + } + + if (pi->mask & OFFSET_BITS_64) { + __u64 offset; + + offset = get_offset (&ih->ih_key); + fwrite64 (&offset); + sent += sizeof (__u64); + } + + if (pi->mask & OFFSET_BITS_32) { + __u32 offset; + + offset = get_offset (&ih->ih_key); + fwrite32 (&offset); + sent += sizeof (__u32); + } +} + + +static void pack_direct (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + pi->mask |= DIRECT_ITEM; + + /* send packed item header to stdout */ + fwrite (pi, sizeof (*pi), 1, stdout); + sent += sizeof (*pi); + + /* send key components which are to be sent */ + pack_key (pi, ih); +} + + +/* if there is at least one extent longer than 2 - it is worth packing */ +static int should_pack_indirect (__u32 * ind_item, int unfm_num) +{ + int i, len; + + for (i = 1, len = 1; i < unfm_num; i ++) { + if ((!ind_item [i] && !ind_item [i - 1]) || /* hole continues */ + ind_item [i] == ind_item [i - 1] + 1) { /* subsequent blocks */ + len ++; + if (len > 2) + return 1; + } else { + /* sequence of blocks or hole broke */ + len = 1; + } + } + return 0; +} + + +/* indirect item can be either packed using "extents" (when it is + worth doing) or be stored as is. Size of item in packed form is not + stored. Unpacking will stop when full item length is reached */ +static void pack_indirect (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + int i; + __u32 * ind_item; + __u16 len; + + pi->mask |= INDIRECT_ITEM; + if (ih_entry_count (ih)) + pi->mask |= ENTRY_COUNT; + + ind_item = (__u32 *)B_I_PITEM (bh, ih); + if (!should_pack_indirect (ind_item, I_UNFM_NUM (ih))) + pi->mask |= WHOLE_INDIRECT; + + /* send packed item header to stdout */ + fwrite (pi, sizeof (*pi), 1, stdout); + sent += sizeof (*pi); + + /* send key components which are to be sent */ + pack_key (pi, ih); + + if (pi->mask & ENTRY_COUNT) { + __u16 ih_free_space; + + ih_free_space = ih_entry_count (ih); + fwrite16 (&ih_free_space); + sent += sizeof (__u16); + } + + if (pi->mask & WHOLE_INDIRECT) { + fwrite (ind_item, ih_item_len (ih), 1, stdout); + sent += ih_item_len (ih); + return; + } + + fwrite32 (&ind_item [0]); + sent += sizeof (__u32); + for (i = 1, len = 1; i < I_UNFM_NUM (ih); i ++) { + if ((!ind_item [i] && !ind_item [i - 1]) || /* hole continues */ + ind_item [i] == ind_item[ i - 1] + 1) { /* subsequent blocks */ + len ++; + } else { + fwrite16 (&len); + fwrite32 (&ind_item[i]); + sent += (sizeof (__u32) + sizeof (__u16)); + len = 1; + } + } + fwrite16 (&len); + sent += sizeof (__u16); + + return; +} + + +/* directory item is packed: + entry count - 16 bits + for each entry + mask (8 bits) - it shows whether there are any of (deh_dir_id, gen counter, deh_state) + entry length 16 bits + entry itself + deh_objectid - 32 bits + maybe deh_dir_id (32 bits) + maybe gencounter (16) + maybe deh_state (16) +*/ +static void pack_direntry (reiserfs_filsys_t fs, struct packed_item * pi, + struct buffer_head * bh, + struct item_head * ih) +{ + int i; + struct reiserfs_de_head * deh; + struct packed_dir_entry pe; + __u16 entry_count, gen_counter; + + + pi->mask |= (DIRENTRY_ITEM | ENTRY_COUNT); + + /* send packed item header to stdout */ + fwrite (pi, sizeof (*pi), 1, stdout); + sent += sizeof (*pi); + + /* send key components which are to be sent */ + pack_key (pi, ih); + + /* entry count is sent unconditionally */ + entry_count = ih_entry_count (ih); + fwrite16 (&entry_count); + + deh = B_I_DEH (bh, ih); + for (i = 0; i < entry_count; i ++, deh ++) { + pe.entrylen = entry_length (ih, deh, i); + pe.mask = 0; + if (deh_dir_id (deh) != le32_to_cpu (ih->ih_key.k_objectid)) + /* entry points to name of another directory, store deh_dir_id */ + pe.mask |= HAS_DIR_ID; + + gen_counter = GET_GENERATION_NUMBER (deh_offset (deh)); + if (gen_counter != 0) + /* store generation counter if it is != 0 */ + pe.mask |= HAS_GEN_COUNTER; + + if (le16_to_cpu (deh->deh_state) != 4) + /* something unusual in deh_state. Store it */ + pe.mask |= HAS_STATE; + + fwrite8 (&pe.mask); + fwrite16 (&pe.entrylen); + fwrite (name_in_entry (deh, i), pe.entrylen, 1, stdout); + fwrite32 (&(deh->deh_objectid)); + sent += (sizeof (__u8) + sizeof (__u16) + pe.entrylen + sizeof (__u32)); + + if (pe.mask & HAS_DIR_ID) { + fwrite32 (&deh->deh_dir_id); + sent += sizeof (__u32); + } + + if (pe.mask & HAS_GEN_COUNTER) { + fwrite16 (&gen_counter); + sent += sizeof (__u16); + } + + if (pe.mask & HAS_STATE) { + fwrite16 (&deh->deh_state); + sent += sizeof (__u16); + } + } +} + + +static void pack_stat_data (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + pi->mask |= STAT_DATA_ITEM; + + if (stat_data_v1 (ih)) { + /* for old stat data: we take + mode - 16 bits + nlink - 16 bits + size - 32 bits + blocks/rdev - 32 bits + maybe first_direct byte 32 bits + */ + struct stat_data_v1 * sd_v1; + + sd_v1 = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + if (sd_v1->sd_first_direct_byte != 0xffffffff) + pi->mask |= WITH_SD_FIRST_DIRECT_BYTE; + + /* we are done with packed_item send packed it to stdout */ + fwrite (pi, sizeof (*pi), 1, stdout); + sent += sizeof (*pi); + + /* send key components which are to be sent */ + pack_key (pi, ih); + + fwrite16 (&sd_v1->sd_mode); + fwrite16 (&sd_v1->sd_nlink); + fwrite32 (&sd_v1->sd_size); + fwrite32 (&sd_v1->u.sd_blocks); + sent += (sizeof (__u16) * 2 + sizeof (__u32) * 2); + if (pi->mask & WITH_SD_FIRST_DIRECT_BYTE) { + fwrite32 (&sd_v1->sd_first_direct_byte); + sent += sizeof (__u32); + } + } else { + /* for new stat data + mode - 16 bits + nlink in either 16 or 32 bits + size in either 32 or 64 bits + blocks - 32 bits + */ + struct stat_data * sd; + __u16 nlink16; + __u32 nlink32, size32; + __u64 size64; + + sd = (struct stat_data *)B_I_PITEM (bh, ih); + if (sd->sd_nlink > 0xffff) { + pi->mask |= NLINK_BITS_32; + nlink32 = sd->sd_nlink; + } else { + nlink16 = sd->sd_nlink; + } + if (sd->sd_size > 0xffffffff) { + pi->mask |= SIZE_BITS_64; + size64 = sd->sd_size; + } else { + size32 = sd->sd_size; + } + + /* we are done with packed_item send packed it to stdout */ + fwrite (pi, sizeof (*pi), 1, stdout); + sent += sizeof (*pi); + + /* send key components which are to be sent */ + pack_key (pi, ih); + + fwrite16 (&sd->sd_mode); + sent += sizeof (__u16); + if (pi->mask & NLINK_BITS_32) { + fwrite32 (&nlink32); + sent += sizeof (__u32); + } else { + fwrite16 (&nlink16); + sent += sizeof (__u16); + } + + if (pi->mask & SIZE_BITS_64) { + fwrite64 (&size64); + sent += sizeof (__u64); + } else { + fwrite32 (&size32); + sent += sizeof (__u32); + } + + fwrite32 (&sd->sd_blocks); + sent += sizeof (__u32); + } +} + + +static void pack_full_block (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + __u16 magic; + __u32 block; + + magic = FULL_BLOCK_START_MAGIC; + fwrite16 (&magic); + + block = bh->b_blocknr; + fwrite32 (&block); + + fwrite (bh->b_data, 4096, 1, stdout); + sent += 4096; + had_to_be_sent += 4096; + + full_blocks ++; + + if (who_is_this (bh->b_data, bh->b_size) == THE_JDESC) + descs ++; + if (who_is_this (bh->b_data, bh->b_size) == THE_INTERNAL) + internals ++; + if (block_of_journal (fs, bh->b_blocknr)) + full_of_journal ++; +} + + +/* unformatted node pointer is considered bad when it points either to blocks + of journal, bitmap blocks, super block or is transparently out of range of + disk block numbers */ +static int check_unfm_ptr (reiserfs_filsys_t fs, __u32 block) +{ + if (block >= SB_BLOCK_COUNT (fs)) + return 1; + + if (not_data_block (fs, block)) + return 1; + + return 0; +} + + +/* we only pack leaves which do not have any corruptions */ +static int can_pack_leaf (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + if (is_it_bad_item (fs, ih, B_I_PITEM (bh, ih), check_unfm_ptr, 1/*bad dir*/)) + return 0; + } + return 1; +} + + +/* pack leaf only if all its items are correct: keys are correct, + direntries are hashed properly and hash function is defined, + indirect items are correct, stat data ?, */ +static void pack_leaf (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + int i; + struct item_head * ih; + struct packed_item pi; + __u16 v16; + + if (!can_pack_leaf (fs, bh)) { + /* if something looks suspicious in this leaf - pack whole block */ + pack_full_block (fs, bh); + fprintf (stderr, "leaf %lu is bad\n", bh->b_blocknr); + bad_leaves ++; + return; + } + + /* start magic in low 8 bits, hash code in high 8 bits */ + v16 = (LEAF_START_MAGIC | (func2code (fs->s_hash_function) << 8)); + fwrite16 (&v16); + + /* block number */ + fwrite32 (&bh->b_blocknr); + + /* item number */ + v16 = node_item_number (bh); + fwrite16 (&v16); + + ih = B_N_PITEM_HEAD (bh, 0); + + for (i = 0; i < node_item_number (bh); i ++, ih ++) { +#if 0 + v32 = ITEM_START_MAGIC; + fwrite32 (&v32); +#endif + + pi.mask = 0; + pi.item_len = ih_item_len (ih); + + // format + if (ih_key_format (ih) == KEY_FORMAT_2) + pi.mask |= NEW_FORMAT; + + // k_dir_id + if (!i || (i && ih->ih_key.k_dir_id != (ih - 1)->ih_key.k_dir_id)) { + /* if item is first in the leaf or if previous item has different + k_dir_id - store it */ + pi.mask |= DIR_ID; + } + // k_object_id + if (!i || (i && ih->ih_key.k_objectid != (ih - 1)->ih_key.k_objectid)) { + /* if item is first in the leaf or if previous item has different + k_objectid - store it */ + pi.mask |= OBJECT_ID; + } + + /* store offset if it is != 0 in 32 or 64 bits */ + if (get_offset (&ih->ih_key)) { + if (get_offset (&ih->ih_key) > 0xffffffffULL) + pi.mask |= OFFSET_BITS_64; + else + pi.mask |= OFFSET_BITS_32; + } + + if (is_direct_ih (ih)) { + pack_direct (&pi, bh, ih); + } else if (is_indirect_ih (ih)) + pack_indirect (&pi, bh, ih); + else if (is_direntry_ih (ih)) + pack_direntry (fs, &pi, bh, ih); + else if (is_stat_data_ih (ih)) + pack_stat_data (&pi, bh, ih); + else + die ("pack_leaf: unknown item found"); +#if 0 + v32 = ITEM_END_MAGIC; + fwrite32 (&v32); +#endif + } + + v16 = LEAF_END_MAGIC; + fwrite16 (&v16); + + packed_leaves ++; + had_to_be_sent += 4096; + + return; +} + + +static int can_pack_internal (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + return 0; +} + + +/* pack internal node as a full block */ +static void pack_internal (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + if (!can_pack_internal (fs, bh)) { + pack_full_block (fs, bh); + return; + } + + reiserfs_panic ("pack_internal: packing code is not ready"); +} + + +static int how_many_to_pack (reiserfs_filsys_t fs, unsigned long first, int count) +{ + int i; + int used; + + used = 0; + for (i = 0; i < count; i ++) { + if ((SB_BLOCK_COUNT (fs) > (first + i)) && + reiserfs_bitmap_test_bit (what_to_pack, first + i)) + used ++; + } + return used; +} + + +/* packed blocks are marked free in the bitmap*/ +static void send_block (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + int type; + + + if ((type = who_is_this (bh->b_data, bh->b_size)) != THE_LEAF) { + if (type == THE_INTERNAL) { + pack_internal (fs, bh); + } else if (!not_data_block (fs, bh->b_blocknr)) { + /* unformatted */ + return; + } else + /* bitmaps, super block, blocks of journal - not leaves */ + pack_full_block (fs, bh); + } else + pack_leaf (fs, bh); + + reiserfs_bitmap_set_bit (what_packed, bh->b_blocknr); + reiserfs_bitmap_clear_bit (what_to_pack, bh->b_blocknr); +} + + +/* super block, journal, bitmaps */ +static void pack_frozen_data (reiserfs_filsys_t fs) +{ + int i; + struct buffer_head * bh; + + /* super block */ + reiserfs_warning (stderr, "super block..");fflush (stderr); + send_block (fs, fs->s_sbh); + reiserfs_warning (stderr, "ok\nbitmaps..(%d).. ", SB_BMAP_NR (fs)); + fflush (stderr); + + /* bitmaps */ + for (i = 0; i < SB_BMAP_NR (fs); i ++) { + send_block (fs, SB_AP_BITMAP (fs)[i]); + } + + reiserfs_warning (stderr, "ok\njournal (from %lu to %lu)..", + rs_journal_start (fs->s_rs), + rs_journal_start (fs->s_rs) + rs_journal_size (fs->s_rs)); + fflush (stderr); + /* journal */ + for (i = rs_journal_start (fs->s_rs); + i <= rs_journal_start (fs->s_rs) + rs_journal_size (fs->s_rs); + i ++) { + bh = bread (fs->s_dev, i, fs->s_blocksize); + send_block (fs, bh); + brelse (bh); + } + reiserfs_warning (stderr, "ok\n");fflush (stderr); +} + + +/* pack all "not data blocks" and correct leaf */ +void pack_partition (reiserfs_filsys_t fs) +{ + int i, j; + struct buffer_head tmp, * bh; + int nr_to_read = BLOCKS_PER_READ; + __u32 magic32; + __u16 blocksize; + __u16 magic16; + unsigned long done = 0, total; + + + magic32 = REISERFS_SUPER_MAGIC; + fwrite32 (&magic32); + + blocksize = fs->s_blocksize; + fwrite16 (&blocksize); + + tmp.b_size = blocksize; + + /* will save information about what packed here */ + what_packed = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + + /* will get information about what is to be packed */ + what_to_pack = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + if (!what_to_pack) + die ("pack_partition: could not create bitmap"); + if (mode == DO_PACK) { + /* read blocks marked used and pack them */ + reiserfs_fetch_disk_bitmap (what_to_pack, fs); + reiserfs_warning (stderr, "Packing blocks marked used on the device %d\n", + reiserfs_bitmap_ones (what_to_pack)); + } else { + reiserfs_bitmap_fill (what_to_pack); + reiserfs_warning (stderr, "Packing all blocks of the device %d\n", + reiserfs_bitmap_ones (what_to_pack)); + } + + + /* super block, journal, bitmaps */ + pack_frozen_data (fs); + + reiserfs_warning (stderr, + "Super block, bitmaps, journal - %d blocks - done, %d blocks left\n", + reiserfs_bitmap_ones (what_packed), reiserfs_bitmap_ones (what_to_pack)); + total = reiserfs_bitmap_ones (what_to_pack); + + for (i = 0; i < SB_BLOCK_COUNT (fs); i += nr_to_read) { + int to_pack; + + to_pack = how_many_to_pack (fs, i, nr_to_read); + if (to_pack) { + print_how_far (&done, total, to_pack, opt_quiet); + + bh = bread (fs->s_dev, i / nr_to_read, blocksize * nr_to_read); + if (bh) { + for (j = 0; j < nr_to_read; j ++) { + if (reiserfs_bitmap_test_bit (what_to_pack, i + j)) { + tmp.b_data = bh->b_data + j * tmp.b_size; + tmp.b_blocknr = i + j; + send_block (fs, &tmp); + } + } + brelse (bh); + } else { + /* bread failed */ + if (nr_to_read != 1) { + /* we tryied to read bunch of blocks. Try to read them by one */ + nr_to_read = 1; + i --; + continue; + } else { + /* we were reading one block at time, and failed, so mark + block bad */ + reiserfs_warning (stderr, "could not read block %lu\n", i); + } + } + + } + } + + magic16 = END_MAGIC; + fwrite16 (&magic16); + + fprintf (stderr, "Packed\n\tleaves %d\n" + "\tfull blocks %d\n" + "\t\tof journal %d\n" + "\t\tcorrupted leaves %d\n" + "\t\tinternals %d\n" + "\t\tdescriptors %d\n", + packed_leaves, full_blocks, full_of_journal, bad_leaves, internals, descs); + fprintf (stderr, "data packed with ratio %.2f\n", (double)sent / had_to_be_sent); + + if (where_to_save) + reiserfs_bitmap_save (where_to_save, what_packed); +} + + + + +void pack_one_block (reiserfs_filsys_t fs, unsigned long block) +{ + __u32 magic32; + __u16 magic16; + struct buffer_head * bh; + + // reiserfs magic + magic32 = REISERFS_SUPER_MAGIC; + fwrite32 (&magic32); + + // blocksize + fwrite16 (&fs->s_blocksize); + + bh = bread (fs->s_dev, block, fs->s_blocksize); + + if (who_is_this (bh->b_data, bh->b_size) == THE_LEAF) + pack_leaf (fs, bh); + else + pack_full_block (fs, bh); + + brelse (bh); + + // end magic + magic16 = END_MAGIC; + fwrite16 (&magic16); + + fprintf (stderr, "Packed\n\tleaves %d\n\tfull block %d\n\tcorrupted leaves %d\n", + packed_leaves, full_blocks, bad_leaves); +} + + +#if 0 +// +// this test program has two modes: 'pack file blocknr' +// and 'unpack file' +// in the first mode blocknr-th 4k block of the 'file' will be packed out to stdout +// the the second mode standart input will be converted to the reiserfs leaf on 'file' +// +static int do_unpack (char * file) +{ + char * buf; + int fd; + + fd = open (file, O_RDONLY); + if (fd == -1) { + perror ("open failed"); + return 0; + } + + buf = malloc (4096); + if (!buf) { + perror ("malloc failed"); + return 0; + } + + fread (buf, 4096, 1, stdin); + if (!feof (stdin)) { + printf ("fread returned not eof\n"); + return 0; + } + + unpack_leaf (buf, fd); + + free (buf); + close (fd); + return 0; +} + +static int do_pack (char * file, int block) +{ + int fd; + struct buffer_head * bh; + char * buf; + int len; + + fprintf (stderr, "dumping block %d of the \"%s\"\n", block, file); + + fd = open (file, O_RDONLY); + if (fd == -1) { + perror ("open failed"); + return 0; + } + + bh = bread (fd, block, 4096); + if (!bh) { + fprintf (stderr, "bread failed\n"); + return 0; + } + + if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) { + fprintf (stderr, "block %d is not a leaf\n", block); + return 0; + } + + len = pack_leaf (bh, buf); + fwrite (buf, len, 1, stdout); + + free (buf); + close (fd); + return 0; +} + + +int main (int argc, char ** argv) +{ + if (argc == 3 && !strcmp (argv[1], "unpack")) + return do_unpack (argv[2]); + + if (argc == 4 && !strcmp (argv[1], "pack")) + return do_pack (argv[2], atoi (argv[3])); + + fprintf (stderr, "Usage: \n\t%s pack filename block\nor\n" + "\t%s unpack filename\n", argv[0], argv[0]); + return 0; +} + +#endif diff --git a/debugreiserfs/unpack.c b/debugreiserfs/unpack.c new file mode 100644 index 0000000..c83bae6 --- /dev/null +++ b/debugreiserfs/unpack.c @@ -0,0 +1,558 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include "debugreiserfs.h" +#include <sys/resource.h> + + +#define print_usage_and_exit() die ("Usage: %s [-v] [-b filename] device\n\ +-v prints blocks number of every block unpacked\n\ +-b filename makes unpack to save bitmap of blocks unpacked\n", argv[0]); + + + +/* when super block gets unpacked for the first time - create a bitmap + and mark in it what have been unpacked. Save that bitmap at the end */ +reiserfs_bitmap_t what_unpacked = 0; + + +int unpacked, data_blocks_unpacked; + +int verbose = 0; + + + +static void unpack_offset (struct packed_item * pi, struct item_head * ih) +{ + if (pi->mask & OFFSET_BITS_64) { + __u64 v64; + + if (ih_key_format (ih) != KEY_FORMAT_2) + die ("unpack_offset: key format is not set or wrong"); + fread64 (&v64); + set_offset (KEY_FORMAT_2, &ih->ih_key, v64); + return; + } + + if (pi->mask & OFFSET_BITS_32) { + __u32 v32; + + fread32 (&v32); + set_offset (ih_key_format (ih), &ih->ih_key, v32); + return; + } + + // offset is 0 + return; +} + + +static void unpack_type (struct packed_item * pi, struct item_head * ih) +{ + if (pi->mask & DIRECT_ITEM) + set_type (ih_key_format (ih), &ih->ih_key, TYPE_DIRECT); + else if (pi->mask & STAT_DATA_ITEM) + set_type (ih_key_format (ih), &ih->ih_key, TYPE_STAT_DATA); + else if (pi->mask & INDIRECT_ITEM) + set_type (ih_key_format (ih), &ih->ih_key, TYPE_INDIRECT); + else if (pi->mask & DIRENTRY_ITEM) + set_type (ih_key_format (ih), &ih->ih_key, TYPE_DIRENTRY); + else + reiserfs_panic (0, "%h, mask 0%o\n", ih, pi->mask); +} + + +/* direntry item comes in the following format: + entry count - 16 bits + for each entry + mask - 8 bits + entry length - 16 bits + entry itself + deh_objectid - 32 bits + maybe deh_dir_id (32 bits) + maybe gencounter (16) + maybe deh_state (16) +*/ +static void unpack_direntry (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih, hashf_t hash_func) +{ + __u16 entry_count, namelen, gen_counter, entry_len; + __u8 mask; + int i; + struct reiserfs_de_head * deh; + int location; + char * item; + + if (!hash_func) + die ("unpack_direntry: hash function is not set"); + + fread16 (&entry_count); + set_entry_count (ih, entry_count); + + item = bh->b_data + ih_location (ih); + deh = (struct reiserfs_de_head *)item; + location = pi->item_len; + for (i = 0; i < entry_count; i ++, deh ++) { + fread8 (&mask); + fread16 (&entry_len); + location -= entry_len; + deh->deh_location = location; + fread (item + location, entry_len, 1, stdin); + + /* find name length */ + if (*(item + location + entry_len - 1)) + namelen = entry_len; + else + namelen = strlen (item + location); + + fread32 (&deh->deh_objectid); + if (mask & HAS_DIR_ID) + fread32 (&deh->deh_dir_id); + else + deh->deh_dir_id = ih->ih_key.k_objectid; + if (*(item + location) == '.' && namelen == 1) + /* old or new "." */ + deh->deh_offset = DOT_OFFSET; + else if (*(item + location) == '.' && *(item + location + 1) == '.' && namelen == 2) + /* old or new ".." */ + deh->deh_offset = DOT_DOT_OFFSET; + else + deh->deh_offset = GET_HASH_VALUE (hash_func (item + location, + namelen)); + if (mask & HAS_GEN_COUNTER) { + fread16 (&gen_counter); + deh->deh_offset |= gen_counter; + } + + if (mask & HAS_STATE) + fread16 (&deh->deh_state); + else + deh->deh_state = (1 << DEH_Visible); + } + + return; +} + + +/* struct packed_item is already unpacked */ +static void unpack_stat_data (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + set_entry_count (ih, 0xffff); + + if (ih_key_format (ih) == KEY_FORMAT_1) { + /* stat data comes in the following format: + if this is old stat data: + mode - 16 bits + nlink - 16 bits + size - 32 bits + blocks/rdev - 32 bits + maybe first_direct byte 32 bits + */ + struct stat_data_v1 * sd; + + sd = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + memset (sd, 0, sizeof (sd)); + + fread16 (&sd->sd_mode); + fread16 (&sd->sd_nlink); + fread32 (&sd->sd_size); + fread32 (&sd->u.sd_blocks); + + if (pi->mask & WITH_SD_FIRST_DIRECT_BYTE) { + fread32 (&sd->sd_first_direct_byte); + } else { + sd->sd_first_direct_byte = 0xffffffff; + } + } else { + /* for new stat data + mode - 16 bits + nlink in either 16 or 32 bits + size in either 32 or 64 bits + blocks - 32 bits + */ + struct stat_data * sd; + + sd = (struct stat_data *)B_I_PITEM (bh, ih); + memset (sd, 0, sizeof (sd)); + + fread16 (&sd->sd_mode); + + if (pi->mask & NLINK_BITS_32) { + fread32 (&sd->sd_nlink); + } else { + __u16 nlink16; + + fread16 (&nlink16); + sd->sd_nlink = nlink16; + } + + if (pi->mask & SIZE_BITS_64) { + fread64 (&sd->sd_size); + } else { + __u32 size32; + + fread32 (&size32); + sd->sd_size = size32; + } + + fread32 (&sd->sd_blocks); + } + + return; +} + + +/* indirect item comes either in packed form or as is. ih_free_space + can go first */ +static void unpack_indirect (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + __u32 * ind_item, * end; + int i; + __u16 v16; + + v16 = 0; + if (pi->mask & ENTRY_COUNT) + fread16 (&v16); + + set_entry_count (ih, v16); + + ind_item = (__u32 *)B_I_PITEM (bh, ih); + if (pi->mask & WHOLE_INDIRECT) { + fread (ind_item, pi->item_len, 1, stdin); + return; + } + + end = ind_item + I_UNFM_NUM (ih); + while (ind_item < end) { + fread32 (ind_item); + fread16 (&v16); + for (i = 1; i < v16; i ++) { + if (ind_item[0]) + ind_item [i] = ind_item[0] + i; + else + ind_item [i] = 0; + } + ind_item += i; + } + return; +} + + +// FIXME: we have no way to preserve symlinks +static void unpack_direct (struct packed_item * pi, struct buffer_head * bh, + struct item_head * ih) +{ + set_entry_count (ih, 0xffff); + memset (bh->b_data + ih_location (ih), 'a', pi->item_len); + return; +} + + +static void unpack_leaf (int dev, hashf_t hash_func) +{ + static int unpacked_leaves = 0; + struct buffer_head * bh; + struct packed_item pi; + struct item_head * ih; + int i; + __u16 v16; + __u32 v32; + + /* block number */ + fread32 (&v32); + + + /* item number */ + fread16 (&v16); + + if (verbose) + fprintf (stderr, "leaf %d\n", v32); + + + + bh = getblk (dev, v32, 4096); + if (!bh) + die ("unpack_leaf: getblk failed"); + + set_node_item_number (bh, v16); + set_node_level (bh, DISK_LEAF_NODE_LEVEL); + set_node_free_space (bh, bh->b_size - BLKH_SIZE); + + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < v16; i ++, ih ++) { +#if 0 + fread32 (&v32); + if (v32 != ITEM_START_MAGIC) + die ("unpack_leaf: no start item magic found: block %lu, item %i", + bh->b_blocknr, i); +#endif + + fread (&pi, sizeof (struct packed_item), 1, stdin); + + /* dir_id - if it is there */ + if (pi.mask & DIR_ID) { + fread32 (&v32); + ih->ih_key.k_dir_id = v32; + } else { + if (!i) + die ("unpack_leaf: dir_id is not set"); + ih->ih_key.k_dir_id = (ih - 1)->ih_key.k_dir_id; + } + + /* object_id - if it is there */ + if (pi.mask & OBJECT_ID) { + fread32 (&v32); + ih->ih_key.k_objectid = v32; + } else { + if (!i) + die ("unpack_leaf: object_id is not set"); + ih->ih_key.k_objectid = (ih - 1)->ih_key.k_objectid; + } + + // we need to set item format before offset unpacking + set_key_format (ih, (pi.mask & NEW_FORMAT) ? KEY_FORMAT_2 : KEY_FORMAT_1); + + // offset + unpack_offset (&pi, ih); + + /* type */ + unpack_type (&pi, ih); + + /* item length and item location */ + set_ih_item_len (ih, pi.item_len); + set_ih_location (ih, (i ? ih_location (ih - 1) : bh->b_size) - pi.item_len); + + // item itself + if (is_direct_ih (ih)) { + unpack_direct (&pi, bh, ih); + } else if (is_indirect_ih (ih)) { + unpack_indirect (&pi, bh, ih); + } else if (is_direntry_ih (ih)) { + unpack_direntry (&pi, bh, ih, hash_func); + } else if (is_stat_data_ih (ih)) { + unpack_stat_data (&pi, bh, ih); + } + set_node_free_space (bh, node_free_space (bh) - (IH_SIZE + ih_item_len (ih))); +#if 0 + fread32 (&v32); + if (v32 != ITEM_END_MAGIC) + die ("unpack_leaf: no end item magic found: block %lu, item %i", + bh->b_blocknr, i); +#endif + } + + fread16 (&v16); + if (v16 != LEAF_END_MAGIC) + die ("unpack_leaf: wrong end signature found - %x, block %lu", + v16, bh->b_blocknr); + + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); + /* + if (!not_data_block (bh->b_blocknr)) + data_blocks_unpacked ++; + */ + brelse (bh); + + if (what_unpacked) + reiserfs_bitmap_set_bit (what_unpacked, bh->b_blocknr); + unpacked ++; + + if (!(++ unpacked_leaves % 10)) + fprintf (stderr, "#"); +} + + +static void unpack_full_block (int dev, int blocksize) +{ + static int full_blocks_unpacked = 0; + __u32 block; + struct buffer_head * bh; + + fread32 (&block); + + if (verbose) + fprintf (stderr, "full #%d\n", block); + + bh = getblk (dev, block, blocksize); + if (!bh) + die ("unpack_full_block: getblk failed"); + + fread (bh->b_data, bh->b_size, 1, stdin); + + if (who_is_this (bh->b_data, bh->b_size) == THE_SUPER && !what_unpacked) { + unsigned long blocks; + + blocks = rs_block_count ((struct reiserfs_super_block *)(bh->b_data)); + fprintf (stderr, "There were %lu blocks on the device\n", blocks); + what_unpacked = reiserfs_create_bitmap (blocks); + } + + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); +/* + if (!not_data_block (bh->b_blocknr)) + data_blocks_unpacked ++; +*/ + brelse (bh); + + if (what_unpacked) + reiserfs_bitmap_set_bit (what_unpacked, block); + unpacked ++; + + if (!(++ full_blocks_unpacked % 50)) + fprintf (stderr, "."); +} + + +/* just skip bitmaps of unformatted nodes */ +static void unpack_unformatted_bitmap (int dev, int blocksize) +{ + __u16 bmap_num; + __u32 block_count; + int i; + char * buf; + + fread16 (&bmap_num); + fread32 (&block_count); + + buf = malloc (blocksize); + if (!buf) + reiserfs_panic ("unpack_unformatted_bitmap: malloc failed: %m"); + + for (i = 0; i < bmap_num; i ++) { + if (fread (buf, blocksize, 1, stdin) != 1) + reiserfs_panic ("unpack_unformatted_bitmap: " + "could not read bitmap #%d: %m", i); + } + free (buf); +} + + +// read packed reiserfs partition metadata from stdin +void unpack_partition (int dev) +{ + __u32 magic32; + __u16 magic16; + __u16 blocksize; + + fread32 (&magic32); + if (magic32 != REISERFS_SUPER_MAGIC) + die ("unpack_partition: reiserfs magic number not found"); + + fread16 (&blocksize); + + if (verbose) + fprintf (stderr, "Blocksize %d\n", blocksize); + + while (!feof (stdin)) { + char c[2]; + + fread (c, 1, 1, stdin); + switch (c[0]) { + case '.': + if (verbose) + fprintf (stderr, "\".\" skipped\n"); + continue; + + case '1': + fread (c, 1, 1, stdin); /* that was 100%, read in first 0 */ + case '2': + case '4': + case '6': + case '8': + fread (c, 1, 1, stdin); + case '0': + fread (c + 1, 1, 1, stdin); /* read % */ + + if (c[0] != '0' || c[1] != '%') + die ("0%% expected\n"); + + if (verbose) + fprintf (stderr, "0%% skipped\n"); + continue; + } + + fread (c + 1, 1, 1, stdin); + magic16 = *(__u16 *)c; + /*fread16 (&magic16);*/ + + switch (magic16 & 0xff) { + case LEAF_START_MAGIC: + unpack_leaf (dev, code2func (magic16 >> 8)); + break; + + case FULL_BLOCK_START_MAGIC: + unpack_full_block (dev, blocksize); + break; + + case UNFORMATTED_BITMAP_START_MAGIC: + fprintf (stderr, "\nBitmap of unformatted - ignored\n"); + unpack_unformatted_bitmap (dev, blocksize); + break; + + case END_MAGIC: + break; + + default: + die ("unpack_partition: bad magic found - %x", magic16 & 0xff); + } + } + + fprintf (stderr, "Unpacked %d (%d) blocks\n", unpacked, what_unpacked ? reiserfs_bitmap_ones (what_unpacked) : 0); + + + /* fclose (block_list);*/ +} + + +int main (int argc, char ** argv) +{ + int fd; + int c; + char * filename = ".bitmap"; + struct rlimit lim = {0xffffffff, 0xffffffff}; + + print_banner ("unpack"); + + /* with this 2.4.0-test9's file_write does not send SIGXFSZ */ + if (setrlimit (RLIMIT_FSIZE, &lim)) { + fprintf (stderr, "sertlimit failed: %m\n"); + } + + while ((c = getopt (argc, argv, "vb:")) != EOF) { + switch (c) { + case 'v': + verbose = 1; + case 'b': + asprintf (&filename, "%s", optarg); + break; + } + } + if (optind != argc - 1) + /* only one non-option argument is permitted */ + print_usage_and_exit(); + + if (is_mounted (argv[optind])) + reiserfs_panic ("%s seems mounted, umount it first\n", argv[optind]); + + fd = open (argv[optind], O_RDWR | O_LARGEFILE); + if (fd == -1) { + perror ("open failed"); + return 0; + } + + unpack_partition (fd); + + if (what_unpacked && filename) + reiserfs_bitmap_save (filename, what_unpacked); + + close (fd); + return 0; +} diff --git a/fsck/Makefile.am b/fsck/Makefile.am new file mode 100644 index 0000000..a64e913 --- /dev/null +++ b/fsck/Makefile.am @@ -0,0 +1,11 @@ +sbin_PROGRAMS = reiserfsck + +reiserfsck_SOURCES = main.c pass0.c pass1.c pass2.c semantic.c pass4.c lost+found.c \ +ubitmap.c uobjectid.c ustree.c ufile.c check.c check_tree.c journal.c info.c segments.c\ +fsck.h +man_MANS = reiserfsck.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include -I. diff --git a/fsck/Makefile.in b/fsck/Makefile.in new file mode 100644 index 0000000..1d26e75 --- /dev/null +++ b/fsck/Makefile.in @@ -0,0 +1,358 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +sbin_PROGRAMS = reiserfsck + +reiserfsck_SOURCES = main.c pass0.c pass1.c pass2.c semantic.c pass4.c lost+found.c ubitmap.c uobjectid.c ustree.c ufile.c check.c check_tree.c journal.c info.c segments.c fsck.h + +man_MANS = reiserfsck.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include -I. +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +PROGRAMS = $(sbin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +reiserfsck_OBJECTS = main.o pass0.o pass1.o pass2.o semantic.o pass4.o \ +lost+found.o ubitmap.o uobjectid.o ustree.o ufile.o check.o \ +check_tree.o journal.o info.o segments.o +reiserfsck_LDADD = $(LDADD) +reiserfsck_DEPENDENCIES = ../lib/libmisc.a ../reiserfscore/libcore.a +reiserfsck_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +man8dir = $(mandir)/man8 +MANS = $(man_MANS) + +NROFF = nroff +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(reiserfsck_SOURCES) +OBJECTS = $(reiserfsck_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps fsck/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +reiserfsck: $(reiserfsck_OBJECTS) $(reiserfsck_DEPENDENCIES) + @rm -f reiserfsck + $(LINK) $(reiserfsck_LDFLAGS) $(reiserfsck_OBJECTS) $(reiserfsck_LDADD) $(LIBS) + +install-man8: + $(mkinstalldirs) $(DESTDIR)$(man8dir) + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \ + done + +uninstall-man8: + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \ + rm -f $(DESTDIR)$(man8dir)/$$inst; \ + done +install-man: $(MANS) + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-man8 +uninstall-man: + @$(NORMAL_UNINSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-man8 + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = fsck + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +check.o: check.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +check_tree.o: check_tree.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +info.o: info.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +journal.o: journal.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +lost+found.o: lost+found.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +main.o: main.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h +pass0.o: pass0.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +pass1.o: pass1.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +pass2.o: pass2.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +pass4.o: pass4.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +segments.o: segments.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +semantic.o: semantic.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +ubitmap.o: ubitmap.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +ufile.o: ufile.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +uobjectid.o: uobjectid.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +ustree.o: ustree.c fsck.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-sbinPROGRAMS +install-exec: install-exec-am + +install-data-am: install-man +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-sbinPROGRAMS uninstall-man +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) $(MANS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man8 + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-sbinPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \ +clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ +install-sbinPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile install-man8 uninstall-man8 \ +install-man uninstall-man tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/fsck/check.c b/fsck/check.c new file mode 100644 index 0000000..37dacdd --- /dev/null +++ b/fsck/check.c @@ -0,0 +1,329 @@ +/* + * Copyright 1996, 1997 Hans Reiser + */ + +#include "fsck.h" + + +/* this goes through buffers checking delimiting keys + */ + +struct buffer_head * g_left = 0; +struct buffer_head * g_right = 0; +struct key * g_dkey = 0; + + +static void check_directory_item (struct item_head * ih, struct buffer_head * bh) +{ + int i; + struct reiserfs_de_head * deh; + + for (i = 0, deh = B_I_DEH (bh, ih); i < ih_entry_count (ih) - 1; i ++) + if (deh_offset(&deh[i]) > deh_offset(&deh[i + 1])) + die ("check_directory_item: entries are not sorted properly"); +} + + +static void check_items (struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + for (i = 0; i < B_NR_ITEMS (bh); i++) + { + ih = B_N_PITEM_HEAD (bh, i); + + if (is_direntry_ih (ih)) + check_directory_item (ih, bh); + + } +} + + +static void compare_neighboring_leaves_in_pass1 (void) +{ + struct key * left = B_N_PKEY (g_left, B_NR_ITEMS (g_left) - 1); + + if (comp_keys (left, B_N_PKEY (g_right, 0)) != -1/*SECOND_GREATER*/) + die ("compare_neighboring_leaves_in_pass1: left key is greater, that the right one"); + + if (/*comp_keys (B_PRIGHT_DELIM_KEY (g_left), g_dkey) == FIRST_GREATER ||*/ + comp_keys (g_dkey, B_N_PKEY (g_right, 0))) { + reiserfs_panic (0, "compare_neighboring_leaves_in_pass1: dkey %k, first key in right %k", + g_dkey, B_N_PKEY (g_right, 0)); + } + + check_items (g_left); + + /*&&&&&&&&&&&&&&&&&&&&&&&&&& + for (i = 0, ih = B_N_PITEM_HEAD (g_left, i); i < B_NR_ITEMS (g_left); i ++, ih ++) + if (is_item_accessed (ih) == YES) + die ("compare_neighboring_leaves_in_pass1: item marked as accessed in g_left"); + for (i = 0, ih = B_N_PITEM_HEAD (g_right, i); i < B_NR_ITEMS (g_right); i ++, ih ++) + if (is_item_accessed (ih) == YES) + die ("compare_neighboring_leaves_in_pass1: item marked as accessed in g_right"); + &&&&&&&&&&&&&&&&&&&&&&&&&&&*/ + +} + + +static void is_there_unaccessed_items (struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + if (is_objectid_used (fs, ih->ih_key.k_objectid) == 0) + die ("is_there_unaccessed_items: %lu is not marked as used", ih->ih_key.k_objectid); + + if (!is_item_reachable (ih)) { + die ("is_there_unaccessed_items: block %lu - unaccessed item found", + bh->b_blocknr); + } + } +} + + +static void compare_neighboring_leaves_after_all (void) +{ + struct item_head * left = B_N_PITEM_HEAD(g_left, B_NR_ITEMS (g_left) - 1); + struct item_head * right = B_N_PITEM_HEAD(g_right, 0); + /* struct key * left = B_N_PKEY (g_left, B_NR_ITEMS (g_left) - 1); + struct key * right = B_N_PKEY (g_right, 0);*/ + + /* + if (comp_keys (&left->ih_key, B_PRIGHT_DELIM_KEY (g_left)) != SECOND_GREATER) + die ("compare_neighboring_leaves_after_all: invalid right delimiting key"); + */ + if (comp_keys (&left->ih_key, B_N_PKEY (g_right, 0)) != -1/*SECOND_GREATER*/) + die ("compare_neighboring_leaves_after_all: left key is greater than the right one"); + + if (//comp_le_keys (B_PRIGHT_DELIM_KEY (g_left), g_dkey) != KEYS_IDENTICAL || + comp_keys (g_dkey, B_N_PKEY (g_right, 0))) { + reiserfs_panic (0, "compare_neighboring_leaves_after all: invalid delimiting keys from left to right (%k %k)", + g_dkey, B_N_PKEY (g_right, 0)); + } + + if (!not_of_one_file (&left->ih_key, &right->ih_key)) { + // items of one file: check offset correctness + if (is_direct_ih (left) || is_indirect_ih (left)) + //if (get_offset(&right->ih_key) != get_offset(&left->ih_key) + get_bytes_number (g_left, left /*B_NR_ITEMS (g_left) - 1*/, 0, CHECK_FREE_BYTES)) + if (get_offset(&right->ih_key) != get_offset(&left->ih_key) + get_bytes_number (left, g_left->b_size)) + die ("compare_neighboring_leaves_after all: hole between items or items are overlapped"); + } + is_there_unaccessed_items (g_left); + +} + + +typedef void (check_function_t)(void); + + +/* only directory item can be fatally bad */ +static int is_leaf_bad_xx (struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + if (!is_leaf_node (bh)) + return 0; + for (i = 0, ih = B_N_PITEM_HEAD (bh, 0); i < B_NR_ITEMS (bh); i ++, ih ++) + if (is_bad_item (bh, ih, B_I_PITEM (bh, ih))) { + return 1; + } + return 0; +} + + +static void reiserfsck_check_tree (int dev, int block, int size, check_function_t comp_func) +{ + struct buffer_head * bh; + int what_node; + + bh = bread (dev, block, size); + if (bh == 0) + reiserfs_panic("reiserfsck_check_tree: unable to read %lu block on device 0x%x\n", + block, dev); + + if (!B_IS_IN_TREE (bh)) { + reiserfs_panic (0, "reiserfsck_check_tree: buffer (%b %z) not in tree", bh, bh); + } + + what_node = who_is_this (bh->b_data, bh->b_size); + if (what_node != THE_LEAF && what_node != THE_INTERNAL) + die ("Not formatted node"); + + if (!is_block_used (bh->b_blocknr)) + die ("Not marked as used"); + + if (is_leaf_node (bh) && is_leaf_bad_xx (bh)) + die ("Bad leaf"); + + if (is_internal_node(bh) && is_internal_bad (bh)) + die ("bad internal"); + + if (is_internal_node (bh)) { + int i; + struct disk_child * dc; + + dc = B_N_CHILD (bh, 0); + for (i = 0; i <= B_NR_ITEMS (bh); i ++, dc ++) { + reiserfsck_check_tree (dev, dc->dc_block_number, size, comp_func); + g_dkey = B_N_PDELIM_KEY (bh, i); + } + } else if (is_leaf_node (bh)) { + g_right = bh; + if (g_left != 0 && g_dkey != 0) { + comp_func (); + brelse (g_left); + } + g_left = g_right; + return; + } else { + reiserfs_panic ("reiserfsck_check_tree: block %lu has bad block type (%b)", + bh->b_blocknr, bh); + } + brelse (bh); +} + +static void reiserfsck_check_cached_tree (int dev, int block, int size) +{ + struct buffer_head * bh; + int what_node; + + bh = find_buffer(dev, block, size); + if (bh == 0) + return; + if (!buffer_uptodate (bh)) { + die ("reiserfsck_check_cached_tree: found notuptodate buffer"); + } + + bh->b_count ++; + + if (!B_IS_IN_TREE (bh)) { + die ("reiserfsck_check_cached_tree: buffer (%b %z) not in tree", bh, bh); + } + + what_node = who_is_this (bh->b_data, bh->b_size); + if ((what_node != THE_LEAF && what_node != THE_INTERNAL) || + !is_block_used (bh->b_blocknr) || + (is_leaf_node (bh) && is_leaf_bad (bh)) || + (is_internal_node(bh) && is_internal_bad (bh))) + die ("reiserfsck_check_cached_tree: bad node in the tree"); + if (is_internal_node (bh)) { + int i; + struct disk_child * dc; + + dc = B_N_CHILD (bh, 0); + for (i = 0; i <= B_NR_ITEMS (bh); i ++, dc ++) { + reiserfsck_check_cached_tree (dev, dc->dc_block_number, size); + g_dkey = B_N_PDELIM_KEY (bh, i); + } + } else if (is_leaf_node (bh)) { + brelse (bh); + return; + } else { + reiserfs_panic ("reiserfsck_check_cached_tree: block %lu has bad block type (%b)", + bh->b_blocknr, bh); + } + brelse (bh); +} + + +void reiserfsck_tree_check (check_function_t how_to_compare_neighbors) +{ + g_left = 0; + g_dkey = 0; + reiserfsck_check_tree (fs->s_dev, SB_ROOT_BLOCK(fs), fs->s_blocksize, how_to_compare_neighbors); + brelse (g_right); +} + + +void reiserfsck_check_pass1 () +{ + /* if (opt_check == 1)*/ + reiserfsck_tree_check (compare_neighboring_leaves_in_pass1); +} + +void check_cached_tree () +{ + reiserfsck_check_cached_tree (fs->s_dev, SB_ROOT_BLOCK (fs), fs->s_blocksize); +} + +void reiserfsck_check_after_all () +{ + reiserfsck_tree_check (compare_neighboring_leaves_after_all); +} + +#if 0 +static int is_bad_sd (struct item_head * ih, char * item) +{ + struct stat_data * sd = (struct stat_data *)item; + + if (!S_ISDIR (sd->sd_mode) && !S_ISREG(sd->sd_mode) && + !S_ISCHR (sd->sd_mode) && !S_ISBLK(sd->sd_mode) && + !S_ISLNK (sd->sd_mode) && !S_ISFIFO(sd->sd_mode) && + !S_ISSOCK(sd->sd_mode)) { + fsck_log ("file %k unexpected mode encountered 0%o\n", &ih->ih_key, sd->sd_mode); + } + return 0; +} +#endif + + + +#include <sys/ioctl.h> +#include <sys/mount.h> + + +int blocks_on_device (int dev, int blocksize) +{ + int size; + + if (ioctl (dev, BLKGETSIZE, &size) >= 0) { + return size / (blocksize / 512); + } + if (ioctl (dev, BLKGETSIZE, &size) >= 0) { + return size / (blocksize / 512); + } else { + struct stat stat_buf; + memset(&stat_buf, '\0', sizeof(struct stat)); + if(fstat(dev, &stat_buf) >= 0) { + return stat_buf.st_size / (blocksize / 512); + } else { + die ("can not calculate device size\n"); + } + } + return 0; +} + + +int is_internal_bad (struct buffer_head * bh) +{ + struct key * key; + + int i; + + if (!is_internal_node(bh)) + return 0; + for (i = 0; i < B_NR_ITEMS (bh); i ++) { + key = B_N_PDELIM_KEY (bh, i); + if (//key->k_dir_id >= key->k_objectid || + key->u.k_offset_v1.k_uniqueness != V1_DIRENTRY_UNIQUENESS && key->u.k_offset_v1.k_uniqueness != V1_DIRECT_UNIQUENESS && + key->u.k_offset_v1.k_uniqueness != V1_INDIRECT_UNIQUENESS && key->u.k_offset_v1.k_uniqueness != V1_SD_UNIQUENESS && + key->u.k_offset_v2.k_type != TYPE_DIRENTRY && key->u.k_offset_v2.k_type != TYPE_DIRECT && + key->u.k_offset_v2.k_type != TYPE_INDIRECT && key->u.k_offset_v2.k_type != TYPE_STAT_DATA //&& + // key->u.k_offset_v1.k_uniqueness != V1_ANY_UNIQUENESS && key->u.k_offset_v2.k_type != TYPE_ANY + ) + return 1; + } + return 0; +} + + + + + + + diff --git a/fsck/check_tree.c b/fsck/check_tree.c new file mode 100644 index 0000000..6615409 --- /dev/null +++ b/fsck/check_tree.c @@ -0,0 +1,915 @@ +/* + * Copyright 1999 Hans Reiser + */ + +#include "fsck.h" + + +// +// +// check S+ tree of the file system +// +// check_fs_tree stops and recommends to run fsck --rebuild-tree when: +// 1. read fails +// 2. node of wrong level found in the tree +// 3. something in the tree points to wrong block number +// out of filesystem boundary is pointed by tree +// to block marked as free in bitmap +// the same block is pointed from more than one place +// not data blocks (journal area, super block, bitmaps) +// 4. bad formatted node found +// 5. delimiting keys are incorrect +// + + + +/* mark every block we see in the tree in control bitmap, so, when to make + sure, that no blocks are pointed to from more than one place we use + additional bitmap (control_bitmap). If we see pointer to a block we set + corresponding bit to 1. If it is set already - run fsck with --rebuild-tree */ +static reiserfs_bitmap_t control_bitmap; + +static int tree_scanning_failed = 0; + + +/* 1 if block is not marked as used in the bitmap */ +static int is_block_free (reiserfs_filsys_t fs, unsigned long block) +{ + return !reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), block); +} + + +/* we have seen this block in the tree, mark corresponding bit in the + control bitmap */ +static void we_met_it (unsigned long block) +{ + reiserfs_bitmap_set_bit (control_bitmap, block); +} + + +/* have we seen this block somewhere in the tree before? */ +static int did_we_meet_it (unsigned long block) +{ + return reiserfs_bitmap_test_bit (control_bitmap, block); +} + + +static void init_control_bitmap (reiserfs_filsys_t fs) +{ + int i; + + control_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + if (!control_bitmap) + die ("init_control_bitmap: could not create control bitmap"); + + /* skipped and super block */ + for (i = 0; i <= SB_BUFFER_WITH_SB (fs)->b_blocknr; i ++) + we_met_it (i); + + /* bitmaps */ + for (i = 0; i < SB_BMAP_NR (fs); i ++) + we_met_it (SB_AP_BITMAP (fs)[i]->b_blocknr); + + for (i = 0; i < rs_journal_size (fs->s_rs) + 1; i ++) + we_met_it (i + SB_JOURNAL_BLOCK (fs)); + +} + + +#if 0 +static void show_diff (int n, char * disk, char * control, int bits) +{ + int i; + int last_diff = 0; + int from, num; + + fsck_log ("bitmap %d does not match to the correct one\n", n); + + from = 0; + num = 0; + for (i = 0; i < bits; i ++) { + if (test_bit (i, disk) && !test_bit (i, control)) { + if (last_diff == 1) { + num ++; + continue; + } else if (last_diff == 2) { + fsck_log ("Block [%d-%d] free in disk bitmap, used in control\n", from, from + num - 1); + } + num = 1; + from = n * bits + i; + last_diff = 1; + continue; + } + if (!test_bit (i, disk) && test_bit (i, control)) { + if (last_diff == 2) { + num ++; + continue; + } else if (last_diff == 1) { + fsck_log ("Block [%d-%d] used in disk bitmap, free in control\n", from, from + num - 1); + } + num = 1; + from = n * bits + i; + last_diff = 2; + continue; + } + /* the same bits */ + if (last_diff == 1) + fsck_log ("Block [%d-%d] used in disk bitmap, free in control\n", from, from + num - 1); + if (last_diff == 2) + fsck_log ("Block [%d-%d] free in disk bitmap, used in control\n", from, from + num - 1); + + num = 0; + from = 0; + last_diff = 0; + continue; + } +} +#endif + + +/* if we managed to complete tree scanning and if control bitmap and/or proper + amount of free blocks mismatch with bitmap on disk and super block's + s_free_blocks - we can fix that */ +static void compare_bitmaps (reiserfs_filsys_t fs) +{ + int diff; + + if (tree_scanning_failed) { + fsck_progress ("Could not scan whole tree. " + "--rebuild-tree is required\n"); + return; + } + + fsck_progress ("Comparing bitmaps.."); + + /* check free block counter */ + if (SB_FREE_BLOCKS (fs) != reiserfs_bitmap_zeros (control_bitmap)) { + fsck_log ("free block count %lu mismatches with a correct one %lu. \n", + SB_FREE_BLOCKS (fs), reiserfs_bitmap_zeros (control_bitmap)); +#if 0 + if (fsck_fix_fixable (fs)) { + set_free_blocks (fs->s_rs, reiserfs_bitmap_zeros (control_bitmap)); + mark_buffer_dirty (fs->s_sbh); + mark_filesystem_dirty (fs); + fsck_log ("Fixed\n"); + } else { + fsck_log ("Can be fixed by --fix-fixable\n"); + } +#endif + } + + diff = reiserfs_bitmap_compare (fsck_disk_bitmap (fs), control_bitmap); + if (diff) { + fsck_log ("on-disk bitmap does not match to the correct one. %d bytes differ\n", diff); +#if 0 + if (fsck_fix_fixable (fs)) { + reiserfs_flush_bitmap (control_bitmap, fs); + mark_filesystem_dirty (fs); + fsck_log ("Fixed\n"); + } else { + fsck_log ("Can be fixed by --fix-fixable\n"); + } +#endif + } + + fsck_progress ("ok\n"); + return; +} + + + +/* is this block legal to be pointed to by some place of the tree? */ +static int bad_block_number (struct super_block * s, unsigned long block) +{ + if (block >= SB_BLOCK_COUNT (s)) { + /*reiserfs_warning ("block out of filesystem boundary found\n");*/ + return 1; + } + + if (not_data_block (s, block)) { + /*reiserfs_warning ("not data block (%lu) is used in the tree\n", + block);*/ + return 1; + } + + if (is_block_free (s, block)) { + fsck_log ("block %lu is not marked as used in the disk bitmap\n", block); + return 0; + } + + return 0; +} + + +static int got_already (struct super_block * s, unsigned long block) +{ + if (0/*opt_fsck_mode == FSCK_FAST_REBUILD*/){ + if (is_block_used(block)){ + fsck_log ("block %lu is in tree already\n", block); + return 1; + } + } else { + if (did_we_meet_it (block)) { + /*fsck_log ("block %lu is in tree already\n", block);*/ + return 1; + } + we_met_it (block); + } + return 0; +} + + +/* 1 if some of fields in the block head of bh look bad */ +static int bad_block_head (struct buffer_head * bh) +{ + struct block_head * blkh; + + blkh = B_BLK_HEAD (bh); + if (le16_to_cpu (blkh->blk_nr_item) > (bh->b_size - BLKH_SIZE) / IH_SIZE) { + fsck_log ("block %lu has wrong blk_nr_items (%z)\n", + bh->b_blocknr, bh); + return 1; + } + if (le16_to_cpu (blkh->blk_free_space) > + bh->b_size - BLKH_SIZE - IH_SIZE * le16_to_cpu (blkh->blk_nr_item)) { + fsck_log ("block %lu has wrong blk_free_space %z\n", + bh->b_blocknr, bh); + return 1; + } + return 0; +} + + +/* 1 if it does not look like reasonable stat data */ +static int bad_stat_data (struct buffer_head * bh, struct item_head * ih) +{ + unsigned long objectid; + int pos; + +/* + if (opt_fsck_mode == FSCK_FAST_REBUILD) + return 0; +*/ + objectid = le32_to_cpu (ih->ih_key.k_objectid); + if (!is_objectid_used (fs, objectid)) { + /* FIXME: this could be cured right here */ + fsck_log ("\nbad_stat_data: %lu is marked free, but used by an object %k\n", + objectid, &ih->ih_key); + } + + if (is_objectid_really_used (proper_id_map (fs), objectid, &pos)) { + fsck_log ("\nbad_stat_data: %lu is shared by at least two files\n", + objectid); + return 0; + } + mark_objectid_really_used (proper_id_map (fs), objectid); + return 0; +} + + +/* it looks like we can check item length only */ +static int bad_direct_item (struct buffer_head * bh, struct item_head * ih) +{ + return 0; +} + + +/* for each unformatted node pointer: make sure it points to data area and + that it is not in the tree yet */ +static int bad_indirect_item (reiserfs_filsys_t fs, struct buffer_head * bh, + struct item_head * ih) +{ + int i; + __u32 * ind = (__u32 *)B_I_PITEM (bh, ih); + + if (ih_item_len (ih) % 4) { + fsck_log ("bad_indirect_item: block %lu: item (%H) has bad length\n", + bh->b_blocknr, ih); + return 1; + } + + for (i = 0; i < I_UNFM_NUM (ih); i ++) { + __u32 unfm_ptr; + + unfm_ptr = le32_to_cpu (ind [i]); + if (!unfm_ptr) + continue; + + /* check unformatted node pointer and mark it used in the + control bitmap */ + if (bad_block_number (fs, unfm_ptr)) { + fsck_log ("bad_indirect_item: block %lu: item %H has bad pointer %d: %lu", + bh->b_blocknr, ih, i, unfm_ptr); + if (fsck_fix_fixable (fs)) { + fsck_log (" - fixed"); + ind [i] = 0; + mark_buffer_dirty (bh); + } + fsck_log ("\n"); + continue; + } + + if (got_already (fs, unfm_ptr)) { + fsck_log ("bad_indirect_item: block %lu: item %H has a pointer %d " + "to the block %lu which is in tree already", + bh->b_blocknr, ih, i, unfm_ptr); + if (fsck_fix_fixable (fs)) { + fsck_log (" - fixed"); + ind [i] = 0; + mark_buffer_dirty (bh); + } + fsck_log ("\n"); + continue; + } + } + + /* delete this check for 3.6 */ + if (ih_free_space (ih) > fs->s_blocksize - 1) + fsck_log ("bad_indirect_item: %H has wrong ih_free_space\n", ih); + return 0; +} + + +/* FIXME: this was is_bad_directory from pass0.c */ +static int bad_directory_item (struct buffer_head * bh, struct item_head * ih) +{ + int i; + char * name; + int namelen; + struct reiserfs_de_head * deh = B_I_DEH (bh, ih); + int min_entry_size = 1;/* we have no way to understand whether the + filesystem were created in 3.6 format or + converted to it. So, we assume that minimal name + length is 1 */ + __u16 state; + + + /* make sure item looks like a directory */ + if (ih_item_len (ih) / (DEH_SIZE + min_entry_size) < ih_entry_count (ih)) + /* entry count can not be that big */ + return 1; + + if (deh[ih_entry_count (ih) - 1].deh_location != DEH_SIZE * ih_entry_count (ih)) + /* last entry should start right after array of dir entry headers */ + return 1; + + /* check name hashing */ + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + namelen = name_length (ih, deh, i); + name = name_in_entry (deh, i); + if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) { + return 1; + } + } + + deh = B_I_DEH (bh, ih); + state = 0; + set_bit (DEH_Visible, &state); + /* ok, items looks like a directory */ + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + if (deh_state (deh) != state) { + fsck_log ("bad_directory_item: block %lu: item %H has entry " + "\"%.*s\" with wrong deh_state %o", + bh->b_blocknr, ih, name_length (ih, deh, i), + name_in_entry (deh, i), deh_state (deh)); + if (fsck_fix_fixable (fs)) { + deh->deh_state = 0; + mark_de_visible (deh); + mark_buffer_dirty (bh); + fsck_log (" - fixed"); + } + fsck_log ("\n"); + } + } + + return 0; +} + + +static int bad_item (struct super_block * s, struct buffer_head * bh, int i) +{ + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, i); + if (is_stat_data_ih (ih)) + return bad_stat_data (bh, ih); + + if (is_direct_ih (ih)) + return bad_direct_item (bh, ih); + + if (is_indirect_ih(ih)) + return bad_indirect_item (s, bh, ih); + + return bad_directory_item (bh, ih); +} + + +/* 1 if i-th and (i-1)-th items can not be neighbors in a leaf */ +int bad_pair (struct super_block * s, struct buffer_head * bh, int i) +{ + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, i); + + + if (comp_keys (&((ih - 1)->ih_key), &ih->ih_key) != -1) + return 1; + + if (is_stat_data_ih (ih)) + /* left item must be of another object */ + if (comp_short_keys (&((ih - 1)->ih_key), &ih->ih_key) != -1) + return 1; + + if (is_direct_ih(ih)) { + /* left item must be indirect or stat data item of the same + file */ + if (not_of_one_file (&((ih - 1)->ih_key), &ih->ih_key)) + return 1; + + if (!((is_stat_data_ih (ih - 1) && get_offset (&ih->ih_key) == 1) || + (is_indirect_ih (ih - 1) && + get_offset (&(ih - 1)->ih_key) + get_bytes_number (ih-1, bh->b_size) == //get_bytes_number (bh, ih - 1, 0, CHECK_FREE_BYTES) == + get_offset (&ih->ih_key)))) + return 1; + + } + + if (is_indirect_ih (ih) || is_direntry_ih (ih)) { + /* left item must be stat data of the same object */ + if (not_of_one_file (&((ih - 1)->ih_key), &ih->ih_key)) + return 1; + + if (!is_stat_data_ih (ih - 1)) + return 1; + } + + return 0; +} + +int bad_leaf_2 (struct super_block * s, struct buffer_head * bh) +{ + int i; + + if (bad_block_head (bh)) + return 1; + + for (i = 0; i < B_NR_ITEMS (bh); i ++) { + if (i && bad_pair (s, bh, i)) { + fsck_log ("bad_leaf_2: block %lu has wrong order of items\n", + bh->b_blocknr); + return 1; + } + } + return 0; +} + + +/* 1 if block head or any of items is bad */ +static int bad_leaf (struct super_block * s, struct buffer_head * bh) +{ + int i; + + if (bad_block_head (bh)) + return 1; + + for (i = 0; i < B_NR_ITEMS (bh); i ++) { + if (bad_item (s, bh, i)) { + fsck_log ("bad_leaf: block %lu has invalid item %d: %H\n", + bh->b_blocknr, i, B_N_PITEM_HEAD (bh, i)); + } + + if (i && bad_pair (s, bh, i)) { + fsck_log ("bad_leaf: block %lu has wrong order of items\n", + bh->b_blocknr); + } + } + return 0; +} + + +/* 1 if bh does not look like internal node */ +static int bad_internal (struct super_block * s, struct buffer_head * bh) +{ + int i; + + for (i = 0; i <= B_NR_ITEMS (bh); i ++) + { + if (i != B_NR_ITEMS (bh) && i != B_NR_ITEMS (bh) - 1) + if (comp_keys (B_N_PDELIM_KEY (bh, i), B_N_PDELIM_KEY (bh, i + 1)) != -1) + return 1; + if (bad_block_number(s, child_block_number(bh,i))){ + return 1; + } + } + return 0; +} + + +/* h == 0 for root level. block head's level == 1 for leaf level */ +static inline int h_to_level (struct super_block * s, int h) +{ + return SB_TREE_HEIGHT (s) - h - 1; +} + + +/* bh must be formatted node. blk_level must be tree_height - h + 1 */ +static int bad_node (struct super_block * s, struct buffer_head ** path, + int h) +{ + struct buffer_head ** pbh = &path[h]; + + if (B_LEVEL (*pbh) != h_to_level (s, h)) { + fsck_log ("node (%lu) with wrong level (%d) found in the tree (should be %d)\n", + (*pbh)->b_blocknr, B_LEVEL (*pbh), h_to_level (s, h)); + return 1; + } + + if (bad_block_number (s, (*pbh)->b_blocknr)) { + return 1; + } + + if (got_already (s, (*pbh)->b_blocknr)) + return 1; + + if (is_leaf_node (*pbh)) + return bad_leaf (s, *pbh); + + return bad_internal (s, *pbh); +} + + +/* internal node bh must point to block */ +static int get_pos (struct buffer_head * bh, unsigned long block) +{ + int i; + + for (i = 0; i <= B_NR_ITEMS (bh); i ++) { + if (child_block_number (bh, i) == block) + return i; + } + die ("get_pos: position for block %lu not found", block); + return 0; +} + + +/* path[h] - leaf node */ +static struct key * lkey (struct buffer_head ** path, int h) +{ + int pos; + + while (h > 0) { + pos = get_pos (path[h - 1], path[h]->b_blocknr); + if (pos) + return B_N_PDELIM_KEY(path[h - 1], pos - 1); + h --; + } + return 0; +} + + +/* path[h] - leaf node */ +static struct key * rkey (struct buffer_head ** path, int h) +{ + int pos; + + while (h > 0) { + pos = get_pos (path[h - 1], path[h]->b_blocknr); + if (pos != B_NR_ITEMS (path[h - 1])) + return B_N_PDELIM_KEY (path[h - 1], pos); + h --; + } + return 0; +} + + +/* are all delimiting keys correct */ +static int bad_path (struct super_block * s, struct buffer_head ** path, int h1) +{ + int h = 0; + struct key * dk; + + while (path[h]) + h ++; + + h--; + + // path[h] is leaf + if (h != h1) + die ("bad_path: wrong path"); + + dk = lkey (path, h); + if (dk && comp_keys (dk, B_N_PKEY (path[h], 0))) + // left delimiting key must be equal to the key of 0-th item in the + // node + return 1; + + dk = rkey (path, h); + if (dk && comp_keys (dk, B_N_PKEY (path[h], node_item_number (path[h]) - 1)) != 1) + // right delimiting key must be bigger than the key of the last item + // in the node + return 1; + + return 0; +} + + +/* pass the S+ tree of filesystem */ +void check_fs_tree (struct super_block * s) +{ + init_control_bitmap (s); + + proper_id_map (s) = init_id_map (); + + fsck_progress ("Checking S+tree.."); + + pass_through_tree (s, bad_node, bad_path); + + /* S+ tree is correct (including all objects have correct + sequences of items) */ + fsck_progress ("ok\n"); + + /* compare created bitmap with the original */ + compare_bitmaps (s); + + free_id_map (&proper_id_map (s)); +} + +#if 0 + +void remove_internal_pointer(struct super_block * s, struct buffer_head ** path) +{ + int h = 0; + int pos, items; + __u32 block; + + + while (path[h]) + h ++; + + h--; + block = path[h]->b_blocknr; + printf("\nremove pointer to (%d) block", block); + brelse(path[h]); + path[h] = 0; + h--; + while (h>=0) + { + if (B_NR_ITEMS(path[h]) <= 1) + { + block = path[h]->b_blocknr; + brelse(path[h]); + path[h] = 0; + mark_block_free(block); + /*unmark_block_formatted(block);*/ + used_blocks++; + h --; + continue; + } + pos = get_pos (path[h], block); + if (pos) + { + memmove (B_N_CHILD(path[h],pos), B_N_CHILD(path[h],pos+1), + s->s_blocksize - BLKH_SIZE - B_NR_ITEMS(path[h])*KEY_SIZE - DC_SIZE*(pos+1)); + memmove(B_N_PDELIM_KEY(path[h],pos-1), B_N_PDELIM_KEY(path[h],pos), + s->s_blocksize - BLKH_SIZE - (pos)*KEY_SIZE); + }else{ + __u32 move_block = path[h]->b_blocknr; + int move_to_pos; + int height = h; + + while(--height >= 0) + { + move_to_pos = get_pos (path[height], move_block); + if (move_to_pos == 0){ + move_block = path[height]->b_blocknr; + continue; + } + *B_N_PDELIM_KEY(path[height], move_to_pos-1) = *B_N_PDELIM_KEY(path[h], 0); + break; + } + + memmove (B_N_CHILD(path[h], 0), B_N_CHILD(path[h], 1), + s->s_blocksize - BLKH_SIZE - B_NR_ITEMS(path[h])*KEY_SIZE - DC_SIZE); + memmove(B_N_PDELIM_KEY(path[h], 0), B_N_PDELIM_KEY(path[h], 1), + s->s_blocksize - BLKH_SIZE - KEY_SIZE); + } + set_node_item_number(path[h], node_item_number(path[h]) - 1); + mark_buffer_dirty(path[h], 1); + break; + } + if (h == -1) + { + SB_DISK_SUPER_BLOCK(s)->s_root_block = ~0; + SB_DISK_SUPER_BLOCK(s)->s_tree_height = ~0; + mark_buffer_dirty(SB_BUFFER_WITH_SB(s), 1); + } +} + +void handle_buffer(struct super_block * s, struct buffer_head * bh) +{ + int i, j; + struct item_head * ih; + + if (is_leaf_node (bh)) + { + for (i = 0, ih = B_N_PITEM_HEAD (bh, 0); i < B_NR_ITEMS (bh); i ++, ih ++) + { + if (is_indirect_ih(ih)) + for (j = 0; j < I_UNFM_NUM (ih); j ++) + if (B_I_POS_UNFM_POINTER(bh,ih,j)){ + /*mark_block_unformatted(le32_to_cpu(B_I_POS_UNFM_POINTER(bh,ih,j)));*/ + mark_block_used(le32_to_cpu(B_I_POS_UNFM_POINTER(bh,ih,j))); + used_blocks++; + } + if (is_stat_data_ih (ih)) { + /*add_event (STAT_DATA_ITEMS);*/ + if (ih_key_format(ih) == KEY_FORMAT_1) + ((struct stat_data_v1 *)B_I_PITEM(bh,ih))->sd_nlink = 0; + else + ((struct stat_data *)B_I_PITEM(bh,ih))->sd_nlink = 0; + mark_buffer_dirty(bh, 1); + } + } + } + mark_block_used(bh->b_blocknr); +// we_met_it(s, bh->b_blocknr); + used_blocks++; +} + +/* bh must be formatted node. blk_level must be tree_height - h + 1 */ +static int handle_node (struct super_block * s, struct buffer_head ** path, int h) +{ + if (bad_node(s, path, h)){ + remove_internal_pointer(s, path); + return 1; + } + handle_buffer(s, path[h]); + return 0; +} + +/* are all delimiting keys correct */ +static int handle_path (struct super_block * s, struct buffer_head ** path, int h) +{ + if (bad_path(s, path, h)){ + remove_internal_pointer(s, path); + return 1; + } + return 0; +} + +//return 1 to run rebuild tree from scratch +void check_internal_structure(struct super_block * s) +{ + /* control bitmap is used to keep all blocks we should not put into tree again */ + /* used bitmap is used to keep all inserted blocks. The same as control bitmap plus unfm blocks */ +// init_control_bitmap(s); + + printf ("Checking S+tree.."); + + pass_through_tree (s, handle_node, handle_path); + +// compare_bitmaps(s); + printf ("ok\n"); +} + +#endif + +int check_sb (struct super_block * s) +{ + int format_sb = 0; + int problem = 0; + struct reiserfs_super_block * rs; + __u32 block_count; + + rs = s->s_rs; + // in (REISERFS_DISK_OFFSET_IN_BYTES / 4096) block + if (is_reiser2fs_magic_string (rs) && + SB_JOURNAL_BLOCK(s) == get_journal_start_must (rs_blocksize (rs))) + { + // 3.6 or >=3.5.22 + printf("\t 3.6.x format SB found\n"); + format_sb = 1; + goto good_format; + } + + if (is_reiserfs_magic_string (rs) && + SB_JOURNAL_BLOCK(s) == get_journal_start_must (rs_blocksize (rs))) + { + // >3.5.9(10) and <=3.5.21 + printf("\t>=3.5.9 format SB found\n"); + format_sb = 2; + goto good_format; + } + + // in 2 block + if (is_reiser2fs_magic_string (rs) && + SB_JOURNAL_BLOCK(s) == get_journal_old_start_must (rs)) + { + // <3.5.9(10) converted to new format + printf("\t< 3.5.9(10) SB converted to new format found \n"); + format_sb = 3; + goto good_format; + } + + if (is_reiserfs_magic_string (rs) && + SB_JOURNAL_BLOCK(s) == get_journal_old_start_must (rs)) + { + // <3.5.9(10) + printf("\t< 3.5.9(10) format SB found\n"); + format_sb = 4; + goto good_format; + } + else + die("check SB: wrong SB format found\n"); + +good_format: + + printf("\n\t%d-%d\n", SB_BLOCK_COUNT (s), SB_FREE_BLOCKS (s)); + if (s->s_blocksize != 4096) { + fsck_log("check SB: specified block size (%d) is not correct must be 4096\n", s->s_blocksize); + problem++; + } + + //for 4096 blocksize only + if ((rs_tree_height(rs) < DISK_LEAF_NODE_LEVEL) || (rs_tree_height(rs) > MAX_HEIGHT)){ + fsck_log ("check SB: wrong tree height (%d)\n", rs_tree_height(rs)); + problem++; + } + + block_count = count_blocks ("", s->s_blocksize, s->s_dev); + + if (SB_BLOCK_COUNT(s) > block_count){ + fsck_log ("check SB: specified block number (%d) is too high\n", SB_BLOCK_COUNT(s)); + problem++; + } + + if ((rs_root_block(rs) >= block_count) || (rs_root_block(rs) < 0)){ + fsck_log ("check SB: specified root block number (%d) is too high\n", rs_root_block(rs)); + problem++; + } + + if (SB_FREE_BLOCKS(s) > SB_BLOCK_COUNT(s)){ + fsck_log ("check SB: specified free block number (%d) is too high\n", SB_FREE_BLOCKS(s)); + problem++; + } + + if (SB_REISERFS_STATE(s) != REISERFS_VALID_FS){ + fsck_log ("check SB: wrong (%d) state\n", SB_REISERFS_STATE(s)); + problem++; + } + + if ( SB_BMAP_NR(s) != SB_BLOCK_COUNT(s) / (s->s_blocksize * 8) + + ((SB_BLOCK_COUNT(s) % (s->s_blocksize * 8)) ? 1 : 0)){ + fsck_log("check SB: wrong bitmap number (%d)\n", SB_BMAP_NR(s)); + problem++; + } + + if (SB_VERSION(s) == REISERFS_VERSION_2 || SB_VERSION(s) == REISERFS_VERSION_1) + { + if (!(SB_VERSION(s) == REISERFS_VERSION_2 && (format_sb == 1 || format_sb == 3)) && + !(SB_VERSION(s) == REISERFS_VERSION_1 && (format_sb == 2 || format_sb == 4))){ + fsck_log("check SB: wrong SB version == %d, format == %d\n", SB_VERSION(s), format_sb); + problem++; + } + } + else{ + fsck_log ("check SB: wrong SB version (%d)\n", SB_VERSION(s)); + problem++; + } + + if (SB_VERSION(s) == REISERFS_VERSION_2 && + (rs_hash (rs) < 1 || rs_hash (rs) > 3)) { + /* FIXME: */ + fsck_log("check SB: wrong hash (%d)\n", rs_hash (rs)); + problem++; + } + + + if ((SB_VERSION(s) == REISERFS_VERSION_2) ? + (rs_objectid_map_max_size (rs) != ((s->s_blocksize - SB_SIZE) / sizeof(__u32) / 2 * 2)) : + (rs_objectid_map_max_size (rs) != ((s->s_blocksize - SB_SIZE_V1) / sizeof(__u32) / 2 * 2))) { + fsck_log("check SB: objectid map corrupted max_size == %d\n", rs_objectid_map_max_size (rs)); + problem++; + } + + if (rs_objectid_map_size (rs) < 2 || + rs_objectid_map_size (rs) > rs_objectid_map_max_size (rs)) { + fsck_log("check SB: objectid map corrupted cur_size == %d\n", rs_objectid_map_size (rs)); + problem++; + } + + if (rs_journal_size(rs) != JOURNAL_BLOCK_COUNT){ + fsck_log("check SB: specified journal size (%d) is not correct must be %d\n", + rs_journal_size(rs), JOURNAL_BLOCK_COUNT); + problem++; + } + + if (!problem) { + fsck_progress ("\t No problem found\n"); + } else if (fsck_log_file (fs) != stderr) + fsck_progress ("Look for super block corruptions in the log file\n"); + + return format_sb; +} + + diff --git a/fsck/fsck.h b/fsck/fsck.h new file mode 100644 index 0000000..ac0c7ce --- /dev/null +++ b/fsck/fsck.h @@ -0,0 +1,425 @@ +/* + * Copyright 1996-2001 Hans Reiser + */ + +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <asm/types.h> +#include <sys/vfs.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <asm/types.h> +#include <assert.h> + +#include "io.h" +#include "misc.h" +#include "reiserfs_lib.h" + + + + +/* main.c */ +extern reiserfs_filsys_t fs; +extern reiserfs_bitmap_t uninsertable_leaf_bitmap; +int main (int argc, char * argv []); + + +/* + * modes + */ +#define DO_NOTHING 0 /* -a specified */ +#define FSCK_CHECK 1 +#define FSCK_SB 2 +#define FSCK_REBUILD 3 + +/* temporary */ +#define FSCK_ZERO_FILES 4 +#define DO_TEST 5 + +/* +#define FSCK_FAST_REBUILD 4 +*/ + + +/* + * options + */ +#define OPT_INTERACTIVE 1 +#define OPT_FIX_FIXABLE 2 /* not default yet */ +#define OPT_FIX_NON_CRITICAL 4 /* not default yet */ +#define OPT_QUIET 8 +#define OPT_SAVE_EXTERN_BITMAP 16 +#define OPT_SILENT 32 + +extern int g_blocks_to_read; + +/* pass 0 and 1 read the device NR_TO_READ block in time */ +#define NR_TO_READ 8 + +/* pass0.c */ +int pass_0 (reiserfs_filsys_t); +int are_there_used_leaves (unsigned long from, int count); +int is_used_leaf (unsigned long block); +int how_many_leaves_were_there (void); +int is_bad_unformatted (unsigned long block); +int is_good_unformatted (unsigned long block); +void mark_good_unformatted (unsigned long block); +int are_there_allocable_blocks (int amout_needed); +unsigned long alloc_block (void); +void make_allocable (unsigned long block); +void register_uninsertable (unsigned long block); +unsigned long how_many_uninsertables_were_there (void); +void register_saved_item (void); +unsigned long how_many_items_were_saved (void); +int still_bad_unfm_ptr_1 (unsigned long block); +int still_bad_unfm_ptr_2 (unsigned long block); +void make_alloc_bitmap (struct super_block * s); + +#define __is_marked(name,block) reiserfs_bitmap_test_bit (name##_bitmap, block) +#define __mark(name,block) reiserfs_bitmap_set_bit (name##_bitmap, block) +#define __unmark(name,block) reiserfs_bitmap_clear_bit (name##_bitmap, block) + +/* unformatted in tree */ +extern reiserfs_bitmap_t bad_unfm_in_tree_once_bitmap; +#define is_bad_unfm_in_tree_once(block) __is_marked (bad_unfm_in_tree_once, block) +#define mark_bad_unfm_in_tree_once(block) __mark (bad_unfm_in_tree_once, block) + + + +/* pass1.c */ +void pass_1_pass_2_build_the_tree (void); +struct buffer_head * make_buffer (int dev, int blocknr, int size, char * data); +void build_the_tree (void); +extern int g_unaccessed_items; +int is_item_reachable (struct item_head * ih); +void mark_item_reachable (struct item_head * ih, struct buffer_head * bh); +void mark_item_unreachable (struct item_head * ih); +void rebuild_sb (reiserfs_filsys_t fs); +struct si * remove_saved_item (struct si * si); + + +/* pass2.c */ +void insert_item_separately (struct item_head * ih, char * item, + int was_in_tree); +struct si * save_and_delete_file_item (struct si * si, struct path * path); +void take_bad_blocks_put_into_tree (void); +void rewrite_object (struct item_head * ih, int do_remap); +void pass_2_take_bad_blocks_put_into_tree (void); +/*int is_remapped (struct item_head * ih);*/ +void link_relocated_files (void); +void relocate_file (struct item_head * ih, int change_ih); +void relocate_dir (struct item_head * ih, int change_ih); +__u32 objectid_for_relocation (struct key * key); + +/* file.c */ +struct si { + struct item_head si_ih; + char * si_dnm_data; + struct si * si_next; + __u32 si_blocknr; + + // changed by XB; + struct si * last_known; +}; +void put_saved_items_into_tree (struct si *); +int reiserfsck_file_write (struct item_head * ih, char * item, int); +int are_file_items_correct (struct key * key, int key_version, __u64 * size, __u32 * blocks, int mark_passed_items, + int symlink, __u64 symlink_size); + + +/* semantic.c */ +extern struct key root_dir_key; +extern struct key parent_root_dir_key; +extern struct key lost_found_dir_key; +void pass_3_semantic (void); +void semantic_check (void); +int check_semantic_tree (struct key * key, struct key * parent, int is_dot_dot, int lost_found, struct item_head * new_ih); +void zero_nlink (struct item_head * ih, void * sd); +int not_a_directory (void * sd); +int is_dot_dot (char * name, int namelen); +int is_dot (char * name, int namelen); +void create_dir_sd (reiserfs_filsys_t fs, + struct path * path, struct key * key); +int rebuild_check_regular_file (struct path * path, void * sd, + struct item_head * new_ih); +int rebuild_semantic_pass (struct key * key, struct key * parent, int is_dot_dot, + struct item_head * new_ih); + +/* access to stat data fields */ +void get_set_sd_field (int field, struct item_head * ih, void * sd, + void * value, int set); +#define GET_SD_MODE 0 +#define GET_SD_SIZE 1 +#define GET_SD_NLINK 2 +#define GET_SD_BLOCKS 3 +#define GET_SD_FIRST_DIRECT_BYTE 4 + +#define get_sd_mode(ih,sd,pmode) get_set_sd_field (GET_SD_MODE, ih, sd, pmode, 0/*get*/) +#define set_sd_mode(ih,sd,pmode) get_set_sd_field (GET_SD_MODE, ih, sd, pmode, 1/*set*/) + +#define get_sd_size(ih,sd,psize) get_set_sd_field (GET_SD_SIZE, ih, sd, psize, 0/*get*/) +#define set_sd_size(ih,sd,psize) get_set_sd_field (GET_SD_SIZE, ih, sd, psize, 1/*set*/) + +#define get_sd_blocks(ih,sd,pblocks) get_set_sd_field (GET_SD_BLOCKS, ih, sd, pblocks, 0/*get*/) +#define set_sd_blocks(ih,sd,pblocks) get_set_sd_field (GET_SD_BLOCKS, ih, sd, pblocks, 1/*set*/) + +#define get_sd_nlink(ih,sd,pnlink) get_set_sd_field (GET_SD_NLINK, ih, sd, pnlink, 0/*get*/) +#define set_sd_nlink(ih,sd,pnlink) get_set_sd_field (GET_SD_NLINK, ih, sd, pnlink, 1/*set*/) + +#define get_sd_first_direct_byte(ih,sd,pfdb) get_set_sd_field (GET_SD_FIRST_DIRECT_BYTE, ih, sd, pfdb, 0/*get*/) +#define set_sd_first_direct_byte(ih,sd,pfdb) get_set_sd_field (GET_SD_FIRST_DIRECT_BYTE, ih, sd, pfdb, 1/*set*/) + + + +/* lost+found.c */ +void pass_3a_look_for_lost (reiserfs_filsys_t s); + + +/* pass4.c */ +void get_next_key (struct path * path, int i, struct key * key); +int pass_4_check_unaccessed_items (void); + + +/* check.c */ +int is_leaf_bad (struct buffer_head * bh); +int is_internal_bad (struct buffer_head * bh); +int is_bad_item (struct buffer_head * bh, struct item_head *, char *); +/*int check_file_system (void);*/ +void reiserfsck_check_pass1 (void); +void reiserfsck_check_after_all (void); +/*char * bad_name (char * name, int namelen);*/ +/* to test result of direcotry item recovering on pass 0 */ +int is_bad_directory (struct item_head * ih, char * item, int dev, int blocksize); + + +//extern int bad_block_number (struct super_block * s, blocknr_t block); + +/* check_tree.c */ +void check_fs_tree (struct super_block * s); +int check_sb (struct super_block * s); +int bad_pair (struct super_block * s, struct buffer_head * bh, int i); +int bad_leaf_2 (struct super_block * s, struct buffer_head * bh); + + + +/* ustree.c */ +void init_tb_struct (struct tree_balance * tb, struct super_block * s, struct path * path, int size); +void reiserfsck_paste_into_item (struct path * path, const char * body, int size); +void reiserfsck_insert_item (struct path * path, struct item_head * ih, const char * body); +void reiserfsck_delete_item (struct path * path, int temporary); +void reiserfsck_cut_from_item (struct path * path, int cut_size); +typedef int (comp_function_t)(void * key1, void * key2); +typedef int (comp3_function_t)(void * key1, void * key2, int version); +/*typedef int (comp_function_t)(struct key * key1, struct key * key2);*/ +int ubin_search_id(__u32 * id, __u32 * base, __u32 number, __u32 * pos); +int usearch_by_key (struct super_block * s, struct key * key, struct path * path); +int usearch_by_key_3 (struct super_block * s, struct key * key, struct path * path, int * repeat, int stop_level, + comp3_function_t comp_func, int version); +int usearch_by_entry_key (struct super_block * s, struct key * key, struct path * path); +int usearch_by_position (struct super_block * s, struct key * key, int version, struct path * path); +struct key * uget_lkey (struct path * path); +struct key * uget_rkey (struct path * path); + +typedef int do_after_read_t (struct super_block * s, struct buffer_head **, int h); +typedef int do_on_full_path_t (struct super_block * s, struct buffer_head **, int); +void pass_through_tree (struct super_block *, do_after_read_t, do_on_full_path_t); + +//int comp_keys_3 (void * key1, void * key2); +//int comp_dir_entries (void * key1, void * key2); +inline int ubin_search (void * key, void * base, int num, int width, __u32 *ppos, comp_function_t comp_func); + + +/* bitmap.c */ +int reiserfsck_reiserfs_new_blocknrs (reiserfs_filsys_t fs, + unsigned long * pblocknrs, + unsigned long start_from, + int amount_needed); +int reiserfsck_reiserfs_free_block (reiserfs_filsys_t fs, unsigned long block); +struct buffer_head * reiserfsck_get_new_buffer (unsigned long start); +int is_block_used (unsigned long block); +int is_to_be_read (reiserfs_filsys_t fs, unsigned long block); +void mark_block_used (unsigned long block); +void mark_block_uninsertable (unsigned long block); +int is_block_uninsertable (unsigned long block); + + +/* objectid.c */ +int is_objectid_used (struct super_block * s, __u32 objectid); +void mark_objectid_as_used (struct super_block * s, __u32 objectid); +void mark_objectid_as_free (struct super_block * s, __u32 objectid); +__u32 get_unused_objectid (struct super_block * s); + +struct id_map * init_id_map (void); +void free_id_map (struct id_map **); +int is_objectid_really_used (struct id_map *, __u32 id, int * ppos); +int mark_objectid_really_used (struct id_map *, __u32 id); +void flush_objectid_map (struct id_map * map, reiserfs_filsys_t fs); +void fetch_objectid_map (struct id_map * map, reiserfs_filsys_t fs); + + +/* segments.c */ +struct overwritten_unfm_segment { + int ous_begin; + int ous_end; + struct overwritten_unfm_segment * ous_next; +}; +struct overwritten_unfm * look_for_overwritten_unfm (__u32); +struct overwritten_unfm_segment * find_overwritten_unfm (unsigned long unfm, int length, struct overwritten_unfm_segment * segment_to_init); +int get_unoverwritten_segment (struct overwritten_unfm_segment * list_head, struct overwritten_unfm_segment * unoverwritten_segment); +void save_unfm_overwriting (unsigned long unfm, struct item_head * direct_ih); +void free_overwritten_unfms (void); +void mark_formatted_pointed_by_indirect (__u32); +int is_formatted_pointed_by_indirect (__u32); + + + +struct id_map { + __u32 * m_begin; /* pointer to map area */ + unsigned long m_used_slots_count; + int m_page_count; /* objectid map expands by one page at + time. This is size of objectid map size in + pages */ + unsigned long objectids_marked; /* number of objectids marked used + in a map */ +}; + + +struct fsck_data { + unsigned long all_blocks; /* super block's block count */ + + /* pass 0 */ + unsigned long analyzed; /* blocks marked used (not data not included) */ + unsigned long free; /* free blocks */ + unsigned long not_data; /* super block, bitmap, journal */ + unsigned long leaves; /* blocks looking like reiserfs leaves */ + unsigned long pointed_leaves; + unsigned long pointed; /* by indirect items */ + unsigned long pointed_once; + unsigned long pointed_more_than_once; + unsigned long allocable; + unsigned long wrong_pointers; /* out of range or pointers to free + area */ + unsigned long leaves_corrected; + unsigned long all_contents_removed; + + /* pass 1, 2 */ + unsigned long read_leaves; + unsigned long uninsertable_leaves; + unsigned long inserted_leaves; + unsigned long shared_objectids; + unsigned long saved_on_pass1; + unsigned long relocated; + unsigned long rewritten; + + /* stat of semantic pass */ + unsigned long regular_files; + unsigned long broken_files; /* files having stat data and broken body */ + unsigned long directories; + unsigned long symlinks; + unsigned long others; + unsigned long fixed_sizes; + unsigned long deleted_entries; /* entries pointing to nowhere */ + unsigned long oid_sharing; /* files relocated due to objectid sharing */ + unsigned long oid_sharing_files_relocated; /* relocated files */ + unsigned long oid_sharing_dirs_relocated; /* relocated dirs */ + unsigned long lost_found; + unsigned long empty_lost_dirs; + unsigned long lost_found_dirs; + unsigned long dir_recovered; + unsigned long lost_found_files; + + /* pass 4 */ + unsigned long deleted_items; /* items which were not touched by + semantic pass */ + + /* objectid maps */ + struct id_map * proper_id_map; + struct id_map * semantic_id_map; /* this objectid map is used to + cure objectid sharing problem */ + + /* bitmaps */ + reiserfs_bitmap_t on_disk_bitmap; + reiserfs_bitmap_t new_bitmap; + reiserfs_bitmap_t allocable_bitmap; + + char * bitmap_file_name; + char * new_bitmap_file_name; + + unsigned short mode; + unsigned long options; + + /* log file name and handle */ + char * log_file_name; + FILE * log; + + /* hash hits stat */ + int hash_amount; + unsigned long * hash_hits; + +#define USED_BLOCKS 1 +#define EXTERN_BITMAP 2 +#define ALL_BLOCKS 3 + int scan_area; + int test; +}; + + +#define stats(s) ((struct fsck_data *)((s)->s_vp)) + +#define proper_id_map(s) stats(s)->proper_id_map +#define semantic_id_map(s) stats(s)->semantic_id_map + +#define fsck_disk_bitmap(s) stats(s)->on_disk_bitmap +#define fsck_new_bitmap(s) stats(s)->new_bitmap +#define fsck_allocable_bitmap(s) stats(s)->allocable_bitmap + +#define fsck_interactive(fs) (stats(fs)->options & OPT_INTERACTIVE) +#define fsck_fix_fixable(fs) (stats(fs)->options & OPT_FIX_FIXABLE) + +/* change unknown modes (corrupted) to mode of regular files, fix file + sizes which are bigger than a real file size, relocate files with + shared objectids (this slows fsck down (when there are too many + files sharing the same objectid), it will also remove other names + pointing to this file */ +#define fsck_fix_non_critical(fs) (stats(fs)->options & OPT_FIX_NON_CRITICAL) +#define fsck_quiet(fs) (stats(fs)->options & OPT_QUIET) +#define fsck_silent(fs) (stats(fs)->options & OPT_SILENT) + +#define fsck_save_leaf_bitmap(fs) (stats(fs)->options & OPT_SAVE_EXTERN_BITMAP) + +#define fsck_mode(fs) (stats(fs)->mode) +#define fsck_log_file(fs) (stats(fs)->log) + + +/* ?? */ +extern inline int change_version (int version) +{ + return (version == 1)?0:1; +} + + +int fsck_user_confirmed (reiserfs_filsys_t fs, char * q, char * a, int default_answer); +void stage_report (int, reiserfs_filsys_t fs); + +/* journal.c */ +int reiserfs_replay_journal (struct super_block * s); + + +#define fsck_log(fmt, list...) \ +{\ +if (!fsck_silent (fs))\ + reiserfs_warning (fsck_log_file (fs), fmt, ## list);\ +} + +#define fsck_progress(fmt, list...) \ +{\ +reiserfs_warning (stderr, fmt, ## list);\ +fflush (stderr);\ +} diff --git a/fsck/info.c b/fsck/info.c new file mode 100644 index 0000000..cd591d8 --- /dev/null +++ b/fsck/info.c @@ -0,0 +1,134 @@ + +/* + * Copyright 1996-1999 Hans Reiser + */ +#include "fsck.h" +#include <stdarg.h> + + +int fsck_user_confirmed (reiserfs_filsys_t fs, char * q, char * a, int default_answer) +{ + if (!fsck_interactive (fs)) + return default_answer; + + return user_confirmed (q, a); +} + + +void stage_report (int pass, reiserfs_filsys_t fs) +{ + FILE * fp; + struct fsck_data * stat; + + stat = stats (fs); + fp = stderr; + + switch (pass) { + case 0: + fsck_progress ("\tRead blocks (but not data blocks) %lu\n", stat->analyzed); + stat->analyzed = 0; + fsck_progress ("\t\tLeaves among those %lu\n", stat->leaves); + if (stat->leaves_corrected) + fsck_progress ("\t\t\t- corrected leaves %lu\n", stat->leaves_corrected); + if (stat->all_contents_removed) + fsck_progress ("\t\t\t- eaves all contents of which could not be saved and deleted %lu\n", stat->all_contents_removed); + if (stat->pointed_leaves) + fsck_progress ("\t\t\t- leaves pointed by indirect items %lu\n", stat->pointed_leaves); + if (stat->pointed) + fsck_progress ("\t\tBlocks pointed by indirect items %lu\n", stat->pointed); + if (stat->pointed_once) + fsck_progress ("\t\t\t- once %lu\n", stat->pointed_once); + if (stat->pointed_more_than_once) + fsck_progress ("\t\t\t- more than once %lu\n", stat->pointed_more_than_once); + if (stat->wrong_pointers) + fsck_progress ("\t\t\t- pointers to wrong area of filesystem (zeroed) %lu\n", stat->wrong_pointers); + /* pass1 will calculate how many pointers were zeeros there */ + stat->wrong_pointers = 0; + fsck_progress ("\t\tObjectids found %lu\n", proper_id_map (fs)->objectids_marked); + + /*fsck_progress ("\tblocks marked free %lu\n", stat->free);*/ + fsck_progress ("\tallocable %lu blocks\n", stat->allocable); + break; + + case 1: + fsck_progress ("\t%lu leaves read\n", stat->analyzed); + fsck_progress ("\t\t%lu inserted\n", stat->inserted_leaves); + if (stat->uninsertable_leaves) + fsck_progress ("\t\t%lu not inserted\n", stat->uninsertable_leaves); + if (stat->saved_on_pass1) + fsck_progress ("\tSaved %lu items\n", stat->saved_on_pass1); + if (stat->wrong_pointers) + fsck_progress ("\tPointers to leaves or non-unique (zeroed) %lu\n", + stat->wrong_pointers); + break; + + case 2: + if (stat->shared_objectids) + fsck_progress ("\t%lu shared objectids\n", stat->shared_objectids); + if (stat->relocated) + fsck_progress ("\tFiles relocated because of key conflicts w/ a directory %lu\n", + stat->relocated); + if (stat->rewritten) + fsck_progress ("\tFiles rewritten %lu\n", + stat->rewritten); + return; + + case 3: /* semantic pass */ + fsck_progress ("\tFiles found: %ld\n", stat->regular_files); + fsck_progress ("\tDirectories found: %ld\n", stat->directories); + if (stat->symlinks) + fsck_progress ("\tSymlinks found: %ld\n", stat->symlinks); + if (stat->others) + fsck_progress ("\tOthers: %ld\n", stat->others); + if (stat->fixed_sizes) + fsck_progress ("\tFiles with fixed size: %ld\n", stat->fixed_sizes); + if (stat->oid_sharing) + fsck_progress ("\tObjects having used objectids: %lu\n", stat->oid_sharing); + if (stat->oid_sharing_files_relocated) + fsck_progress ("\t\tfiles fixed %lu\n", stat->oid_sharing_files_relocated); + if (stat->oid_sharing_dirs_relocated) + fsck_progress ("\t\tdirs fixed %lu\n", stat->oid_sharing_dirs_relocated); + stat->oid_sharing = 0; + stat->oid_sharing_files_relocated = 0; + stat->oid_sharing_dirs_relocated = 0; + break; + + case 0x3a: /* looking for lost files */ + if (stat->lost_found) + fsck_progress ("\tObjects without names %lu\n", + stat->lost_found); + if (stat->empty_lost_dirs) + fsck_progress ("\tEmpty lost dirs removed %lu\n", + stat->empty_lost_dirs); + if (stat->lost_found_dirs) + fsck_progress ("\tDirs linked to /lost+found: %lu\n", + stat->lost_found_dirs); + if (stat->dir_recovered) + fsck_progress ("\t\tDirs without stat data found %lu\n", + stat->dir_recovered); + + if (stat->lost_found_files) + fsck_progress ("\tFiles linked to /lost+found %lu\n", + stat->lost_found_files); + if (stat->oid_sharing) + fsck_progress ("\tObjects having used objectids: %lu\n", stat->oid_sharing); + if (stat->oid_sharing_files_relocated) + fsck_progress ("\t\tfiles fixed %lu\n", stat->oid_sharing_files_relocated); + if (stat->oid_sharing_dirs_relocated) + fsck_progress ("\t\tdirs fixed %lu\n", stat->oid_sharing_dirs_relocated); + break; + + case 4: /* removing of unreachable */ + if (stat->deleted_items) + fsck_progress ("\tDeleted unreachable items %lu\n", + stat->deleted_items); + break; + } + + if (!fsck_user_confirmed (fs, "Continue? (Yes):", "Yes\n", 1)) { + reiserfs_close (fs); + exit (0); + } +} + + diff --git a/fsck/journal.c b/fsck/journal.c new file mode 100644 index 0000000..656dc4d --- /dev/null +++ b/fsck/journal.c @@ -0,0 +1,219 @@ +/* + * Copyright 2000 Hans Reiser + */ + +#include "fsck.h" +#include <limits.h> +/*#include <stdlib.h>*/ + + + + + +#define bh_desc(bh) ((struct reiserfs_journal_desc *)((bh)->b_data)) +#define bh_commit(bh) ((struct reiserfs_journal_commit *)((bh)->b_data)) + + + + + + + +static int next_expected_desc (struct super_block * s, struct buffer_head * d_bh) +{ + int offset; + struct reiserfs_journal_desc * desc; + + desc = (struct reiserfs_journal_desc *)d_bh->b_data; + offset = d_bh->b_blocknr - SB_JOURNAL_BLOCK (s); + return SB_JOURNAL_BLOCK (s) + ((offset + desc->j_len + 1 + 1) % JOURNAL_BLOCK_COUNT); +} + + +static int is_valid_transaction (struct super_block * s, struct buffer_head * d_bh) +{ + struct buffer_head * c_bh; + int offset; + struct reiserfs_journal_desc *desc = (struct reiserfs_journal_desc *)d_bh->b_data; + struct reiserfs_journal_commit *commit ; + __u32 block, start_block; + + + offset = d_bh->b_blocknr - SB_JOURNAL_BLOCK (s); + + /* ok, we have a journal description block, lets see if the transaction was valid */ + block = next_expected_desc (s, d_bh) - 1; + start_block = d_bh->b_blocknr; + while(!(c_bh = bread (s->s_dev, block, s->s_blocksize))){ + if (++block == SB_JOURNAL_BLOCK (s) + JOURNAL_BLOCK_COUNT) + block = SB_JOURNAL_BLOCK (s); + if (block == start_block) + return 0; + } + + commit = (struct reiserfs_journal_commit *)c_bh->b_data ; + if (does_desc_match_commit (desc, commit)) { + // if (journal_compare_desc_commit (s, desc, commit)) { +/* printf ("desc and commit block do not match\n");*/ + brelse (c_bh) ; + return 0; + } + brelse (c_bh); + return 1; +} + + +int reiserfs_replay_journal (struct super_block * s) +{ + struct buffer_head * d_bh, * c_bh, * jh_bh; + struct reiserfs_journal_header * j_head; + struct reiserfs_journal_desc * j_desc; + struct reiserfs_journal_commit * j_commit; + unsigned long latest_mount_id; + unsigned long j_cur; + unsigned long j_start; + unsigned long j_size; + unsigned long mount_id, trans_id; + unsigned long t_first, t_last, t_count, t_flushed; + unsigned long t_offset; + int i; + + fsck_progress ("Analyzing journal.."); + + j_start = SB_JOURNAL_BLOCK (s); + j_cur = 0; + j_size = rs_journal_size (s->s_rs); + t_first = 0; + t_last = 0; + latest_mount_id = 0; + + /* look for the transactions with the most recent mount_id */ + for (j_cur = 0; j_cur < j_size; ) { + d_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize); + if (d_bh && who_is_this (d_bh->b_data, d_bh->b_size) == THE_JDESC && is_valid_transaction (s, d_bh)) { + j_desc = (struct reiserfs_journal_desc *)d_bh->b_data; + + mount_id = le32_to_cpu (j_desc->j_mount_id); + trans_id = le32_to_cpu (j_desc->j_trans_id); + + if (mount_id > latest_mount_id) { + /* more recent mount_id found */ + latest_mount_id = mount_id; + t_first = t_last = trans_id; + t_offset = j_cur; + t_count = 1; + } else if (mount_id == latest_mount_id) { + t_count ++; + if (trans_id > t_last) + t_last = trans_id; + if (trans_id < t_first) { + t_first = trans_id; + t_offset = j_cur; + } + } + j_cur += le32_to_cpu (j_desc->j_len) + 1; + } + j_cur ++; + brelse (d_bh); + } + + /* replay only if journal header looks resonable */ + jh_bh = bread (s->s_dev, j_start + j_size, s->s_blocksize); + j_head = (struct reiserfs_journal_header *)(jh_bh->b_data); + + if (latest_mount_id != le32_to_cpu (j_head->j_mount_id)) { + fsck_progress ("nothing to replay (no transactions match to latest mount id)\n"); + brelse (jh_bh); + return 0; + } + /* last transaction flushed - which should not be replayed */ + t_flushed = le32_to_cpu (j_head->j_last_flush_trans_id); + if (t_flushed >= t_last) { + fsck_progress ("nothing to replay (no transactions older than last flushed one found)\n"); + brelse (jh_bh); + return 0; + } + if (t_first > t_flushed + 1) { + if (t_flushed) + fsck_progress ("last flushed trans %lu, the oldest but newer is %lu\n", + t_flushed, t_first); + } else { + /* start replaying with first not flushed transaction */ + t_first = t_flushed + 1; + t_offset = le32_to_cpu (j_head->j_first_unflushed_offset); + } + + fsck_progress ("last flushed trans %lu, mount_id %lu, " + "will replay from %lu up to %lu:Yes?", + t_flushed, latest_mount_id, t_first, t_last); + if (!fsck_user_confirmed (fs, "", "Yes\n", 1)) + die (""); + + /* replay transactions we have found */ + for (j_cur = t_offset; t_first <= t_last; t_first ++) { + unsigned long offset; + + d_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize); + j_desc = (struct reiserfs_journal_desc *)d_bh->b_data; + if (who_is_this (d_bh->b_data, d_bh->b_size) != THE_JDESC || + le32_to_cpu (j_desc->j_mount_id) != latest_mount_id || + le32_to_cpu (j_desc->j_trans_id) != t_first) + die ("reiserfs_replay_journal: desc block not found"); + + offset = j_cur + 1; + j_cur += le32_to_cpu (j_desc->j_len) + 1; + j_cur %= j_size; + c_bh = bread (s->s_dev, j_start + j_cur, s->s_blocksize); + j_commit = (struct reiserfs_journal_commit *)c_bh->b_data; + if (does_desc_match_commit (j_desc, j_commit)) + die ("reiserfs_replay_journal: commit block not found"); + + fsck_log ("Mount_id %lu, transaction %lu, desc block %lu, commit block %lu: (", + latest_mount_id, t_first, d_bh->b_blocknr, c_bh->b_blocknr); + + /* replay one transaction */ + for (i = 0; i < le32_to_cpu (j_desc->j_len); i ++) { + struct buffer_head * in_place, * log; + unsigned long block; + + log = bread (s->s_dev, j_start + ((offset + i) % j_size), s->s_blocksize); + + if (i < JOURNAL_TRANS_HALF) { + block = le32_to_cpu (j_desc->j_realblock[i]); + } else { + block = le32_to_cpu (j_commit->j_realblock[i - JOURNAL_TRANS_HALF]); + } + + if (not_journalable (s, block)) { + fsck_log ("transaction %lu, block %d could not be replayed (%lu)\n", + t_first, i, block); + } else { + fsck_log (" %lu", block); + + in_place = getblk (s->s_dev, block, s->s_blocksize) ; + memcpy (in_place->b_data, log->b_data, s->s_blocksize); + mark_buffer_dirty (in_place); + mark_buffer_uptodate (in_place, 1); + bwrite (in_place); + brelse (in_place); + } + brelse (log); + + } + fsck_log (")\n"); + + brelse (d_bh); + brelse (c_bh); + j_cur ++; + j_cur %= j_size; + + /* update journal header */ + j_head->j_last_flush_trans_id = cpu_to_le32 (t_first); + mark_buffer_dirty (jh_bh); + bwrite (jh_bh); + } + + brelse (jh_bh); + fsck_progress ("Journal replaied\n"); + return 0; +} diff --git a/fsck/lost+found.c b/fsck/lost+found.c new file mode 100644 index 0000000..83e77e7 --- /dev/null +++ b/fsck/lost+found.c @@ -0,0 +1,269 @@ +/* + * Copyright 2000-2001 Hans Reiser + */ + +#include "fsck.h" + + +/* fixme: search_by_key is not needed after any add_entry */ +static __u64 _look_for_lost (reiserfs_filsys_t fs, int link_lost_dirs) +{ + struct key key, prev_key, * rdkey; + INITIALIZE_PATH (path); + int item_pos; + struct buffer_head * bh; + struct item_head * ih; + unsigned long leaves; + int is_it_dir; + static int lost_files = 0; /* looking for lost dirs we calculate amount of + lost files, so that when we will look for + lost files we will be able to stop when + there are no lost files anymore */ + int retval; + __u64 size; + + key = root_dir_key; + + if (!link_lost_dirs && !lost_files) { + /* we have to look for lost files but we know already that there are + no any */ + return 0; + } + + fsck_progress ("Looking for lost %s:\n", link_lost_dirs ? "directories" : "files"); + leaves = 0; + + /* total size of added entries */ + size = 0; + while (1) { + retval = usearch_by_key (fs, &key, &path); + /* fixme: we assume path ends up with a leaf */ + bh = get_bh (&path); + item_pos = get_item_pos (&path); + if (retval != ITEM_FOUND) { + if (item_pos == node_item_number (bh)) { + rdkey = uget_rkey (&path); + if (!rdkey) { + pathrelse (&path); + break; + } + key = *rdkey; + pathrelse (&path); + continue; + } + /* we are on the item in the buffer */ + } + + /* print ~ how many leaves were scanned and how fast it was */ + if (!fsck_quiet (fs)) + print_how_fast (0, leaves++, 50); + + for (ih = get_ih (&path); item_pos < node_item_number (bh); item_pos ++, ih ++) { + if (is_item_reachable (ih)) + continue; + + /* found item which can not be reached */ + if (!is_direntry_ih (ih) && !is_stat_data_ih (ih)) { + continue; + } + + if (is_direntry_ih (ih)) { + /* if this directory has no stat data - try to recover it */ + struct key sd; + struct path tmp; + + sd = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &sd, SD_OFFSET, TYPE_STAT_DATA); + if (usearch_by_key (fs, &sd, &tmp) == ITEM_FOUND) { + /* should not happen - because if there were a stat data - + we would have done with the whole directory */ + pathrelse (&tmp); + continue; + } + stats(fs)->dir_recovered ++; + create_dir_sd (fs, &tmp, &sd); + key = sd; + pathrelse (&path); + goto cont; + } + + + /* stat data marked "not having name" found */ + is_it_dir = ((not_a_directory (B_I_PITEM (bh,ih))) ? 0 : 1); + + if (is_it_dir) { + struct key tmp_key; + INITIALIZE_PATH (tmp_path); + struct item_head * tmp_ih; + + /* there is no need to link empty lost directories into /lost+found */ + tmp_key = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &tmp_key, 0xffffffff, TYPE_DIRENTRY); + usearch_by_key (fs, &tmp_key, &tmp_path); + tmp_ih = get_ih (&tmp_path); + tmp_ih --; + if (not_of_one_file (&tmp_key, tmp_ih)) + reiserfs_panic ("not directory found"); + if (!is_direntry_ih (tmp_ih) || + (deh_offset (B_I_DEH (get_bh (&tmp_path), ih) + + ih_entry_count (tmp_ih) - 1) == DOT_DOT_OFFSET)) { + /* last directory item is either stat data or empty + directory item - do not link this dir into lost+found */ + stats(fs)->empty_lost_dirs ++; + pathrelse (&tmp_path); + continue; + } + pathrelse (&tmp_path); + } + + if (link_lost_dirs && !is_it_dir) { + /* we are looking for directories and it is not a dir */ + lost_files ++; + continue; + } + + stats(fs)->lost_found ++; + + { + struct key obj_key = {0, 0, {{0, 0},}}; + char * lost_name; + struct item_head tmp_ih; + int pos_in_map; + + /* key to continue */ + key = ih->ih_key; + key.k_objectid ++; + + tmp_ih = *ih; + if (is_objectid_really_used (semantic_id_map (fs), ih->ih_key.k_objectid, + &pos_in_map)) { + /* objectid is used, relocate an object */ + stats(fs)->oid_sharing ++; + if (fsck_fix_non_critical (fs)) { + if (is_it_dir) { + relocate_dir (&tmp_ih, 1); + stats(fs)->oid_sharing_dirs_relocated ++; + } else { + relocate_file (&tmp_ih, 1); + stats(fs)->oid_sharing_files_relocated ++; + } + } + } else { + if (!is_it_dir) + mark_objectid_really_used (semantic_id_map (fs), ih->ih_key.k_objectid); + } + + asprintf (&lost_name, "%u_%u", le32_to_cpu (tmp_ih.ih_key.k_dir_id), + le32_to_cpu (tmp_ih.ih_key.k_objectid)); + + /* entry in lost+found directory will point to this key */ + obj_key.k_dir_id = tmp_ih.ih_key.k_dir_id; + obj_key.k_objectid = tmp_ih.ih_key.k_objectid; + + + pathrelse (&path); + + /* 0 does not mean anyting - item w/ "." and ".." already + exists and reached, so only name will be added */ + size += reiserfs_add_entry (fs, &lost_found_dir_key, lost_name, &obj_key, 0/*fsck_need*/); + + if (is_it_dir) { + /* fixme: we hope that if we will try to pull all the + directory right now - then there will be less + lost_found things */ + fsck_progress ("\tChecking lost dir \"%s\":", lost_name); + rebuild_semantic_pass (&obj_key, &lost_found_dir_key, /*dot_dot*/0, /*reloc_ih*/0); + fsck_progress ("ok\n"); + stats(fs)->lost_found_dirs ++; + } else { + if (usearch_by_key (fs, &obj_key, &path) != ITEM_FOUND) + reiserfs_panic ("look_for_lost: lost file stat data %K not found", + &obj_key); + + /* check_regular_file does not mark stat data reachable */ + mark_item_reachable (get_ih (&path), get_bh (&path)); + mark_buffer_dirty (get_bh (&path)); + + rebuild_check_regular_file (&path, get_item (&path), 0/*reloc_ih*/); + pathrelse (&path); + + stats(fs)->lost_found_files ++; + lost_files --; + } + + free (lost_name); + goto cont; + } + } /* for */ + + prev_key = key; + get_next_key (&path, item_pos - 1, &key); + if (comp_keys (&prev_key, &key) != -1) + reiserfs_panic ("pass_3a: key must grow 2: prev=%k next=%k", + &prev_key, &key); + pathrelse (&path); + + cont: + if (!link_lost_dirs && !lost_files) { + break; + } + } + + pathrelse (&path); + +#if 0 + /* check names added we just have added to/lost+found. Those names are + marked DEH_Lost_found flag */ + fsck_progress ("Checking lost+found directory.."); fflush (stdout); + check_semantic_tree (&lost_found_dir_key, &root_dir_key, 0, 1/* lost+found*/); + fsck_progress ("ok\n"); +#endif + + if (!link_lost_dirs && lost_files) + fsck_log ("look_for_lost: %d files seem to left not linked to lost+found\n", + lost_files); + + return size; + +} + + +void pass_3a_look_for_lost (reiserfs_filsys_t fs) +{ + INITIALIZE_PATH (path); + struct item_head * ih; + void * sd; + __u64 size, sd_size; + __u32 blocks; + + fsck_progress ("Pass 3a (looking for lost files):\n"); + + /* when warnings go not to stderr - separate then in the log */ + if (fsck_log_file (fs) != stderr) + fsck_log ("####### Pass 3a (lost+found pass) #########\n"); + + + /* look for lost dirs first */ + size = _look_for_lost (fs, 1); + + /* link files which are still lost */ + size += _look_for_lost (fs, 0); + + /* update /lost+found sd_size and sd_blocks (nlink is correct already) */ + if (usearch_by_key (fs, &lost_found_dir_key, &path) != ITEM_FOUND) + reiserfs_panic ("look_for_lost: /lost+found stat data %K not found", + &lost_found_dir_key); + ih = get_ih (&path); + sd = get_item (&path); + get_sd_size (ih, sd, &sd_size); + size += sd_size; + blocks = dir_size2st_blocks (fs->s_blocksize, size); + + set_sd_size (ih, sd, &size); + set_sd_blocks (ih, sd, &blocks); + mark_buffer_dirty (get_bh (&path)); + pathrelse (&path); + + stage_report (0x3a, fs); +} + diff --git a/fsck/main.c b/fsck/main.c new file mode 100644 index 0000000..9f9575e --- /dev/null +++ b/fsck/main.c @@ -0,0 +1,709 @@ +/* + * Copyright 1996-2000 Hans Reiser + */ +#include "fsck.h" +#include <getopt.h> + +#include "../version.h" + + + +#define print_usage_and_exit() die ("Usage: %s [options] "\ +" device\n"\ +"\n"\ +"Options:\n\n"\ +" --check\t\tconsistency checking (default)\n"\ +" --rebuild-sb\t\tsuper block checking and rebuilding if needed\n"\ +" --rebuild-tree\tforce fsck to rebuild filesystem from scratch\n"\ +" \t\t\t(takes a long time)\n"\ +" --interactive, -i\tmake fsck to stop after every stage\n"\ +" -l | --logfile logfile\n"\ +" \t\t\tmake fsck to complain to specifed file\n"\ +" -b | --scan-marked-in-bitmap file\n"\ +" \t\t\tbuild tree of blocks marked in the bitmapfile\n"\ +" -c | --create-bitmap-file\n"\ +" \t\t\tsave bitmap of found leaves\n"\ +" -x | --fix-fixable\tfix corruptions which can be fixed w/o --rebuild-tree\n"\ +" -o | --fix-non-critical\n"\ +" \t\t\tfix strange modes, file sizes to real size and\n"\ +" \t\t\trelocate files using busy objectids\n"\ +" -q | --quiet\t\tno speed info\n"\ +" -n | --nolog\t\t suppresses all logs\n"\ +" -V\t\t\tprints version and exits\n"\ +" -a\t\t\tmakes fsck to do nothing\n"\ +" -p\t\t\tdo nothing, exist for compatibility with fsck(8)\n"\ +" -r\n", argv[0]); + + + + +/* fsck is called with one non-optional argument - file name of device + containing reiserfs. This function parses other options, sets flags + based on parsing and returns non-optional argument */ +static char * parse_options (struct fsck_data * data, int argc, char * argv []) +{ + int c; + static int mode = FSCK_CHECK; + + data->scan_area = USED_BLOCKS; + while (1) { + static struct option options[] = { + /* modes */ + {"check", no_argument, &mode, FSCK_CHECK}, + {"rebuild-sb", no_argument, &mode, FSCK_SB}, + {"rebuild-tree", no_argument, &mode, FSCK_REBUILD}, +/* + {"fast-rebuild", no_argument, &opt_fsck_mode, FSCK_FAST_REBUILD}, +*/ + + /* options */ + {"logfile", required_argument, 0, 'l'}, + {"interactive", no_argument, 0, 'i'}, + {"fix-fixable", no_argument, 0, 'x'}, + {"fix-non-critical", no_argument, 0, 'o'}, + {"quiet", no_argument, 0, 'q'}, + {"nolog", no_argument, 0, 'n'}, + + /* if file exists ad reiserfs can be load of it - only + blocks marked used in that bitmap will be read */ + {"scan-marked-in-bitmap", required_argument, 0, 'b'}, + + /* */ + {"create-leaf-bitmap", required_argument, 0, 'c'}, + + /* all blocks will be read */ + {"scan-whole-partition", no_argument, 0, 'S'}, + + /* special option: will mark free blocks used, zero all + unformatted node pointers and mark them free */ + {"zero-files", no_argument, &mode, FSCK_ZERO_FILES}, + {0, 0, 0, 0} + }; + int option_index; + + c = getopt_long (argc, argv, "iql:b:Sc:xoVaprt:n", + options, &option_index); + if (c == -1) + break; + + switch (c) { + case 0: + /* long option specifying fsck mode is found */ + break; + + case 'i': /* --interactive */ + data->options |= OPT_INTERACTIVE; + break; + + case 'q': /* --quiet */ + data->options |= OPT_QUIET; + break; + + case 'l': /* --logfile */ + asprintf (&data->log_file_name, "%s", optarg); + data->log = fopen (optarg, "w"); + if (!data->log) + fprintf (stderr, "reiserfsck: could not open \'%s\': %m", optarg); + + break; + + case 'b': /* --scan-marked-in-bitmap */ + /* will try to load a bitmap from a file and read only + blocks marked in it. That bitmap could be created by + previous run of reiserfsck with -c */ + asprintf (&data->bitmap_file_name, "%s", optarg); + data->scan_area = EXTERN_BITMAP; + break; + + case 'S': /* --scan-whole-partition */ + data->scan_area = ALL_BLOCKS; + break; + + case 'c': /* --create-leaf-bitmap */ + asprintf (&data->new_bitmap_file_name, "%s", optarg); + data->options |= OPT_SAVE_EXTERN_BITMAP; + break; + + case 'x': /* --fix-fixable */ + data->options |= OPT_FIX_FIXABLE; + break; + + case 'o': /* --fix-non-critical */ + data->options |= OPT_FIX_NON_CRITICAL; + break; + + case 'n': /* --nolog */ + data->options |= OPT_SILENT; + break; + + case 'V': + case 'p': /* these say reiserfsck to do nothing */ + case 'r': + case 'a': + mode = DO_NOTHING; + break; + + case 't': + mode = DO_TEST; + data->test = atoi (optarg); + break; + + default: + print_usage_and_exit(); + } + } + + if (optind != argc - 1 && mode != DO_NOTHING) + /* only one non-option argument is permitted */ + print_usage_and_exit(); + + data->mode = mode; + if (!data->log) + data->log = stderr; + + return argv[optind]; +} + + +reiserfs_filsys_t fs; + + + +static void reset_super_block (reiserfs_filsys_t fs) +{ + set_free_blocks (fs->s_rs, SB_BLOCK_COUNT (fs)); + set_root_block (fs->s_rs, ~0); + set_tree_height (fs->s_rs, ~0); + + /* make file system invalid unless fsck done () */ + set_state (fs->s_rs, REISERFS_ERROR_FS); + + + if (is_reiser2fs_magic_string (fs->s_rs)) { + set_version (fs->s_rs, REISERFS_VERSION_2); + } + if (is_reiserfs_magic_string (fs->s_rs)) { + set_version (fs->s_rs, REISERFS_VERSION_1); + } + + /* can be not set yet. If so, hash function will be set when first dir + entry will be found */ + fs->s_hash_function = code2func (rs_hash (fs->s_rs)); + + /* objectid map is not touched */ + + mark_buffer_dirty (fs->s_sbh); + bwrite (fs->s_sbh); + +} + + +reiserfs_bitmap_t uninsertable_leaf_bitmap; + +int g_blocks_to_read; + + +/* on-disk bitmap is read, fetch it. create new bitmap, mark used blocks which + are always used (skipped, super block, journal area, bitmaps), create other + auxiliary bitmaps */ +static void init_bitmaps (reiserfs_filsys_t fs) +{ + unsigned long i; + unsigned long block_count; + unsigned long tmp; + + block_count = SB_BLOCK_COUNT (fs); + + switch (stats (fs)->scan_area) { + case ALL_BLOCKS: + fsck_disk_bitmap (fs) = reiserfs_create_bitmap (block_count); + reiserfs_bitmap_fill (fsck_disk_bitmap (fs)); + fsck_progress ("Whole device (%d blocks) is to be scanned\n", + reiserfs_bitmap_ones (fsck_disk_bitmap (fs))); + break; + + case USED_BLOCKS: + fsck_progress ("Loading on-disk bitmap .. "); + fsck_disk_bitmap (fs) = reiserfs_create_bitmap (block_count); + reiserfs_fetch_disk_bitmap (fsck_disk_bitmap (fs), fs); + fsck_progress ("%d bits set - done\n", + reiserfs_bitmap_ones (fsck_disk_bitmap (fs))); + break; + + case EXTERN_BITMAP: + fsck_disk_bitmap (fs) = reiserfs_bitmap_load (stats (fs)->bitmap_file_name); + if (!fsck_disk_bitmap (fs)) + reiserfs_panic ("could not load fitmap from \"%s\"", + stats (fs)->bitmap_file_name); + break; + + default: + reiserfs_panic ("No area to scan specified"); + } + + + /* pass 0 will skip super block and journal areas and bitmap blocks, find + how many blocks have to be read */ + tmp = 0; + for (i = 0; i <= fs->s_sbh->b_blocknr; i ++) { + if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i)) + continue; + reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i); + tmp ++; + } + + /* unmark bitmaps */ + for (i = 0; i < rs_bmap_nr (fs->s_rs); i ++) { + unsigned long block; + + block = SB_AP_BITMAP (fs)[i]->b_blocknr; + if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), block)) + continue; + reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), block); + tmp ++; + } + + /* unmark journal area */ + for (i = rs_journal_start (fs->s_rs); + i <= rs_journal_start (fs->s_rs) + rs_journal_size (fs->s_rs); i ++) { + if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i)) + continue; + reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i); + tmp ++; + } + reiserfs_warning (stderr, "Skipping %d blocks (super block, journal, " + "bitmaps) %d blocks will be read\n", + tmp, reiserfs_bitmap_ones (fsck_disk_bitmap (fs))); + +#if 0 + { + int tmp = 0; + int tmp2 = 0; + int tmp3 = 0; + int j; + + for (i = 0; i < block_count; i += 32) { + if (i + 32 < block_count && !*(int *)&(fsck_disk_bitmap (fs)->bm_map[i/8])) { + tmp2 ++; + continue; + } + tmp3 ++; + for (j = 0; j < 32 && ((i + j) < block_count); j ++) { + if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i + j)) + continue; + if (not_data_block (fs, i + j)) { + reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i + j); + tmp ++; + continue; + } + } + } + /* + for (i = 0; i < block_count; i ++) { + if (!fsck_disk_bitmap (fs)->bm_map[i / 8]) + continue; + if (!reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i)) + continue; + if (not_data_block (fs, i)) { + + if (reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), i)) + tmp ++; + reiserfs_bitmap_clear_bit (fsck_disk_bitmap (fs), i); + continue; + } + } +*/ + reiserfs_warning (stderr, "%d not data blocks cleared (skipped %d checked %d)\n", tmp, tmp2, tmp3); + } +#endif + + + fsck_new_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + + /* mark_block_used skips 0, ste the bit explicitly */ + reiserfs_bitmap_set_bit (fsck_new_bitmap (fs), 0); + + /* mark other skipped blocks and super block used */ + for (i = 1; i <= SB_BUFFER_WITH_SB (fs)->b_blocknr; i ++) + mark_block_used (i); + + /* mark bitmap blocks as used */ + for (i = 0; i < SB_BMAP_NR (fs); i ++) + mark_block_used (SB_AP_BITMAP (fs)[i]->b_blocknr); + + /* mark journal area as used */ + for (i = 0; i < JOURNAL_BLOCK_COUNT + 1; i ++) + mark_block_used (i + SB_JOURNAL_BLOCK (fs)); + + + uninsertable_leaf_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_bitmap_fill (uninsertable_leaf_bitmap); + + fsck_allocable_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_bitmap_fill (fsck_allocable_bitmap (fs)); + +} + + + +#define REBUILD_WARNING \ +"\nThis is an experimental version of reiserfsck, MAKE A BACKUP FIRST!\n\ +Don't run this program unless something is broken. \n\ +Some types of random FS damage can be recovered\n\ +from by this program, which basically throws away the internal nodes\n\ +of the tree and then reconstructs them. This program is for use only\n\ +by the desperate, and is of only beta quality. Email\n\ +reiserfs@devlinux.com with bug reports. \nWill rebuild the filesystem tree\n" + +/* + warning #2 + you seem to be running this automatically. you are almost + certainly doing it by mistake as a result of some script that + doesn't know what it does. doing nothing, rerun without -p if you + really intend to do this. */ + +void warn_what_will_be_done (struct fsck_data * data) +{ + fsck_progress ("\n"); + + /* warn about fsck mode */ + switch (data->mode) { + case FSCK_CHECK: + fsck_progress ("Will read-only check consistency of the partition\n"); + if (data->options & OPT_FIX_FIXABLE) + fsck_progress ("\tWill fix what can be fixed w/o --rebuild-tree\n"); + break; + + case FSCK_SB: + fsck_progress ("Will check SB and rebuild if it is needed\n"); + break; + + case FSCK_REBUILD: + { + fsck_progress (REBUILD_WARNING); + if (data->options & OPT_INTERACTIVE) + fsck_progress ("\tWill stop after every stage and ask for " + "confirmation before continuing\n"); + if (data->options & OPT_SAVE_EXTERN_BITMAP) + fsck_progress ("Will save list of found leaves in '%s'\n", + data->new_bitmap_file_name); + if (data->bitmap_file_name) + fsck_progress ("\tWill try to load bitmap of leaves from file '%s'\n", + data->bitmap_file_name); + if (data->options & OPT_FIX_NON_CRITICAL) + fsck_progress ("\tWill fix following non-critical things:\n" + "\t\tunknown file modes will be set to regular files\n" + "\t\tfile sizes will be set to real file size\n" + "\t\tfiles sharing busy inode number will be relocated\n"); + break; + } + + case FSCK_ZERO_FILES: + fsck_progress ("Will zero existing files and mark free blocks as used\n"); + } + + fsck_progress ("Will put log info to '%s'\n", (data->log != stderr) ? + data->log_file_name : "stderr"); + + if (!user_confirmed ("Do you want to run this program?[N/Yes] (note need to type Yes):", "Yes\n")) + exit (0); +} + + +static void start_rebuild (reiserfs_filsys_t fs) +{ + reset_super_block (fs); + init_bitmaps (fs); + + proper_id_map (fs) = init_id_map (); + semantic_id_map (fs) = init_id_map (); +} + + +/* called before semantic pass starts */ +static void end_rebuilding (reiserfs_filsys_t fs) +{ + reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs); + flush_objectid_map (proper_id_map (fs), fs); + set_fsck_state (fs->s_rs, TREE_IS_BUILT); + set_free_blocks (fs->s_rs, reiserfs_bitmap_zeros (fsck_new_bitmap (fs))); + + mark_buffer_dirty (SB_BUFFER_WITH_SB (fs)); + + /* write all dirty blocks */ + fsck_progress ("Syncing.."); fflush (stdout); + reiserfs_flush (fs); + fsck_progress ("done\n"); fflush (stdout); + + /* release what will not be needed */ + reiserfs_delete_bitmap (fsck_disk_bitmap (fs)); + reiserfs_delete_bitmap (fsck_allocable_bitmap (fs)); + + /* FIXME: could be not a bitmap */ + reiserfs_delete_bitmap (uninsertable_leaf_bitmap); + + if (fsck_user_confirmed (fs, "Tree building completed. " + "You can stop now and restart from this point later " + "(this is probably not what you need). Do you want to stop? ", + "Yes\n", 0/*default*/)) { + reiserfs_close (fs); + exit (4); + } +} + + +static int skip_rebuilding (reiserfs_filsys_t fs) +{ + if (fsck_state (fs->s_rs) == TREE_IS_BUILT) { + if (fsck_user_confirmed (fs, "S+ tree of filesystem looks built. Skip rebuilding? ", "Yes\n", 0/*default*/)) { + + fsck_new_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_fetch_disk_bitmap (fsck_new_bitmap (fs), fs); + + proper_id_map (fs) = init_id_map (); + fetch_objectid_map (proper_id_map (fs), fs); + + semantic_id_map (fs) = init_id_map (); + + return 1; + } + } + return 0; +} + + +static void start_continuing (reiserfs_filsys_t fs) +{ + fsck_allocable_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_bitmap_copy (fsck_allocable_bitmap (fs), fsck_new_bitmap (fs)); +} + + +static void the_end (reiserfs_filsys_t fs) +{ + reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs); + flush_objectid_map (proper_id_map (fs), fs); + set_fsck_state (fs->s_rs, 0); + set_free_blocks (fs->s_rs, reiserfs_bitmap_zeros (fsck_new_bitmap (fs))); + set_state (fs->s_rs, REISERFS_VALID_FS); + mark_buffer_dirty (SB_BUFFER_WITH_SB (fs)); + + /* write all dirty blocks */ + fsck_progress ("Syncing.."); fflush (stderr); + reiserfs_flush (fs); + sync (); + fsck_progress ("done\n"); fflush (stderr); + + reiserfs_delete_bitmap (fsck_new_bitmap (fs)); + + free_id_map (&proper_id_map(fs)); + if (semantic_id_map(fs)) + free_id_map (&semantic_id_map(fs)); + + reiserfs_close (fs); + fsck_progress ("Done\n"); fflush (stderr); +} + + +static void rebuild_tree (reiserfs_filsys_t fs) +{ + if (is_mounted (fs->file_name)) { + fsck_progress ("rebuild_tree: can not rebuild tree of mounted filesystem\n"); + return; + } + + reiserfs_reopen (fs, O_RDWR); + + /* FIXME: for regular file take care of of file size */ + + /* rebuild starts with journal replaying */ + reiserfs_replay_journal (fs); + + + if (!skip_rebuilding (fs)) { + fsck_progress ("Rebuilding..\n"); + start_rebuild (fs); + + pass_0 (fs); + + /* passes 1 and 2. building of the tree */ + pass_1_pass_2_build_the_tree (); + + end_rebuilding (fs); + } + + /* re-building of filesystem tree is now separated of sematic pass of the + fsck */ + start_continuing (fs); + + /* 3. semantic pass */ + pass_3_semantic (); + + /* if --lost+found is set - link unaccessed directories to lost+found + directory */ + pass_3a_look_for_lost (fs); + + /* 4. look for unaccessed items in the leaves */ + pass_4_check_unaccessed_items (); + + the_end (fs); + +} + + +static void zero_files (reiserfs_filsys_t fs) +{ + init_bitmaps (fs); + reiserfs_reopen (fs, O_RDWR); + pass_0 (fs); +} + + +/* check umounted or read-only mounted filesystems only */ +static void check_fs (reiserfs_filsys_t fs) +{ + if (!is_mounted (fs->file_name)) { + /* filesystem is not mounted, replay journal before checking */ + reiserfs_reopen (fs, O_RDWR); + + reiserfs_replay_journal (fs); + + reiserfs_reopen (fs, O_RDONLY); + } else { + /* filesystem seems mounted. we do not check filesystems mounted with + r/w permissions */ + if (!is_mounted_read_only (fs->file_name)) { + fsck_progress ("Device %s is mounted w/ write permissions, can not check it\n", + fs->file_name); + reiserfs_close (fs); + exit (0); + } + fsck_progress ("Filesystem seems mounted read-only. Skipping journal replay..\n"); + + if (fsck_fix_fixable (fs)) { + fsck_progress ("--fix-fixable ignored\n"); + stats(fs)->options &= ~OPT_FIX_FIXABLE; + } + } + + fsck_disk_bitmap (fs) = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + reiserfs_fetch_disk_bitmap (fsck_disk_bitmap (fs), fs); + + if (fsck_fix_fixable (fs)) + reiserfs_reopen (fs, O_RDWR); + + /*proper_id_map (fs) = init_id_map ();*/ + semantic_id_map (fs) = init_id_map (); + + check_fs_tree (fs); + + semantic_check (); + + reiserfs_delete_bitmap (fsck_disk_bitmap (fs)); + /*free_id_map (proper_id_map (fs));*/ + free_id_map (&semantic_id_map (fs)); + reiserfs_close (fs); +} + + +#include <sys/resource.h> + +int main (int argc, char * argv []) +{ + char * file_name; + struct fsck_data * data; + struct rlimit rlim = {0xffffffff, 0xffffffff}; + + print_banner ("reiserfsck"); + + /* this is only needed (and works) when running under 2.4 on regural files */ + if (setrlimit (RLIMIT_FSIZE, &rlim) == -1) { + reiserfs_warning (stderr, "could not setrlimit: %m"); + } + + data = getmem (sizeof (struct fsck_data)); + + file_name = parse_options (data, argc, argv); + + if (data->mode == DO_NOTHING) { + freemem (data); + return 0; + } + + warn_what_will_be_done (data); /* and ask confirmation Yes */ + fs = reiserfs_open (file_name, O_RDONLY, 0, data); + if (!fs) + die ("reiserfsck: could not open filesystem on \"%s\"", file_name); + + + if (fsck_mode (fs) == FSCK_SB) { + reiserfs_reopen (fs, O_RDWR); + rebuild_sb (fs); + reiserfs_close (fs); + return 0; + } + + if (no_reiserfs_found (fs)) { + fsck_progress ("reiserfsck: --rebuild-sb may restore reiserfs super block\n"); + reiserfs_close (fs); + return 0; + } + + + fs->block_allocator = reiserfsck_reiserfs_new_blocknrs; + fs->block_deallocator = reiserfsck_reiserfs_free_block; + + + + if (fsck_mode (fs) == FSCK_CHECK) { + check_fs (fs); + return 0; + } + +#ifdef FAST_REBUILD_READY /* and tested */ + if (opt_fsck_mode == FSCK_FAST_REBUILD) { + __u32 root_block = SB_ROOT_BLOCK(fs); + reopen_read_write (file_name); + printf ("Replaying log.."); + reiserfs_replay_journal (fs); + printf ("done\n"); + if (opt_fsck == 1) + printf ("ReiserFS : checking %s\n",file_name); + else + printf ("Rebuilding..\n"); + + + reset_super_block (fs); + SB_DISK_SUPER_BLOCK(fs)->s_root_block = cpu_to_le32 (root_block); + init_bitmaps (fs); + + /* 1,2. building of the tree */ + recover_internal_tree(fs); + + /* 3. semantic pass */ + pass3_semantic (); + + /* if --lost+found is set - link unaccessed directories to + lost+found directory */ + look_for_lost (fs); + + /* 4. look for unaccessed items in the leaves */ + pass4_check_unaccessed_items (); + + end_fsck (); + } +#endif /* FAST REBUILD READY */ + + + if (fsck_mode (fs) == FSCK_ZERO_FILES) + zero_files (fs); + + if (fsck_mode (fs) != FSCK_REBUILD && fsck_mode (fs) != DO_TEST) + return 0; + + + /* the --rebuild-tree is here */ + rebuild_tree (fs); + return 0; + +} diff --git a/fsck/pass0.c b/fsck/pass0.c new file mode 100644 index 0000000..8b7b065 --- /dev/null +++ b/fsck/pass0.c @@ -0,0 +1,1502 @@ +/* + * Copyright 1996-2001 Hans Reiser + */ + +#include "fsck.h" + + + +static unsigned long tmp_zeroed; + +/* pass 0 scans the partition (used part). It creates two maps which will be + used on the pass 1. These are a map of nodes looking like leaves and a map + of "bad" unformatted nodes. */ + + +/* leaves */ +reiserfs_bitmap_t leaves_bitmap; +#define pass0_is_leaf(block) __is_marked (leaves, block) +#define pass0_mark_leaf(block) __mark (leaves, block) + +/* nodes which are referred to from only one indirect item */ +reiserfs_bitmap_t good_unfm_bitmap; +#define pass0_is_good_unfm(block) __is_marked (good_unfm, block) +#define pass0_mark_good_unfm(block) __mark (good_unfm, block) +#define pass0_unmark_good_unfm(block) __unmark (good_unfm, block) + +/* nodes which are referred to from more than one indirect item */ +reiserfs_bitmap_t bad_unfm_bitmap; +#define pass0_is_bad_unfm(block) __is_marked (bad_unfm, block) +#define pass0_mark_bad_unfm(block) __mark (bad_unfm, block) +#define pass0_unmark_bad_unfm(block) __unmark (bad_unfm, block) + + + +/* there are three way to say of which blocks the tree should be built off: + default - */ +static void make_aux_bitmaps (reiserfs_filsys_t fs) +{ + + /* bitmap of leaves found on the device. It will be saved if -c specified */ + leaves_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + + good_unfm_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + + bad_unfm_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + +} + + +/* register block some indirect item points to */ +static void register_unfm (unsigned long block) +{ + if (!pass0_is_good_unfm (block) && !pass0_is_bad_unfm (block)) { + /* this block was not pointed by other indirect items yet */ + pass0_mark_good_unfm (block); + return; + } + + if (pass0_is_good_unfm (block)) { + /* block was pointed once already, unmark it in bitmap of good + unformatted nodes and mark in bitmap of bad pointers */ + pass0_unmark_good_unfm (block); + pass0_mark_bad_unfm (block); + return; + } + + assert (pass0_is_bad_unfm (block)); +} + + +/* 'upper' item is correct if 'upper + 2' exists and its key is greater than + key of 'upper' */ +static int upper_correct (struct buffer_head * bh, struct item_head * upper, + int upper_item_num) +{ + if (upper_item_num + 2 < B_NR_ITEMS (bh)) { + if (comp_keys (&upper->ih_key, &(upper + 2)->ih_key) != -1) + /* item-num's item is out of order of order */ + return 0; + return 1; + } + + /* there is no item above the "bad pair" */ + return 2; +} + + +/* 'lower' item is correct if 'lower - 2' exists and its key is smaller than + key of 'lower' */ +static int lower_correct (struct buffer_head * bh, struct item_head * lower, + int lower_item_num) +{ + if (lower_item_num - 2 >= 0) { + if (comp_keys (&(lower - 2)->ih_key, &lower->ih_key) != -1) + return 0; + return 1; + } + return 2; +} + + +/* return 1 if something was changed */ +static int correct_key_format (struct item_head * ih) +{ + int dirty = 0; + + if (is_stat_data_ih (ih)) { + /* for stat data we have no way to check whether key format in item + head matches to the key format found from the key directly */ + if (ih_item_len (ih) == SD_V1_SIZE) { + if (ih_key_format (ih) != KEY_FORMAT_1) { + fsck_log ("correct_key_format: ih_key_format of (%H) is set to format 1\n", + ih); + set_key_format (ih, KEY_FORMAT_1); + return 1; + } + return 0; + } + if (ih_item_len (ih) == SD_SIZE) { + if (ih_key_format (ih) != KEY_FORMAT_2) { + fsck_log ("correct_key_format: ih_key_format of (%H) is set to format 2\n", + ih); + set_key_format (ih, KEY_FORMAT_2); + return 1; + } + return 0; + } + + die ("stat data of wrong length"); + } + + if (key_format (&ih->ih_key) != ih_key_format (ih)) { + fsck_log ("correct_key_format: ih_key_format of (%H) is set to format found in the key\n", + ih); + set_key_format (ih, key_format (&ih->ih_key)); + dirty = 1; + } + + if (type_unknown (&ih->ih_key)) { + /* FIXME: */ + set_type (key_format (&ih->ih_key), &ih->ih_key, TYPE_DIRECT); + dirty = 1; + } + + return dirty; +} + +#if 0 +/* fixme: we might try all available hashes */ +static int prob_name (reiserfs_filsys_t fs, + char ** name, int max_len, __u32 deh_offset) +{ + int start; /* */ + int len; + + for (start = 0; start < max_len; start ++) { + for (len = 0; len < max_len - start; len ++) { + if (is_properly_hashed (fs, *name + start, len + 1, deh_offset)) { + *name = *name + start; + return len + 1; + } + } + } + return 0; +} +#endif + + +static void hash_hits_init (reiserfs_filsys_t fs) +{ + stats (fs)->hash_amount = known_hashes (); + stats (fs)->hash_hits = getmem (sizeof (unsigned long) * stats (fs)->hash_amount); + return; +} + + +static void add_hash_hit (reiserfs_filsys_t fs, int hash_code) +{ + stats (fs)->hash_hits [hash_code] ++; +} + + +/* deh_location look reasonable, try to find name length. return 0 if + we failed */ +static int try_to_get_name_length (struct item_head * ih, struct reiserfs_de_head * deh, + int i) +{ + int len; + + len = name_length (ih, deh, i); + if (i == 0 || !de_bad_location (deh - 1)) + return (len > 0) ? len : 0; + + /* previous entry had bad location so we had no way to find + name length */ + return 0; +} + + + +/* define this if you are using -t to debug recovering of corrupted directory + item */ +#define DEBUG_VERIFY_DENTRY +#undef DEBUG_VERIFY_DENTRY + + +/* check directory item and try to recover something */ +static int verify_directory_item (reiserfs_filsys_t fs, struct buffer_head * bh, + int item_num) +{ + struct item_head * ih; + struct item_head tmp; + char * item; + struct reiserfs_de_head * deh; + char * name; + int name_len; + int bad; + int i, j; +#if 0 + int bad_entries; /* how many bad neighboring entries */ + int total_entry_len; + char * entries, * end; +#endif + int dirty; + int entry_count; + int hash_code; + int bad_locations; + +#ifdef DEBUG_VERIFY_DENTRY + char * direntries; +#endif + + + ih = B_N_PITEM_HEAD (bh, item_num); + item = B_I_PITEM (bh,ih); + deh = (struct reiserfs_de_head *)item; + + dirty = 0; + bad_locations = 0; + entry_count = ih_entry_count (ih); + + + /* check deh_location */ + for (i = 0; i < ih_entry_count (ih); i ++) { + /* silently fix deh_state */ + if (deh [i].deh_state != (1 << DEH_Visible)) { + deh [i].deh_state = cpu_to_le16 (1 << DEH_Visible); + mark_buffer_dirty (bh); + } + if (dir_entry_bad_location (deh + i, ih, !i)) + mark_de_bad_location (deh + i); + } + +#ifdef DEBUG_VERIFY_DENTRY + direntries = getmem (ih_entry_count (ih) * sizeof (int)); + + printf ("entries with bad locations: "); + for (i = 0; i < ih_entry_count (ih); i ++) { + if (de_bad_location (deh + i)) + printf ("%d ", i); + } + printf ("\n"); +#endif /* DEBUG_VERIFY_DENTRY */ + + + /* find entries names in which have mismatching deh_offset */ + for (i = ih_entry_count (ih) - 1; i >= 0; i --) { + if (de_bad (deh + i)) + /* bad location */ + continue; + + if (i) { + if (deh_location (deh + i - 1) < deh_location (deh + i)) + mark_de_bad_location (deh + i - 1); + } + + name = name_in_entry (deh + i, i); + /* we found a name, but we not always we can get its length as + it depends on deh_location of previous entry */ + name_len = try_to_get_name_length (ih, deh + i, i); + +#ifdef DEBUG_VERIFY_DENTRY + if (name_len == 0) + printf ("trying to find name length for %d-th entry\n", i); +#endif /* DEBUG_VERIFY_DENTRY */ + if (is_dot (name, name_len)) { + if (i != 0) + fsck_log ("block %lu: item %d: \".\" is %d-th entry\n", + bh->b_blocknr, item_num, i); + /* check and fix "." */ + + if (deh_offset (deh + i) != DOT_OFFSET) { + deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET); + mark_buffer_dirty (bh); + } + /* "." must point to the directory it is in */ + if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { + fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " + "pointing to (%K) instead of (%K)\n", + bh->b_blocknr, ih, + &(deh[i].deh_dir_id), &(ih->ih_key)); + deh[i].deh_dir_id = key_dir_id (&ih->ih_key); + deh[i].deh_objectid = key_objectid (&ih->ih_key); + mark_buffer_dirty (bh); + } + } else if (is_dot_dot (name, name_len)) { + if (i != 1) + fsck_log ("block %lu: item %d: \"..\" is %d-th entry\n", + bh->b_blocknr, item_num, i); + + /* check and fix ".." */ + if (deh_offset (deh + i) != DOT_DOT_OFFSET) + deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); + } else { + int min_length, max_length; + + /* check other name */ + + if (name_len == 0) { + /* we do not know the length of name - we will try to find it */ + min_length = 1; + max_length = item + ih_item_len (ih) - name; + } else + /* we kow name length, so we will try only one name length */ + min_length = max_length = name_len; + + for (j = min_length; j <= max_length; j ++) { + hash_code = find_hash_in_use (name, j, + GET_HASH_VALUE (deh_offset (deh + i)), + rs_hash (fs->s_rs)); + add_hash_hit (fs, hash_code); + if (code2func (hash_code) != 0) { + /* deh_offset matches to some hash of the name */ + if (!name_len) { + fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " + "matching to deh_offset %u. FIXME: should set deh_location " + "of previous entry (not ready)\n", + bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); + /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ + if (i) { + deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) + + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); + mark_de_good_location (deh + i - 1); + mark_buffer_dirty (bh); + } + } + break; + } + } + if (j == max_length + 1) { + /* deh_offset does not match to anything. it will be + deleted for now, but maybe we could just fix a + deh_offset if it is in ordeer */ + mark_de_bad_offset (deh + i); + } + } + } /* for */ + + + +#if 0 + /* find entries names in which have mismatching deh_offset */ + for (i = 0; i < ih_entry_count (ih); i ++) { + if (de_bad (deh + i)) + /* bad location */ + continue; + + name = name_in_entry (deh + i, i); + /* we found a name, but we not always we can get its length as + it depends on deh_location of previous entry */ + name_len = try_to_get_name_length (ih, deh + i, i); + + if (i == 0 && is_dot (name, name_len)) { + + /* check and fix "." */ + + if (deh_offset (deh + i) != DOT_OFFSET) { + deh[i].deh_offset = cpu_to_le32 (DOT_OFFSET); + mark_buffer_dirty (bh); + } + /* "." must point to the directory it is in */ + if (not_of_one_file (&(deh[i].deh_dir_id), &(ih->ih_key))) { + fsck_log ("verify_direntry: block %lu, item %H has entry \".\" " + "pointing to (%K) instead of (%K)\n", + bh->b_blocknr, ih, + &(deh[i].deh_dir_id), &(ih->ih_key)); + deh[i].deh_dir_id = key_dir_id (&ih->ih_key); + deh[i].deh_objectid = key_objectid (&ih->ih_key); + mark_buffer_dirty (bh); + } + } else if (i == 1 && is_dot_dot (name, name_len)) { + + /* check and fix ".." */ + + if (deh_offset (deh + i) != DOT_DOT_OFFSET) + deh[i].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); + } else { + int min_length, max_length; + + /* check other name */ + + if (name_len == 0) { + /* we do not know the length of name - we will try to find it */ + min_length = 1; + max_length = item + ih_item_len (ih) - name; + } else + /* we kow name length, so we will try only one name length */ + min_length = max_length = name_len; + + for (j = min_length; j <= max_length; j ++) { + hash_code = find_hash_in_use (name, j, + GET_HASH_VALUE (deh_offset (deh + i)), + rs_hash (fs->s_rs)); + add_hash_hit (fs, hash_code); + if (code2func (hash_code) != 0) { + /* deh_offset matches to some hash of the name */ + if (!name_len) { + fsck_log ("pass0: block %lu, item %H: entry %d. found a name \"%.*s\" " + "matching to deh_offset %u. FIXME: should set deh_location " + "of previous entry (not ready)\n", + bh->b_blocknr, ih, i, j, name, deh_offset (deh + i)); + /* FIXME: if next byte is 0 we think that the name is aligned to 8 byte boundary */ + deh[i - 1].deh_location = cpu_to_le16 (deh_location (deh + i) + + ((name[j] || SB_VERSION (fs) == REISERFS_VERSION_1) ? j : ROUND_UP (j))); + } + break; + } + } + if (j == max_length + 1) { + /* deh_offset does not match to anything. it will be + deleted for now, but maybe we could just fix a + deh_offset if it is in ordeer */ + mark_de_bad_offset (deh + i); + } + } + } +#endif + +#ifdef DEBUG_VERIFY_DENTRY + printf ("entries with mismatching deh_offsets: "); + for (i = 0; i < ih_entry_count (ih); i ++) { + if (de_bad_offset (deh + i)) + printf ("%d ", i); + } + printf ("\n"); +#endif /* DEBUG_VERIFY_DENTRY */ + + + /* correct deh_locations such that code cutting entries will not get + screwed up */ + { + int prev_loc; + int loc_fixed; + + + prev_loc = ih_item_len (ih); + for (i = 0; i < ih_entry_count (ih); i ++) { + loc_fixed = 0; + if (de_bad_location (deh + i)) { + deh[i].deh_location = cpu_to_le16 (prev_loc/* - 1*/); + mark_buffer_dirty (bh); + loc_fixed = 1; + } else { + if (deh_location (deh + i) >= prev_loc) { + deh[i].deh_location = cpu_to_le16 (prev_loc/* - 1*/); + mark_buffer_dirty (bh); + loc_fixed = 1; + } + } + + prev_loc = deh_location (deh + i); + + if (i == ih_entry_count (ih) - 1) { + /* last entry starts right after an array of dir entry headers */ + if (!de_bad (deh + i) && + deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { + /* free space in the directory item */ + fsck_log ("verify_direntry: block %lu, item %H has free space\n", + bh->b_blocknr, ih); + cut_entry (fs, bh, item_num, ih_entry_count (ih), 0); + } + if (deh_location (deh + i) != (DEH_SIZE * ih_entry_count (ih))) { + deh[i].deh_location = cpu_to_le16 (DEH_SIZE * ih_entry_count (ih)); + loc_fixed = 1; + mark_buffer_dirty (bh); + } + } + +#ifdef DEBUG_VERIFY_DENTRY + if (loc_fixed) + direntries [i] = 1; +#endif + } /* for */ + +#ifdef DEBUG_VERIFY_DENTRY + printf ("entries with fixed deh_locations: "); + for (i = 0; i < ih_entry_count (ih); i ++) { + if (direntries [i]) + printf ("%d ", i); + } + printf ("\n"); +#endif /* DEBUG_VERIFY_DENTRY */ + + } + +#ifdef DEBUG_VERIFY_DENTRY + printf (" N location name\n"); + for (i = 0; i < ih_entry_count (ih); i ++) { + if (de_bad (deh + i) || + (i && de_bad (deh + i - 1)) || /* previous entry marked bad */ + (i < ih_entry_count (ih) - 1 && de_bad (deh + i + 1))) { /* next entry is marked bad */ + /* print only entries to be deleted and their nearest neighbors */ + printf ("%3d: %8d ", i, deh_location (deh + i)); + if (de_bad (deh + i)) + printf ("will be deleted\n"); + else + printf ("\"%.*s\"\n", name_length (ih, deh + i, i), + name_in_entry (deh + i, i)); + } + } +#endif + + bad = 0; + tmp = *ih; + + /* delete entries which are marked bad */ + for (i = 0; i < ih_entry_count (ih); i ++) { + deh = B_I_DEH (bh, ih) + i; + if (de_bad (deh)) { + bad ++; + if (ih_entry_count (ih) == 1) { + delete_item (fs, bh, item_num); + break; + } else { + cut_entry (fs, bh, item_num, i, 1); + } + i --; + } + } + + if (bad == ih_entry_count (&tmp)) { + fsck_log ("pass0: block %lu, item %H - all entries were deleted\n", bh->b_blocknr, &tmp); + return 0; + } + + deh = B_I_DEH (bh, ih); + if (get_offset (&ih->ih_key) != deh_offset (deh)) { + fsck_log ("verify_direntry: block %lu, item %H: k_offset and deh_offset %u mismatched\n", + bh->b_blocknr, ih, deh_offset (deh)); + set_offset (KEY_FORMAT_1, &ih->ih_key, deh_offset (deh)); + mark_buffer_dirty (bh); + } + + if (bad) + fsck_log ("pass0: block %lu, item %H: %d entries were deleted of \n", + bh->b_blocknr, &tmp, bad); + + return 0; + +#if 0 + + /* FIXME: temporary */ + if (bad_locations > ih_entry_count (ih) / 2) { + fsck_log ("pass0: block %u: item %d (%H) had too bad directory - deleted\n", + bh->b_blocknr, item_num, ih); + delete_item (fs, bh, item_num); + return 0; + } + + if (!dirty) + return 0; + + /* something is broken */ + + fsck_log ("pass0: block %lu: %d-th item (%H) has %d bad entries..", + bh->b_blocknr, item_num, ih, dirty); + + if (get_offset (&ih->ih_key) == DOT_OFFSET) { + /* first item of directory - make sure that "." and ".." are in place */ + if (deh_offset (deh) != DOT_OFFSET || name_in_entry (deh, 0)[0] != '.') { + deh->deh_offset = cpu_to_le32 (DOT_OFFSET); + name_in_entry (deh, 0)[0] = '.'; + } + if (deh_offset (deh + 1) != DOT_DOT_OFFSET || + name_in_entry (deh + 1, 1)[0] != '.' || name_in_entry (deh + 1, 1)[1] != '.') { + (deh + 1)->deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); + name_in_entry (deh + 1, 1)[0] = '.'; + name_in_entry (deh + 1, 1)[1] = '.'; + } + } + + end = item + ih_item_len (ih); + deh += ih_entry_count (ih); + entries = (char *)deh; + total_entry_len = ih_item_len (ih) - DEH_SIZE * ih_entry_count (ih); + i = ih_entry_count (ih); + + bad_entries = 0; + do { + i --; + deh --; + name_len = prob_name (fs, &entries, total_entry_len, deh_offset (deh)); + if (!name_len) { + if (!bad_entries) { + deh->deh_location = cpu_to_le16 (entries - item); + } else { + deh->deh_location = cpu_to_le16 (deh_location (deh + 1) + 1); + } + bad_entries ++; + + /*fsck_log ("verify_directory_item: entry %d: in string \'%s\' there is no substring matching hash %ld\n", + i, bad_name (entries, total_entry_len), masked_offset);*/ + mark_de_bad (deh); + continue; + } + bad_entries = 0; + /*fsck_log ("verify_directory_item: entry %d: found \"%s\" name matching hash %ld\n", + i, bad_name (entries, name_len), masked_offset);*/ + + /* 'entries' points now to the name which match given offset - + so, set deh_location */ + deh->deh_location = cpu_to_le16 (entries - item); + deh->deh_state = 0; + mark_de_visible (deh); + + entries += name_len; + total_entry_len = end - entries; + /* skip padding zeros */ + while (!*entries) { + entries ++; + total_entry_len --; + } + /* 'entries' points now at the place where next (previous) + entry should start */ + } while ((char *)deh != item); + + + /* fixme: this will not work if all entries are to be deleted */ + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + deh = (struct reiserfs_de_head *)B_I_PITEM (bh, ih) + i; + if (de_bad (deh)) { + if (ih_entry_count (ih) == 1) { + delete_item (fs, bh, i); + break; + } else { + cut_entry (fs, bh, item_num, i); + } + i --; + } +/* + fsck_log ("verify_directory_item: %d-th entry is to be deleted: " + "\"%s\" does not match to hash %lu\n", + i, bad_name (name_in_entry (deh, i), name_length (ih, deh, i)), + deh_offset (deh)); +*/ + } + + fsck_log ("%d entries were deleted\n", entry_count - ih_entry_count (ih)); + mark_buffer_dirty (bh); + + + return 0; + +#endif +} + + +/* do this on pass 0 with every leaf marked used */ + +/* FIXME: we can improve fixing of broken keys: we can ssfe direct items which + go after stat data and have broken keys */ +static void pass0_correct_leaf (reiserfs_filsys_t fs, + struct buffer_head * bh) +{ + int i, j; + struct item_head * ih; + __u32 * ind_item; + unsigned long unfm_ptr; + int dirty = 0; + + start_again: + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + if (ih->ih_key.k_dir_id == 0 || ih->ih_key.k_objectid == 0) { + /* sometimes stat datas get k_objectid==0 or k_dir_id==0 */ + if (i == (node_item_number (bh) - 1)) { + /* */ + if (i == 0) { + fsck_log ("block %lu: item %d: (%H) is alone in the block\n", + bh->b_blocknr, i, ih); + return; + } + /* delete last item */ + delete_item (fs, bh, i - 1); + return; + } + + /* there is next item: if it is not stat data - take its k_dir_id + and k_objectid. if key order will be still wrong - the changed + item will be deleted */ + if (!is_stat_data_ih (ih + 1)) { + fsck_log ("block %lu: item %d: (%H) fixed to ", bh->b_blocknr, i, ih); + ih->ih_key.k_dir_id = (ih + 1)->ih_key.k_dir_id; + ih->ih_key.k_objectid = (ih + 1)->ih_key.k_objectid; + set_offset (KEY_FORMAT_1, &ih->ih_key, 0); + set_type (KEY_FORMAT_1, &ih->ih_key, TYPE_STAT_DATA); + fsck_log ("(%H)\n", ih); + dirty = 1; + } else if (i == 0) { + delete_item (fs, bh, i); + goto start_again; + } + } + + /* this recovers corruptions like the below: + 1774 1732 0 0 + 116262638 1732 1 3 + 1774 1736 0 0 */ + if (i && is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) { + if (ih->ih_key.k_objectid != (ih - 1)->ih_key.k_objectid || + ih->ih_key.k_dir_id != (ih - 1)->ih_key.k_dir_id || + get_offset (&ih->ih_key) != 1) { + if (is_direntry_ih (ih)) { + fsck_log ("block %lu: item %d: no \".\" entry found in " + "the first item of a directory\n", bh->b_blocknr, i); + } else { + fsck_log ("block %lu: item %d: (%H) fixed to ", + bh->b_blocknr, i, ih); + ih->ih_key.k_dir_id = (ih - 1)->ih_key.k_dir_id; + ih->ih_key.k_objectid = (ih - 1)->ih_key.k_objectid; + + if (ih_item_len (ih - 1) == SD_SIZE) { + /* stat data is new, therefore this item is new too */ + set_offset (KEY_FORMAT_2, &(ih->ih_key), 1); + if (ih_entry_count (ih) != 0xffff) + set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_INDIRECT); + else + set_type (KEY_FORMAT_2, &(ih->ih_key), TYPE_DIRECT); + set_key_format (ih, KEY_FORMAT_2); + } else { + /* stat data is old, therefore this item is old too */ + set_offset (KEY_FORMAT_1, &(ih->ih_key), 1); + if (ih_entry_count (ih) != 0xffff) + set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_INDIRECT); + else + set_type (KEY_FORMAT_1, &(ih->ih_key), TYPE_DIRECT); + set_key_format (ih, KEY_FORMAT_1); + } + fsck_log ("%H\n", ih); + dirty = 1; + } + } + } + + /* FIXME: corruptions like: + 56702 66802 1 2 + 56702 65536 0 0 + 56702 66803 1 2 + do not get recovered (both last items will be deleted) */ + /* delete item if it is not in correct order of object items */ + if (i && not_of_one_file (&ih->ih_key, &(ih - 1)->ih_key) && + !is_stat_data_ih (ih)) { + fsck_log ("block %lu: item %d: %H follows non stat item %H - deleted\n", + bh->b_blocknr, i, ih, ih - 1); + delete_item (fs, bh, i); + goto start_again; + } + + if (i && comp_keys (&(ih - 1)->ih_key, &ih->ih_key) != -1) { + /* previous item has key not smaller than the key of currect item */ + if (is_stat_data_ih (ih - 1) && !is_stat_data_ih (ih)) { + /* fix key of stat data such as if it was stat data of that item */ + fsck_log ("pass0: block %lu: %d-th item %k is out of order, made a stat data of %d-th (%k)\n", + bh->b_blocknr, i - 1, &(ih - 1)->ih_key, i, &ih->ih_key); + (ih - 1)->ih_key.k_dir_id = ih->ih_key.k_dir_id; + (ih - 1)->ih_key.k_objectid = ih->ih_key.k_objectid; + set_offset (KEY_FORMAT_1, &(ih - 1)->ih_key, 0); + set_type (KEY_FORMAT_1, &(ih - 1)->ih_key, TYPE_STAT_DATA); + dirty = 1; + } else { + /* ok, we have to delete one of these two - decide which one */ + int retval; + + /* something will be deleted */ + dirty = 1; + retval = upper_correct (bh, ih - 1, i - 1); + switch (retval) { + case 0: + /* delete upper item */ + fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n", + bh->b_blocknr, i - 1, &(ih - 1)->ih_key); + delete_item (fs, bh, i - 1); + goto start_again; + + case 1: + /* delete lower item */ + fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n", + bh->b_blocknr, i, &ih->ih_key); + delete_item (fs, bh, i); + goto start_again; + + default: + /* upper item was the first item of a node */ + } + + retval = lower_correct (bh, ih, i); + switch (retval) { + case 0: + /* delete lower item */ + fsck_log ("pass0: block %lu: %d-th (lower) item (%k) is out of order, deleted\n", + bh->b_blocknr, i, &ih->ih_key); + delete_item (fs, bh, i); + goto start_again; + + case 1: + /* delete upper item */ + fsck_log ("pass0: block %lu: %d-th (upper) item (%k) is out of order, deleted\n", + bh->b_blocknr, i - 1, &(ih - 1)->ih_key); + delete_item (fs, bh, i - 1); + goto start_again; + + default: + /* there wer only two items in a node, so we could not + decide what to delete, go and ask user */ + } + fsck_log ("pass0: which of these items looks better (other will be deleted)?\n" + "%H\n%H\n", ih - 1, ih); + if (fsck_user_confirmed (fs, "1 or 2?", "1\n", 1)) + delete_item (fs, bh, i - 1); + else + delete_item (fs, bh, i); + goto start_again; + } + } + + if (is_stat_data_ih (ih) && (ih_item_len (ih) != SD_SIZE && + ih_item_len (ih) != SD_V1_SIZE)) { + fsck_log ("pass0: block %lu, stat data of wrong length %H - deleted\n", + bh, ih); + delete_item (fs, bh, i); + goto start_again; + } + + dirty += correct_key_format (ih); + + if (is_stat_data_ih (ih)) { + ;/*correct_stat_data (fs, bh, i);*/ + } + + if (is_direntry_ih (ih)) { + verify_directory_item (fs, bh, i); + continue; + } + + if (!is_indirect_ih (ih)) + continue; + + ind_item = (__u32 *)B_I_PITEM (bh, ih); + for (j = 0; j < I_UNFM_NUM (ih); j ++) { + unfm_ptr = le32_to_cpu (ind_item [j]); + if (!unfm_ptr) + continue; + + if (fsck_mode (fs) == FSCK_ZERO_FILES) { + /* FIXME: this is temporary mode of fsck */ + ind_item [j] = 0; + reiserfs_bitmap_clear_bit (fsck_new_bitmap(fs), unfm_ptr); + tmp_zeroed ++; + dirty = 1; + continue; + } + + if (not_data_block (fs, unfm_ptr) || /* journal area or bitmap or super block */ + unfm_ptr >= SB_BLOCK_COUNT (fs)) {/* garbage in pointer */ + + stats (fs)->wrong_pointers ++; + /* + fsck_log ("pass0: %d-th pointer (%lu) in item %k (leaf block %lu) is wrong\n", + j, unfm_ptr, &ih->ih_key, bh->b_blocknr); + */ + ind_item [j] = 0; + dirty = 1; + continue; + } +#if 0 + if (!was_block_used (unfm_ptr)) { + /* this will get to a pool of allocable blocks */ + ind_item [j] = 0; + dirty = 1; + stat_wrong_pointer_found (fs); + continue; + } +#endif + /* mark block in bitmaps of unformatted nodes */ + register_unfm (unfm_ptr); + } + } + + /* mark all objectids in use */ + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_dir_id)); + mark_objectid_really_used (proper_id_map (fs), le32_to_cpu (ih->ih_key.k_objectid)); + } + + if (node_item_number (bh) < 1) { + /* pass 1 will skip this */ + stats(fs)->all_contents_removed ++; + fsck_log ("pass0: block %lu got all items deleted\n", + bh->b_blocknr); + } else { + /* pass1 will use this bitmap */ + pass0_mark_leaf (bh->b_blocknr); + + } + if (dirty) { + stats(fs)->leaves_corrected ++; + mark_buffer_dirty (bh); + } +} + + +static int is_bad_sd (struct item_head * ih, char * item) +{ + struct stat_data * sd = (struct stat_data *)item; + + if (ih->ih_key.u.k_offset_v1.k_offset || ih->ih_key.u.k_offset_v1.k_uniqueness) { + reiserfs_warning (stderr, "Bad SD? %H\n", ih); + return 1; + } + + if (ih_item_len (ih) == SD_V1_SIZE) { + /* looks like old stat data */ + if (ih_key_format (ih) != KEY_FORMAT_1) + fsck_log ("item %H has wrong format\n", ih); + return 0; + } + + if (!S_ISDIR (sd->sd_mode) && !S_ISREG(sd->sd_mode) && + !S_ISCHR (sd->sd_mode) && !S_ISBLK(sd->sd_mode) && + !S_ISLNK (sd->sd_mode) && !S_ISFIFO(sd->sd_mode) && + !S_ISSOCK(sd->sd_mode)) { + /*fsck_log ("file %k unexpected mode encountered 0%o\n", &ih->ih_key, sd->sd_mode)*/; + } + return 0; +} + + +int is_bad_directory (struct item_head * ih, char * item, int dev, int blocksize) +{ + int i; + char * name; + int namelen, entrylen; + struct reiserfs_de_head * deh = (struct reiserfs_de_head *)item; + __u32 prev_offset = 0; + __u16 prev_location = ih_item_len (ih); + int min_entry_size = 1;/* we have no way to understand whether the + filesystem were created in 3.6 format or + converted to it. So, we assume that minimal name + length is 1 */ + + if (ih_item_len (ih) / (DEH_SIZE + min_entry_size) < ih_entry_count (ih)) + /* entry count is too big */ + return 1; + + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + entrylen = entry_length(ih, deh, i); + if (entrylen > REISERFS_MAX_NAME_LEN (blocksize)) { + return 1; + } + if (deh_offset (deh) <= prev_offset) { + return 1; + } + prev_offset = deh_offset (deh); + + if (deh_location(deh) + entrylen != prev_location) { + return 1; + } + prev_location = deh_location (deh); + + namelen = name_length (ih, deh, i); + name = name_in_entry (deh, i); + if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) { + return 1; + } + } + return 0; +} + + +/* change incorrect block adresses by 0. Do not consider such item as incorrect */ +static int is_bad_indirect (struct item_head * ih, char * item, int dev, int blocksize) +{ + int i; + int bad = 0; + int blocks; + + if (ih_item_len(ih) % UNFM_P_SIZE) { + fsck_log ("is_bad_indirect: indirect item of %H of invalid length\n", ih); + return 1; + } + + blocks = SB_BLOCK_COUNT (fs); + + for (i = 0; i < I_UNFM_NUM (ih); i ++) { + __u32 * ind = (__u32 *)item; + + if (le32_to_cpu (ind[i]) >= blocks) { + bad ++; + fsck_log ("is_bad_indirect: %d-th pointer of item %H looks bad (%lu)\n", + i, ih, le32_to_cpu (ind [i])); + continue; + } + } + return bad; +} + + +/* this is used by pass1.c:save_item and check.c:is_leaf_bad */ +int is_bad_item (struct buffer_head * bh, struct item_head * ih, char * item) +{ + int blocksize, dev; + + blocksize = bh->b_size; + dev = bh->b_dev; + + // FIXME: refuse transparently bad items + if (ih->ih_key.k_dir_id == ih->ih_key.k_objectid) + return 1; + if (!ih->ih_key.k_dir_id || !ih->ih_key.k_objectid) + return 1; + + if (is_stat_data_ih(ih)) + return is_bad_sd (ih, item); + + if (is_direntry_ih (ih)) + return is_bad_directory (ih, item, dev, blocksize); + + if (is_indirect_ih (ih)) + return is_bad_indirect (ih, item, dev, blocksize); + + if (is_direct_ih (ih)) + return 0; + + return 1; +} + + +int is_leaf_bad (struct buffer_head * bh) +{ + int i; + struct item_head * ih; + int bad = 0; + + assert (is_leaf_node (bh)); + + for (i = 0, ih = B_N_PITEM_HEAD (bh, 0); i < B_NR_ITEMS (bh); i ++, ih ++) { + if (is_bad_item (bh, ih, B_I_PITEM (bh, ih))) { + fsck_log ("is_leaf_bad: block %lu: %d-th item (%H) is bad\n", + bh->b_blocknr, i, ih); + bad = 1; + continue; + } + + if (i && bad_pair (fs, bh, i)) { + fsck_log ("is_leaf_bad: block %luL %d-th item (%H) and " + "the next one (%H) are in wrong order\n", + bh->b_blocknr, i - 1, ih - 1, ih); + bad = 1; + } + } + + return bad; +} + + +static void go_through (reiserfs_filsys_t fs) +{ + struct buffer_head * bh; + int i; + int what_node; + unsigned long done = 0, total; + + if (fsck_mode (fs) == DO_TEST) { + /* just to test pass0_correct_leaf */ + bh = bread (fs->s_dev, stats(fs)->test, fs->s_blocksize); + + /* + if (is_leaf_bad (bh)) { + fsck_progress ("############### bad #################\n"); + } + */ + pass0_correct_leaf (fs, bh); + + print_block (stdout, fs, bh, 3, -1, -1); + + if (is_leaf_bad (bh)) { + fsck_progress ("############### still bad #################\n"); + } + brelse (bh); + reiserfs_free (fs); + exit(4); + } + + + total = reiserfs_bitmap_ones (fsck_disk_bitmap (fs)); + fsck_progress ("\nPass 0 (%lu (of %lu) blocks will be read):\n", + total, SB_BLOCK_COUNT (fs)); + + + for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) { + if (!is_to_be_read (fs, i)) + continue; + + print_how_far (&done, total, 1, fsck_quiet (fs)); + + bh = bread (fs->s_dev, i, fs->s_blocksize); + if (!bh) { + /* we were reading one block at time, and failed, so mark + block bad */ + fsck_progress ("pass0: reading block %lu failed\n", i); + continue; + } + + if (not_data_block (fs, i)) + reiserfs_panic ("not data block found"); + + stats (fs)->analyzed ++; + what_node = who_is_this (bh->b_data, fs->s_blocksize); + if ( what_node != THE_LEAF ) { + brelse (bh); + continue; + } + pass0_correct_leaf (fs, bh); + brelse (bh); + } + + +#if 0 + for (i = 0; i < SB_BLOCK_COUNT (fs); i += nr_to_read) { + to_scan = how_many_to_scan (fs, i, nr_to_read); + if (to_scan) { + print_how_far (&done, total, to_scan, fsck_quiet (fs)); + + /* at least one of nr_to_read blocks is to be checked */ + bbh = bread (fs->s_dev, i / nr_to_read, fs->s_blocksize * nr_to_read); + if (bbh) { + for (j = 0; j < nr_to_read; j ++) { + if (!is_to_be_read (fs, i + j)) + continue; + + if (not_data_block (fs, i + j)) + reiserfs_panic ("not data block found"); + + stats (fs)->analyzed ++; + + data = bbh->b_data + j * fs->s_blocksize; + what_node = who_is_this (data, fs->s_blocksize); + if ( what_node != THE_LEAF ) { + continue; + } + + /* the node looks like a leaf, but it still can be + not perfect */ + bh = make_buffer (fs->s_dev, i + j, fs->s_blocksize, data); + + /*printf ("block %lu .. ", bh->b_blocknr);fflush(stdout);*/ + pass0_correct_leaf (fs, bh); + /*printf ("ok\n");fflush(stdout);*/ + + brelse (bh); + } + + if (buffer_dirty (bbh)) + bwrite (bbh); + bforget (bbh); + } else { + done -= to_scan; + /* bread failed */ + if (nr_to_read != 1) { + /* we tryied to read bunch of blocks. Try to read them by one */ + nr_to_read = 1; + i --; + continue; + } else { + /* we were reading one block at time, and failed, so mark + block bad */ + fsck_progress ("pass0: block %lu is bad, marked used\n", i); + } + } + } + + if (nr_to_read == 1 && ((i + 1) % NR_TO_READ) == 0) { + /* we have read NR_TO_READ blocks one at time, switch back to + reading NR_TO_READ blocks at time */ + i -= (NR_TO_READ - 1); + nr_to_read = NR_TO_READ; + } + } +#endif + + + /* just in case */ + mark_objectid_really_used (proper_id_map (fs), REISERFS_ROOT_OBJECTID); + + fsck_progress ("\n"); + + if (fsck_save_leaf_bitmap (fs)) { + reiserfs_bitmap_save (stats (fs)->new_bitmap_file_name, leaves_bitmap); + } +} + + +/* this makes a map of blocks which can be allocated when fsck will continue: */ +static void find_allocable_blocks (reiserfs_filsys_t fs) +{ + int i; + + + fsck_progress ("Looking for allocable blocks .. "); + stats (fs)->all_blocks = SB_BLOCK_COUNT (fs); + + /* find how many leaves are not pointed by any indirect items */ + for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) { + if (not_data_block (fs, i)) + continue; + +#if 0 + if (!was_block_used (i)) { + /* marked free in the on-disk bitmap - so it is allocable */ + make_allocable (i); + stat_allocable_found (fs); + continue; + } +#endif + + if (pass0_is_leaf (i)) { + /* block looks like a reiserfs leaf */ + stats(fs)->leaves ++; + if (pass0_is_good_unfm (i) || pass0_is_bad_unfm (i)) + /* leaf to which one or more indirect items point to */ + stats(fs)->pointed_leaves ++; + } + + if (pass0_is_good_unfm (i) && pass0_is_bad_unfm (i)) + die ("find_allocable_blocks: bad and good unformatted"); + + if (pass0_is_good_unfm (i)) { + /* blocks which were pointed only once */ + stats(fs)->pointed ++; + stats(fs)->pointed_once ++; + continue; + } + if (pass0_is_bad_unfm (i)) { + /* blocks pointed more than once */ + stats(fs)->pointed ++; + stats(fs)->pointed_more_than_once ++; + continue; + } + + /* blocks which marked used but are not leaves and are not + pointed (internals in short) get into bitmap of allocable + blocks */ + if (!pass0_is_leaf (i)) { + make_allocable (i); + stats(fs)->allocable ++; + } + } + fsck_progress ("ok\n"); +} + +#if 0 +int are_there_used_leaves (unsigned long from, int count) +{ + int i; + int used; + + used = 0; + for (i = 0; i < count; i ++) { + if ((SB_BLOCK_COUNT (fs) > from + i) && + pass0_is_leaf (from + i)) + used ++; + } + return used; +} +#endif + +int is_used_leaf (unsigned long block) +{ + return pass0_is_leaf (block); +} + + +int how_many_leaves_were_there (void) +{ + return stats (fs)->leaves; +} + +/* these are used to correct uformatted node pointers */ +int is_bad_unformatted (unsigned long block) +{ + return pass0_is_bad_unfm (block); +} + + +/* this is for check only. With this we make sure that all pointers we + put into tree on pass 1 do not point to leaves (FIXME), do not + point to journal, bitmap, etc, do not point out of fs boundary and + are marked used in on-disk bitmap */ +int still_bad_unfm_ptr_1 (unsigned long block) +{ + if (!block) + return 0; + if (pass0_is_leaf (block)) + return 1; + if (pass0_is_bad_unfm (block) && !is_bad_unfm_in_tree_once (block)) + return 1; + if (not_data_block (fs, block)) + return 1; + /* + if (!was_block_used (block)) + return 1; + */ + if (block >= stats (fs)->all_blocks) + return 1; + return 0; + +} + + +/* pointers to data block which get into tree are checked with this */ +int still_bad_unfm_ptr_2 (unsigned long block) +{ + if (!block) + return 0; + if (is_block_used (block)) + return 1; + if (block >= stats (fs)->all_blocks) + return 1; + return 0; +} + + +/* these are used to allocate blocks for tree building */ +int are_there_allocable_blocks (int amout_needed) +{ + if (reiserfs_bitmap_zeros (fsck_allocable_bitmap (fs)) < amout_needed) { + int zeros = 0, i; + + fsck_progress ("Hmm, there are not enough allocable blocks, checking bitmap...");fflush (stdout); + for (i = 0; i < fsck_allocable_bitmap (fs)->bm_bit_size; i ++) + if (!reiserfs_bitmap_test_bit (fsck_allocable_bitmap (fs), i)) + zeros ++; + fsck_progress ("there are %d zeros, btw\n", zeros); + return 0; + } + return 1; +} + + +unsigned long alloc_block (void) +{ + unsigned long block = 0; /* FIXME: start point could be used */ + + if (reiserfs_bitmap_find_zero_bit (fsck_allocable_bitmap (fs), &block)) { + die ("alloc_block: allocable blocks counter is wrong"); + return 0; + } + reiserfs_bitmap_set_bit (fsck_allocable_bitmap (fs), block); + return block; +} + + +void make_allocable (unsigned long block) +{ + reiserfs_bitmap_clear_bit (fsck_allocable_bitmap (fs), block); +} + + +unsigned long how_many_uninsertables_were_there (void) +{ + return stats (fs)->uninsertable_leaves; +} + + +unsigned long how_many_items_were_saved (void) +{ + return stats (fs)->saved_on_pass1; +} + + +static void choose_hash_function (reiserfs_filsys_t fs) +{ + unsigned long max; + int hash_code; + int i; + + max = 0; + hash_code = func2code (0); + + for (i = 0; i < stats (fs)->hash_amount; i ++) { + /* remember hash whihc got more hits */ + if (stats (fs)->hash_hits [i] > max) { + hash_code = i; + max = stats (fs)->hash_hits [i]; + } + + if (stats (fs)->hash_hits [i]) + fsck_log ("%s got %lu hits\n", code2name (i), + stats (fs)->hash_hits [i]); + } + if (max == 0) { + /* no names were found. take either super block value or + default */ + reiserfs_hash (fs) = code2func (rs_hash (fs->s_rs)); + if (!reiserfs_hash (fs)) + reiserfs_hash (fs) = code2func (DEFAULT_HASH); + return; + } + + /* compare the most appropriate hash with the hash set in super block */ + if (hash_code != rs_hash (fs->s_rs)) { + fsck_progress ("Selected hash (%s) does not match to one set in super block (%s).\n", + code2name (hash_code), code2name (rs_hash (fs->s_rs))); + /* + if (!fsck_user_confirmed (fs, "Overwrite?(Yes)", "Yes", 1)) { + fsck_progress ("Not confirmed\n"); + exit (4); + } + */ + set_hash (fs->s_rs, hash_code); + } else { + fsck_progress ("\t%s hash is selected\n", code2name (hash_code)); + } + + reiserfs_hash (fs) = code2func (hash_code); +} + + +int pass_0 (reiserfs_filsys_t fs) +{ + if (fsck_log_file (fs) != stderr) + /* this is just to separate warnings in the log file */ + fsck_log ("####### Pass 0 #######\n"); + + if (fsck_mode (fs) == FSCK_ZERO_FILES) { + reiserfs_bitmap_fill (fsck_new_bitmap (fs)); + fsck_progress ("Zeroing existing files - all blocks marked used\n"); + } + + make_aux_bitmaps (fs); + + /* pass0 gathers statistics about hash hits */ + hash_hits_init (fs); + + /* scan the partition */ + go_through (fs); + + /* find blocks which can be allocated */ + find_allocable_blocks (fs); + + if (fsck_mode (fs) == FSCK_ZERO_FILES) { + /* flush bitmaps and exit */ + fsck_progress ("Zeroing existing files - zeroed %lu blocks\n", tmp_zeroed); + reiserfs_flush_bitmap (fsck_new_bitmap (fs), fs); + reiserfs_close (fs); + exit (0); + } + + + choose_hash_function (fs); + + stage_report (0, fs); + return 0; +} + + +#if 0 + +/* node looks like a leaf (block head and ih_item_length & ih_location + of item_head array are correct. This function recovers (tries to) node's key + table and directory items. */ +static void recover_leaf (reiserfs_filsys_t fs, struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + /* mark transparently corrupted items - bad */ + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + if (type_unknown (&ih->ih_key) || + ih->ih_key.k_dir_id == 0 || + ih->ih_key.k_objectid) { + mark_ih_bad (ih); + continue; + } + } +} + +#endif + diff --git a/fsck/pass1.c b/fsck/pass1.c new file mode 100644 index 0000000..d6b29e9 --- /dev/null +++ b/fsck/pass1.c @@ -0,0 +1,881 @@ +/* + * Copyright 1996-1999 Hans Reiser + */ +#include "fsck.h" +#include <stdlib.h> + + +reiserfs_bitmap_t bad_unfm_in_tree_once_bitmap; + +//int step = 0; // 0 - find stat_data or any item ; 1 - find item ; 2 - already found + + +/* allocates buffer head and copy buffer content */ +struct buffer_head * make_buffer (int dev, int blocknr, int size, char * data) +{ + struct buffer_head * bh; + + bh = getblk (dev, blocknr, size); + if (buffer_uptodate (bh)) + return bh; +// die ("make_buffer: uptodate buffer found"); + memcpy (bh->b_data, data, size); + set_bit (BH_Uptodate, (char *)&bh->b_state); + return bh; +} + + +int find_not_of_one_file(struct key * to_find, struct key * key) +{ + if ((to_find->k_objectid != -1) && + (to_find->k_objectid != key->k_objectid)) + return 1; + if ((to_find->k_dir_id != -1) && + (to_find->k_dir_id != key->k_dir_id)) + return 1; + return 0; +} + + +int is_item_reachable (struct item_head * ih) +{ + return ih_reachable (ih) ? 1 : 0; +} + + +void mark_item_unreachable (struct item_head * ih) +{ + mark_ih_unreachable (ih); + + if (is_indirect_ih (ih)) + set_free_space (ih, 0); +} + + +void mark_item_reachable (struct item_head * ih, struct buffer_head * bh) +{ + mark_ih_ok (ih); + mark_buffer_dirty (bh); +} + + +static void stat_data_in_tree (struct buffer_head *bh, + struct item_head * ih) +{ +#if 0 + __u32 objectid; + + objectid = le32_to_cpu (ih->ih_key.k_objectid); + + if (mark_objectid_really_used (proper_id_map (fs), objectid)) { + stat_shared_objectid_found (fs); + mark_objectid_really_used (shared_id_map (fs), objectid); + } +#endif + + zero_nlink (ih, B_I_PITEM (bh, ih)); +} + + +/* this just marks blocks pointed by an indirect item as used in the + new bitmap */ +static void indirect_in_tree (struct buffer_head * bh, + struct item_head * ih) +{ + int i; + __u32 * unp; + unsigned long unfm_ptr; + + unp = (__u32 *)B_I_PITEM (bh, ih); + + for (i = 0; i < I_UNFM_NUM (ih); i ++) { + unfm_ptr = le32_to_cpu (unp[i]); + if (unfm_ptr == 0) + continue; + + if (still_bad_unfm_ptr_1 (unfm_ptr)) + reiserfs_panic ("mark_unformatted_used: (%lu: %k) " + "still has bad pointer %lu", + bh->b_blocknr, &ih->ih_key, unfm_ptr); + + mark_block_used (unfm_ptr); + } +} + + +static void leaf_is_in_tree_now (struct buffer_head * bh) +{ + item_action_t actions[] = {stat_data_in_tree, indirect_in_tree, 0, 0}; + + mark_block_used ((bh)->b_blocknr); + + for_every_item (bh, mark_item_unreachable, actions); + + stats(fs)->inserted_leaves ++; + + mark_buffer_dirty (bh); +} + + +static void insert_pointer (struct buffer_head * bh, struct path * path) +{ + struct item_head * ih; + char * body; + int zeros_number; + int retval; + struct tree_balance tb; + + init_tb_struct (&tb, fs, path, 0x7fff); + + /* fix_nodes & do_balance must work for internal nodes only */ + ih = 0; + + retval = fix_nodes (/*tb.transaction_handle,*/ M_INTERNAL, &tb, ih); + if (retval != CARRY_ON) + die ("insert_pointer: fix_nodes failed with retval == %d", retval); + + /* child_pos: we insert after position child_pos: this feature of the insert_child */ + /* there is special case: we insert pointer after + (-1)-st key (before 0-th key) in the parent */ + if (PATH_LAST_POSITION (path) == 0 && path->pos_in_item == 0) + PATH_H_B_ITEM_ORDER (path, 0) = -1; + else { + if (PATH_H_PPARENT (path, 0) == 0) + PATH_H_B_ITEM_ORDER (path, 0) = 0; +/* PATH_H_B_ITEM_ORDER (path, 0) = PATH_H_PPARENT (path, 0) ? PATH_H_B_ITEM_ORDER (path, 0) : 0;*/ + } + + ih = 0; + body = (char *)bh; + //memmode = 0; + zeros_number = 0; + + do_balance (&tb, ih, body, M_INTERNAL, zeros_number); + + leaf_is_in_tree_now (bh); +} + + +/* return 1 if left and right can be joined. 0 otherwise */ +int balance_condition_fails (struct buffer_head * left, struct buffer_head * right) +{ + if (B_FREE_SPACE (left) >= B_CHILD_SIZE (right) - + (are_items_mergeable (B_N_PITEM_HEAD (left, B_NR_ITEMS (left) - 1), B_N_PITEM_HEAD (right, 0), left->b_size) ? IH_SIZE : 0)) + return 1; + return 0; +} + + +/* return 1 if new can be joined with last node on the path or with + its right neighbor, 0 otherwise */ +int balance_condition_2_fails (struct buffer_head * new, struct path * path) +{ + struct buffer_head * bh; + struct key * right_dkey; + int pos, used_space; + + bh = PATH_PLAST_BUFFER (path); + + + if (balance_condition_fails (bh, new)) + /* new node can be joined with last buffer on the path */ + return 1; + + /* new node can not be joined with its left neighbor */ + + right_dkey = uget_rkey (path); + if (right_dkey == 0) + /* there is no right neighbor */ + return 0; + + pos = PATH_H_POSITION (path, 1); + if (pos == B_NR_ITEMS (bh = PATH_H_PBUFFER (path, 1))) { + /* we have to read parent of right neighbor. For simplicity we + call search_by_key, which will read right neighbor as well */ + INITIALIZE_PATH(path_to_right_neighbor); + + if (usearch_by_key (fs, right_dkey, &path_to_right_neighbor) != ITEM_FOUND) + die ("get_right_neighbor_free_space: invalid right delimiting key"); + used_space = B_CHILD_SIZE (PATH_PLAST_BUFFER (&path_to_right_neighbor)); + pathrelse (&path_to_right_neighbor); + } + else + used_space = B_N_CHILD (bh, pos + 1)->dc_size; + + if (B_FREE_SPACE (new) >= used_space - + (are_items_mergeable (B_N_PITEM_HEAD (new, B_NR_ITEMS (new) - 1), (struct item_head *)right_dkey, new->b_size) ? IH_SIZE : 0)) + return 1; + + return 0; +} + + +static void get_max_buffer_key (struct buffer_head * bh, struct key * key) +{ + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, B_NR_ITEMS (bh) - 1); + copy_key (key, &(ih->ih_key)); + + if (is_direntry_key (key)) { + /* copy 3-rd and 4-th key components of the last entry */ + //set_le_key_k_offset (ih_version(ih), key, B_I_DEH (bh, ih)[I_ENTRY_COUNT (ih) - 1].deh_offset); + //set_le_key_k_type (ih_version(ih), key, TYPE_DIRENTRY); + set_offset (KEY_FORMAT_1, key, + le32_to_cpu (B_I_DEH (bh, ih)[ih_entry_count (ih) - 1].deh_offset)); + + } else if (!is_stat_data_key (key)) + /* get key of the last byte, which is contained in the item */ + set_offset (key_format (key), key, get_offset (key) + get_bytes_number (ih, bh->b_size) - 1); + //set_le_key_k_offset(ih_version(ih), key, + // le_key_k_offset(ih_version(ih), key) + get_bytes_number (bh, ih, 0, CHECK_FREE_BYTES) - 1 ); +} + + +static int tree_is_empty (void) +{ + return (SB_ROOT_BLOCK (fs) == ~0) ? 1 : 0; +} + + +static void make_single_leaf_tree (struct buffer_head * bh) +{ + /* tree is empty, make tree root */ + set_root_block (fs->s_rs, bh->b_blocknr); + set_tree_height (fs->s_rs, 2); + mark_buffer_dirty (fs->s_sbh); + leaf_is_in_tree_now (bh); +} + + +/* inserts pointer to leaf into tree if possible. If not, marks node as + uninsertable in special bitmap */ +static void try_to_insert_pointer_to_leaf (struct buffer_head * new_bh) +{ + INITIALIZE_PATH (path); + struct buffer_head * bh; /* last path buffer */ + struct key * first_bh_key, last_bh_key; /* first and last keys of new buffer */ + struct key last_path_buffer_last_key, * right_dkey; + int ret_value; + + if (tree_is_empty () == 1) { + make_single_leaf_tree (new_bh); + return; + } + + + first_bh_key = B_N_PKEY (new_bh, 0); + + /* try to find place in the tree for the first key of the coming node */ + ret_value = usearch_by_key (fs, first_bh_key, &path); + if (ret_value == ITEM_FOUND) + goto cannot_insert; + + /* get max key in the new node */ + get_max_buffer_key (new_bh, &last_bh_key); + + bh = PATH_PLAST_BUFFER (&path); + if (comp_keys (B_N_PKEY (bh, 0), &last_bh_key) == 1/* first is greater*/) { + /* new buffer falls before the leftmost leaf */ + if (balance_condition_fails (new_bh, bh)) + goto cannot_insert; + + if (uget_lkey (&path) != 0 || PATH_LAST_POSITION (&path) != 0) + die ("try_to_insert_pointer_to_leaf: bad search result"); + + path.pos_in_item = 0; + goto insert; + } + + /* get max key of buffer, that is in tree */ + get_max_buffer_key (bh, &last_path_buffer_last_key); + if (comp_keys (&last_path_buffer_last_key, first_bh_key) != -1/* second is greater */) + /* first key of new buffer falls in the middle of node that is in tree */ + goto cannot_insert; + + right_dkey = uget_rkey (&path); + if (right_dkey && comp_keys (right_dkey, &last_bh_key) != 1 /* first is greater */) + goto cannot_insert; + + if (balance_condition_2_fails (new_bh, &path)) + goto cannot_insert; + + insert: + insert_pointer (new_bh, &path); + goto out; + + cannot_insert: + /* statistic */ + stats (fs)->uninsertable_leaves ++; + + mark_block_uninsertable (new_bh->b_blocknr); + + out: + pathrelse (&path); + return; +} + + + +/* everything should be correct already in the leaf but contents of indirect + items. So we only + 1. zero slots pointing to a leaf + 2. zero pointers to blocks which are pointed already + 3. what we should do with directory entries hashed by another hash? + they are deleted for now +*/ +static void pass1_correct_leaf (reiserfs_filsys_t s, + struct buffer_head * bh) +{ + int i, j; + struct item_head * ih; + __u32 * ind_item; + unsigned long unfm_ptr; + int dirty = 0; + + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + if (is_direntry_ih (ih)) { + struct reiserfs_de_head * deh; + char * name; + int name_len; + int hash_code; + + deh = B_I_DEH (bh, ih); + for (j = 0; j < ih_entry_count (ih); j ++) { + name = name_in_entry (deh + j, j); + name_len = name_length (ih, deh + j, j); + + if ((j == 0 && is_dot (name, name_len)) || + (j == 1 && is_dot_dot (name, name_len))) { + continue; + } + + hash_code = find_hash_in_use (name, name_len, + GET_HASH_VALUE (deh_offset (deh + j)), + rs_hash (fs->s_rs)); + if (hash_code != rs_hash (fs->s_rs)) { + fsck_log ("pass1: block %lu, %H, entry \"%.*s\" " + "hashed with %s whereas proper hash is %s\n", + bh->b_blocknr, ih, name_len, name, + code2name (hash_code), code2name (rs_hash (fs->s_rs))); + if (ih_entry_count (ih) == 1) { + delete_item (fs, bh, i); + i --; + ih --; + break; + } else { + cut_entry (fs, bh, i, j, 1); + j --; + deh = B_I_DEH (bh, ih); + } + } + } + continue; + } + + + if (!is_indirect_ih (ih)) + continue; + + /* correct indirect items */ + ind_item = (__u32 *)B_I_PITEM (bh, ih); + + for (j = 0; j < I_UNFM_NUM (ih); j ++, ind_item ++) { + unfm_ptr = le32_to_cpu (*ind_item); + + if (!unfm_ptr) + continue; + + /* this corruption of indirect item had to be fixed in pass0 */ + if (not_data_block (s, unfm_ptr) || unfm_ptr >= SB_BLOCK_COUNT (s)) + /*!was_block_used (unfm_ptr))*/ + reiserfs_panic ("pass1_correct_leaf: (%lu: %k), %d-th slot is not fixed", + bh->b_blocknr, &ih->ih_key, j); + + /* 1. zero slots pointing to a leaf */ + if (is_used_leaf (unfm_ptr)) { + dirty ++; + *ind_item = 0; + stats(fs)->wrong_pointers ++; + continue; + } + + /* 2. zero pointers to blocks which are pointed already */ + if (is_bad_unformatted (unfm_ptr)) { + /* this unformatted pointed more than once. Did we see it already? */ + if (!is_bad_unfm_in_tree_once (unfm_ptr)) + /* keep first reference to it and mark about that in + special bitmap */ + mark_bad_unfm_in_tree_once (unfm_ptr); + else { + /* Yes, we have seen this pointer already, zero other pointers to it */ + dirty ++; + *ind_item = 0; + stats(fs)->wrong_pointers ++; + continue; + } + } + } + } + + if (dirty) + mark_buffer_dirty (bh); +} + + +/*######### has to die ##########*/ +/* append item to end of list. Set head if it is 0. For indirect item + set wrong unformatted node pointers to 0 */ +void save_item (struct si ** head, struct buffer_head * bh, struct item_head * ih, char * item) +{ + struct si * si, * cur; + + if (is_bad_item (bh, ih, item/*, fs->s_blocksize, fs->s_dev*/)) { + return; + } + + if (is_indirect_ih (ih)) { + fsck_progress ("save_item: %H (should not happen)\n", ih); + } + + stats(fs)->saved_on_pass1 ++; + + si = getmem (sizeof (*si)); + si->si_dnm_data = getmem (ih_item_len(ih)); + /*si->si_blocknr = blocknr;*/ + memcpy (&(si->si_ih), ih, IH_SIZE); + memcpy (si->si_dnm_data, item, ih_item_len(ih)); + + // changed by XB + si->last_known = NULL; + + if (*head == 0) + *head = si; + else { + cur = *head; + // changed by XB + // while (cur->si_next) + // cur = cur->si_next; + + { + int count = 0; + int speedcount = 0; + + while (cur->si_next) { + if (cur->last_known!=NULL) { + cur = cur->last_known; // speed up to the end if the chain + speedcount++; + } else { + cur = cur->si_next; + count++; + } + } + + if ((*head)!=cur) // no self referencing loop please + (*head)->last_known = cur; + } + + cur->si_next = si; + } + return; +} + + +static void save_items (struct si ** head, struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + save_item (head, bh, ih, B_I_PITEM (bh, ih)); + } +} + + +struct si * remove_saved_item (struct si * si) +{ + struct si * tmp = si->si_next; + + freemem (si->si_dnm_data); + freemem (si); + return tmp; +} + + +/* insert_item_separately */ +static void put_saved_items_into_tree_1 (struct si * si) +{ + while (si) { + insert_item_separately (&(si->si_ih), si->si_dnm_data, + 0/*was not in tree*/); + si = remove_saved_item (si); + } +} + + +/* reads the device by set of 8 blocks, takes leaves and tries to + insert them into tree */ +void pass_1_pass_2_build_the_tree (void) +{ + struct buffer_head * bh; + int i; + int what_node; + unsigned long done = 0, total; + struct si * saved_items = 0; + + if (fsck_log_file (fs) != stderr) + fsck_log ("####### Pass 1 #######\n"); + + bad_unfm_in_tree_once_bitmap = reiserfs_create_bitmap (SB_BLOCK_COUNT (fs)); + + + /* on pass0 we have found that amount of leaves */ + total = how_many_leaves_were_there (); + + fsck_progress ("\nPass1:\n"); + + /* read all leaves found on the pass 0 */ + for (i = 0; i < SB_BLOCK_COUNT (fs); i ++) { + if (!is_used_leaf (i)) + continue; + + print_how_far (&done, total, 1, fsck_quiet (fs)); + + /* at least one of nr_to_read blocks is to be checked */ + bh = bread (fs->s_dev, i, fs->s_blocksize); + if (!bh) { + /* we were reading one block at time, and failed, so mark + block bad */ + fsck_progress ("pass1: reading block %lu failed\n", i); + continue; + } + + what_node = who_is_this (bh->b_data, fs->s_blocksize); + if ( what_node != THE_LEAF ) { + fsck_progress ("build_the_tree: nothing but leaves are expected. " + "Block %lu - %s\n", i, + (what_node == THE_INTERNAL) ? "internal" : "??"); + brelse (bh); + continue; + } + + if (is_block_used (i)) + /* block is in new tree already */ + die ("build_the_tree: leaf (%lu) is in tree already\n", i); + + /* fprintf (block_list, "leaf %d\n", i + j);*/ + stats(fs)->analyzed ++; + + /* the leaf may still contain indirect items with wrong + slots. Fix that */ + pass1_correct_leaf (fs, bh); + + if (node_item_number (bh) == 0) { + /* all items were deleted on pass 0 or pass 1 */ + mark_buffer_clean (bh); + brelse (bh); + continue; + } + + if (is_leaf_bad (bh)) { + /* FIXME: will die */ + fsck_log ("pass1: (is_leaf_bad) bad leaf (%lu)\n", bh->b_blocknr); + + /* Save good items only to put them into tree at the + end of this pass */ + save_items (&saved_items, bh); + + brelse (bh); + continue; + } + + try_to_insert_pointer_to_leaf (bh); + brelse (bh); + } + + +#if 0 + /* read all leaves found on the pass 0 */ + for (i = 0; i < SB_BLOCK_COUNT (fs); i += nr_to_read) { + to_scan = how_many_to_scan (fs, i, nr_to_read); + if (to_scan) { + print_how_far (&done, total, to_scan, fsck_quiet (fs)); + + /* at least one of nr_to_read blocks is to be checked */ + bbh = bread (fs->s_dev, i / nr_to_read, fs->s_blocksize * nr_to_read); + if (bbh) { + for (j = 0; j < nr_to_read; j ++) { + if (!is_used_leaf (i + j)) + continue; + + data = bbh->b_data + j * fs->s_blocksize; + + what_node = who_is_this (data, fs->s_blocksize); + if ( what_node != THE_LEAF ) { + fsck_progress ("build_the_tree: nothing but leaves are expected. " + "Block %lu - %s\n", i + j, + (what_node == THE_INTERNAL) ? "internal" : "??"); + continue; + } + + if (is_block_used (i + j)) + /* block is in new tree already */ + die ("build_the_tree: leaf (%lu) is in tree already\n", + i + j); + + /* fprintf (block_list, "leaf %d\n", i + j);*/ + + bh = make_buffer (fs->s_dev, i + j, fs->s_blocksize, data); + stats(fs)->analyzed ++; + + /* the leaf may still contain indirect items with wrong + slots. Fix that */ + pass1_correct_leaf (fs, bh); + + if (node_item_number (bh) == 0) { + /* all items were deleted on pass 0 or pass 1 */ + mark_buffer_clean (bh); + brelse (bh); + continue; + } + + if (is_leaf_bad (bh)) { + /* FIXME: will die */ + fsck_log ("pass1: (is_leaf_bad) bad leaf (%lu)\n", bh->b_blocknr); + + /* Save good items only to put them into tree at the + end of this pass */ + save_items (&saved_items, bh); + + brelse (bh); + continue; + } + + try_to_insert_pointer_to_leaf (bh); + brelse (bh); + } + + bforget (bbh); + } else { + done -= to_scan; + + /* bread failed */ + if (nr_to_read != 1) { + /* we tryied to read bunch of blocks. Try to read them by one */ + nr_to_read = 1; + i --; + continue; + } else { + /* we were reading one block at time, and failed, so mark + block bad */ + fsck_progress ("pass0: block %lu is bad, marked used\n", i); + } + } + } + + if (nr_to_read == 1 && ((i + 1) % NR_TO_READ) == 0) { + /* we have read NR_TO_READ blocks one at time, switch back to + reading NR_TO_READ blocks at time */ + i -= (NR_TO_READ - 1); + nr_to_read = NR_TO_READ; + } + } +#endif + fsck_progress ("\n"); + + /* Pass 1a (this should die) */ + + /* put saved items into tree. These items were in leaves, those could not + be inserted into tree because some indirect items point to those + leaves. Rather than lookup for corresponding unfm pointers in the tree, + we save items of those leaves and put them into tree separately */ + if (how_many_items_were_saved ()) { + fsck_progress ("There were %lu saved items\nPass 1a - ", + how_many_items_were_saved ()); + fflush (stdout); + put_saved_items_into_tree_1 (saved_items); + } + + stage_report (1, fs); + /* end of pass 1 */ + + + if (SB_ROOT_BLOCK(fs) == -1) + die ("\n\nNo reiserfs metadata found"); + + /* pass 2 */ + pass_2_take_bad_blocks_put_into_tree (); + + flush_buffers (); + + stage_report (2, fs); + + fsck_progress ("Tree is built. Checking it - "); fflush (stdout); + reiserfsck_check_pass1 (); + fsck_progress ("done\n"); fflush (stdout); + + reiserfs_delete_bitmap (bad_unfm_in_tree_once_bitmap); + +} + +#if 0 + +/* pass the S+ tree of filesystem */ +void recover_internal_tree (struct super_block * s) +{ + check_internal_structure(s); + build_the_tree(); +} +#endif + + +void rebuild_sb (reiserfs_filsys_t fs) +{ + int version; + struct buffer_head * bh; + struct reiserfs_super_block * rs; + __u32 blocks; + + + if (no_reiserfs_found (fs)) { + char * answer = 0; + size_t n = 0; + printf("\nwhat is version of ReiserFS you use[1-4]\n" + "\t(1) 3.6.x\n" + "\t(2) >=3.5.9\n" + "\t(3) < 3.5.9 converted to new format\n" + "\t(4) < 3.5.9\n" + "\t(X) exit\n"); + getline (&answer, &n, stdin); + version = atoi (answer); + if (version < 1 || version > 4) + die ("rebuild_sb: wrong version"); + + fs->s_blocksize = 4096; + + switch(version){ + case 1: + case 2: + bh = getblk (fs->s_dev, (REISERFS_DISK_OFFSET_IN_BYTES / fs->s_blocksize), + fs->s_blocksize); + break; + case 3: + case 4: + bh = getblk (fs->s_dev, (2), fs->s_blocksize); + break; + default: + exit(0); + } + if (!bh) + die ("rebuild_sb: can't bread"); + rs = (struct reiserfs_super_block *)bh->b_data; + fs->s_rs = rs; + } + else + { + /* reiserfs super block is found */ + version = check_sb(fs); + if (!user_confirmed ("\nDo you want to remake your super block\n" + "(say no if you use resizer)[Yes/no]: ", "Yes\n")) + return; + rs = fs->s_rs; + bh = fs->s_sbh; + } + + // set block number on the device and number of bitmap blocks needed to + // address all blocks + blocks = (count_blocks ("", fs->s_blocksize, fs->s_dev) / 8) * 8; + set_block_count (rs, blocks); + //rs->s_block_count = cpu_to_le32(blocks); + + set_bmap_nr (rs, (blocks + (fs->s_blocksize * 8 - 1)) / (fs->s_blocksize * 8)); + set_journal_size(rs, JOURNAL_BLOCK_COUNT); + + //rs->s_bmap_nr = cpu_to_le16( blocks / (g_sb.s_blocksize * 8) + + // ((blocks % (g_sb.s_blocksize * 8)) ? 1 : 0) ); + + switch (version){ + case 1: + // super block v2 at 64k offset + set_blocksize (rs, fs->s_blocksize); + strncpy (rs->s_v1.s_magic, REISER2FS_SUPER_MAGIC_STRING, + strlen(REISER2FS_SUPER_MAGIC_STRING)); + set_journal_start (rs, get_journal_start_must (fs->s_blocksize)); + set_version (rs, REISERFS_VERSION_2); + set_objectid_map_max_size (rs, (fs->s_blocksize - SB_SIZE) / sizeof(__u32) / 2 * 2); + break; + + case 2: + // super block v1 at 64k offset + set_blocksize (rs, fs->s_blocksize); + strncpy (rs->s_v1.s_magic, REISERFS_SUPER_MAGIC_STRING, + strlen(REISERFS_SUPER_MAGIC_STRING)); + set_journal_start (rs, get_journal_start_must (fs->s_blocksize)); + set_version (rs, REISERFS_VERSION_1); + set_objectid_map_max_size (rs, (fs->s_blocksize - SB_SIZE_V1) / sizeof(__u32) / 2 * 2); + break; + + case 3: + // super block v2 at 8k offset + set_blocksize (rs, fs->s_blocksize); + strncpy (rs->s_v1.s_magic, REISER2FS_SUPER_MAGIC_STRING, + strlen(REISER2FS_SUPER_MAGIC_STRING)); + set_journal_start (rs, get_journal_old_start_must (rs)); + set_version (rs, REISERFS_VERSION_2); + set_objectid_map_max_size (rs, (fs->s_blocksize - SB_SIZE) / sizeof(__u32) / 2 * 2); + break; + + case 4: + // super block v1 at 8k offset + set_blocksize (rs, fs->s_blocksize); + strncpy (rs->s_v1.s_magic, REISERFS_SUPER_MAGIC_STRING, + strlen(REISERFS_SUPER_MAGIC_STRING)); + set_journal_start (rs, get_journal_old_start_must (rs)); + set_version (rs, REISERFS_VERSION_1); + set_objectid_map_max_size (rs, (fs->s_blocksize - SB_SIZE_V1) / sizeof(__u32) / 2 * 2); + break; + } + + print_block (stderr, fs, bh); + if (user_confirmed ("Is this ok ? [N/Yes]: ", "Yes\n")) { + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); + fsck_progress ("\nDo not forget to run reiserfsck --rebuild-tree\n\n"); + } else + fsck_progress ("Super block was not written\n"); + brelse (bh); +} + +/* + check_sb and rebuild-sb don't touch these fields: + __u32 s_journal_dev; + __u32 s_journal_trans_max ; + __u32 s_journal_block_count ; + __u32 s_journal_max_batch ; + __u32 s_journal_max_commit_age ; + __u32 s_journal_max_trans_age ; + + others are checked and set in either rebuild_sb or rebuild-tree +*/ + + + + + + + + + + + + + + + + + + diff --git a/fsck/pass2.c b/fsck/pass2.c new file mode 100644 index 0000000..c598a9f --- /dev/null +++ b/fsck/pass2.c @@ -0,0 +1,485 @@ +/* + * Copyright 1996-2001 Hans Reiser + */ + +#include "fsck.h" + + +/* on pass2 we take leaves which could not be inserted into tree + during pass1 and insert each item separately. It is possible that + items of different objects with the same key can be found. We treat + that in the following way: we put it into tree with new key and + link it into /lost+found directory with name made of dir,oid. When + coming item is a directory - we delete object from the tree, put it + back with different key, link it to /lost+found directory and + insert directory as it is */ + +/* relocation rules: we have an item (it is taken from "non-insertable" + leaf). It has original key yet. We check to see if object with this + key is remapped. Object can be only remapped if it is not a piece + of directory */ + + +/* in list of this structures we store what has been + relocated. */ +struct relocated { + unsigned long old_dir_id; + unsigned long old_objectid; + unsigned long new_objectid; + /*mode_t mode;*/ + struct relocated * next; +}; + + +/* all relocated files will be linked into lost+found directory at the + beginning of semantic pass */ +struct relocated * relocated_list; + + +/* return objectid the object has to be remapped with */ +__u32 objectid_for_relocation (struct key * key) +{ + struct relocated * cur; + + cur = relocated_list; + + while (cur) { + if (cur->old_dir_id == key->k_dir_id && + cur->old_objectid == key->k_objectid) + /* object is relocated already */ + return cur->new_objectid; + cur = cur->next; + } + + cur = getmem (sizeof (struct relocated)); + cur->old_dir_id = key->k_dir_id; + cur->old_objectid = key->k_objectid; + cur->new_objectid = get_unused_objectid (fs); + cur->next = relocated_list; + relocated_list = cur; + fsck_log ("relocation: (%K) is relocated to (%lu, %lu)\n", + key, key->k_dir_id, cur->new_objectid); + return cur->new_objectid; +} + + +/* this item is in tree. All unformatted pointer are correct. Do not + check them */ +static void save_item_2 (struct si ** head, struct item_head * ih, + char * item, __u32 blocknr) +{ + struct si * si, * cur; + + si = getmem (sizeof (*si)); + si->si_dnm_data = getmem (ih_item_len(ih)); + /*si->si_blocknr = blocknr;*/ + memcpy (&(si->si_ih), ih, IH_SIZE); + memcpy (si->si_dnm_data, item, ih_item_len(ih)); + + if (*head == 0) + *head = si; + else { + cur = *head; + while (cur->si_next) + cur = cur->si_next; + cur->si_next = si; + } + return; +} + + +struct si * save_and_delete_file_item (struct si * si, struct path * path) +{ + struct buffer_head * bh = PATH_PLAST_BUFFER (path); + struct item_head * ih = PATH_PITEM_HEAD (path); + + save_item_2 (&si, ih, B_I_PITEM (bh, ih), bh->b_blocknr); + + /* delete item temporary - do not free unformatted nodes */ + reiserfsck_delete_item (path, 1/*temporary*/); + return si; +} + + +/* check whether there are any directory items with this key */ +static int should_relocate (struct item_head * ih) +{ + struct key key; + struct key * rkey; + struct path path; + struct item_head * path_ih; + + + /* starting with the leftmost item with this key */ + key = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA); + + while (1) { + usearch_by_key (fs, &key, &path); + if (get_item_pos (&path) == B_NR_ITEMS (get_bh (&path))) { + rkey = uget_rkey (&path); + if (rkey && !not_of_one_file (&key, rkey)) { + /* file continues in the right neighbor */ + key = *rkey; + pathrelse (&path); + continue; + } + /* there is no more items with this key */ + pathrelse (&path); + break; + } + + path_ih = get_ih (&path); + if (not_of_one_file (&key, &(path_ih->ih_key))) { + /* there are no more item with this key */ + pathrelse (&path); + break; + } + + /* ok, item found, but make sure that it is not a directory one */ + if ((is_stat_data_ih (path_ih) && !not_a_directory (get_item (&path))) || + (is_direntry_ih (path_ih))) { + /* item of directory found. so, we have to relocate the file */ + pathrelse (&path); + return 1; + } + key = path_ih->ih_key; + set_offset (KEY_FORMAT_1, &key, get_offset (&key) + 1); + pathrelse (&path); + } + return 0; +} + + +/* delete all items (but directory ones) with the same key 'ih' has + (including stat data of not a directory) and put them back at the + other place */ +void relocate_file (struct item_head * ih, int change_ih) +{ + struct key key; + struct key * rkey; + struct path path; + struct item_head * path_ih; + struct si * si; + __u32 new_objectid; + + + /* starting with the leftmost one - look for all items of file, + store them and delete */ + key = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA); + + si = 0; + while (1) { + usearch_by_key (fs, &key, &path); + if (get_item_pos (&path) == B_NR_ITEMS (get_bh (&path))) { + rkey = uget_rkey (&path); + if (rkey && !not_of_one_file (&key, rkey)) { + /* file continues in the right neighbor */ + key = *rkey; + pathrelse (&path); + continue; + } + /* there is no more items with this key */ + pathrelse (&path); + break; + } + + path_ih = get_ih (&path); + if (not_of_one_file (&key, &(path_ih->ih_key))) { + /* there are no more item with this key */ + pathrelse (&path); + break; + } + + /* ok, item found, but make sure that it is not a directory one */ + if ((is_stat_data_ih (path_ih) && !not_a_directory (get_item (&path))) || + (is_direntry_ih (path_ih))) { + /* item of directory found. Leave it in the tree */ + key = path_ih->ih_key; + set_offset (KEY_FORMAT_1, &key, get_offset (&key) + 1); + pathrelse (&path); + continue; + } + + si = save_and_delete_file_item (si, &path); + } + + + if (si || change_ih) { + int moved_items; + struct key old, new; + + /* get new objectid for relocation or get objectid with which file + was relocated already */ + new_objectid = objectid_for_relocation (&ih->ih_key); + if (change_ih) + ih->ih_key.k_objectid = new_objectid; + + moved_items = 0; + + /* put all items removed back into tree */ + while (si) { + /*fsck_log ("relocate_file: move %H to ", &si->si_ih);*/ + old = si->si_ih.ih_key; + si->si_ih.ih_key.k_objectid = new_objectid; + new = si->si_ih.ih_key; + /*fsck_log ("%H\n", &si->si_ih);*/ + insert_item_separately (&(si->si_ih), si->si_dnm_data, 1/*was in tree*/); + si = remove_saved_item (si); + moved_items ++; + } + if (moved_items) + fsck_log ("relocate_file: %d items of file %K moved to %K\n", + moved_items, &old, &new); + } +} + + +/* this works for both new and old stat data */ +#define st_mode(sd) le16_to_cpu(((struct stat_data *)(sd))->sd_mode) + +#define st_mtime_v1(sd) le32_to_cpu(((struct stat_data_v1 *)(sd))->sd_mtime) +#define st_mtime_v2(sd) le32_to_cpu(((struct stat_data *)(sd))->sd_mtime) + +static void overwrite_stat_data (struct item_head * new_ih, + void * new_item, struct path * path) +{ + if (stat_data_v1 (new_ih)) { + if (st_mtime_v1 (new_item) > st_mtime_v1 (get_item (path))) { + memcpy (get_item (path), new_item, SD_V1_SIZE); + mark_buffer_dirty (get_bh (path)); + } + } else { + if (st_mtime_v2 (new_item) > st_mtime_v2 (get_item (path))) { + memcpy (get_item (path), new_item, SD_SIZE); + mark_buffer_dirty (get_bh (path)); + } + } + return; +} + + +/* insert sd item if it does not exist, overwrite it otherwise */ +static void put_sd_into_tree (struct item_head * new_ih, char * new_item) +{ + struct path path; + + if (!not_a_directory (new_item)) { + /* new item is a stat data of a directory. So we have to + relocate all items which have the same short key and are of + not a directory */ + relocate_file (new_ih, 0/*do not change new_ih*/); + } else { + /* new item is a stat data of something else but directory. If + there are items of directory - we have to relocate the file */ + if (should_relocate (new_ih)) + relocate_file (new_ih, 1/*change new_ih*/); + } + + /* if we will have to insert item into tree - it is ready */ + zero_nlink (new_ih, new_item); + mark_item_unreachable (new_ih); + + /* we are sure now that if we are inserting stat data of a + directory - there are no items with the same key which are not + items of a directory, and that if we are inserting stat data is + of not a directory - it either has new key already or there are + no items with this key which are items of a directory */ + if (usearch_by_key (fs, &(new_ih->ih_key), &path) == ITEM_FOUND) { + /* this stat data is found */ + if (ih_key_format (get_ih(&path)) != ih_key_format (new_ih)) { + /* in tree stat data and a new one are of different + formats */ + fsck_log ("put_sd_into_tree: inserting stat data %K (%M)..", + &(new_ih->ih_key), st_mode (new_item)); + if (stat_data_v1 (new_ih)) { + /* sd to be inserted is of V1, where as sd in the tree + is of V2 */ + fsck_log ("found newer in the tree (%M), skip inserting\n", + st_mode (get_item (&path))); + } else { + /* the stat data in the tree is sd_v1 */ + fsck_log ("older sd (%M) is replaced with it\n", + st_mode (get_item (&path))); + reiserfsck_delete_item (&path, 0/*not temporary*/); + + usearch_by_key (fs, &new_ih->ih_key, &path); + reiserfsck_insert_item (&path, new_ih, new_item); + } + } else { + /* both stat data are of the same version */ + overwrite_stat_data (new_ih, new_item, &path); + pathrelse (&path); + } + return; + } + + /* item not found, insert a new one */ + reiserfsck_insert_item (&path, new_ih, new_item); +} + + +/* this tries to put each item entry to the tree, if there is no items + of the directory, insert item containing 1 entry */ +static void put_directory_item_into_tree (struct item_head * comingih, char * item) +{ + struct reiserfs_de_head * deh; + int i; + char * buf; + char * name; + int namelen; + + /* if there are anything ith this key but a directory - move it + somewhere else */ + relocate_file (comingih, 0/* do not change ih */); + + deh = (struct reiserfs_de_head *)item; + + for (i = 0; i < ih_entry_count (comingih); i ++, deh ++) { + name = name_in_entry (deh, i); + namelen = name_length (comingih, deh, i); + + if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) + reiserfs_panic ("put_directory_item_into_tree: should be hashed properly ()"); + + asprintf (&buf, "%.*s", namelen, name); + /* 1 for fsck is important: if there is no any items of this + directory in the tree yet - new item will be inserted + marked not reached */ + reiserfs_add_entry (fs, &(comingih->ih_key), buf, (struct key *)&(deh->deh_dir_id), 1/*fsck_need*/); + free (buf); + } + + /* make a directory */ +} + + +/* relocated files get added into lost+found with slightly different names */ +static void link_one (struct relocated * file) +{ + char * name; + struct key obj_key; + + asprintf (&name, "%lu,%lu", file->old_dir_id, file->new_objectid); + obj_key.k_dir_id = file->old_dir_id; + obj_key.k_objectid = file->new_objectid; + + /* 0 for fsck_need does not mean too much - it would make effect + if there were no this directory yet. But /lost_found is there + already */ + reiserfs_add_entry (fs, &lost_found_dir_key, name, &obj_key, 0/*fsck_need*/); + stats(fs)->relocated ++; + free (name); +} + + +void link_relocated_files (void) +{ + struct relocated * tmp; + int count; + + count = 0; + while (relocated_list) { + link_one (relocated_list); + tmp = relocated_list; + relocated_list = relocated_list->next; + freemem (tmp); + count ++; + } +} + + +void insert_item_separately (struct item_head * ih, + char * item, int was_in_tree) +{ + if (ih->ih_key.k_dir_id == ih->ih_key.k_objectid) + reiserfs_panic ("insert_item_separately: can not insert bad item %H", ih); + + if (is_stat_data_ih (ih)) { + put_sd_into_tree (ih, item); + } else if (is_direntry_ih (ih)) { + put_directory_item_into_tree (ih, item); + } else { + if (should_relocate (ih)) + relocate_file (ih, 1/*change new_ih*/); + + reiserfsck_file_write (ih, item, was_in_tree); + } +} + + +static void put_items (struct buffer_head * bh) +{ + int i; + struct item_head * ih; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < B_NR_ITEMS (bh); i ++, ih ++) { + if (i && bad_pair (fs, bh, i)) { + /* skip item if it is in wrong order */ + continue; + } + insert_item_separately (ih, B_I_PITEM (bh, ih), 0/*was in tree*/); + } +} + + +/* uninsertable blocks are marked by 0s in uninsertable_leaf_bitmap + during the pass 1. They must be not in the tree */ +void pass_2_take_bad_blocks_put_into_tree (void) +{ + struct buffer_head * bh; + unsigned long j; + unsigned long bb_counter = 0; + int what_node; + + + if (!stats(fs)->uninsertable_leaves) + return; + + if (fsck_log_file (fs) != stderr) + fsck_log ("####### Pass 2 #######\n"); + + fsck_progress ("\nPass2:\n"); + + j = 0; + while (reiserfs_bitmap_find_zero_bit (uninsertable_leaf_bitmap, &j) == 0) { + bh = bread (fs->s_dev, j, fs->s_blocksize); + if (bh == 0) { + fsck_log ("pass_2_take_bad_blocks_put_into_tree: " + "unable to read %lu block on device 0x%x\n", + j, fs->s_dev); + goto next; + } + + if (is_block_used (bh->b_blocknr)) { + fsck_log ("pass_2_take_bad_blocks_put_into_tree: " + "block %d can not be in tree\n", bh->b_blocknr); + goto next; + } + /* this must be leaf */ + what_node = who_is_this (bh->b_data, fs->s_blocksize); + if (what_node != THE_LEAF) { // || B_IS_KEYS_LEVEL(bh)) { + fsck_log ("take_bad_blocks_put_into_tree: buffer (%b %z) must contain leaf\n", bh, bh); + goto next; + } + + /*fsck_log ("block %lu is being inserted\n", bh->b_blocknr);*/ + put_items (bh); + + print_how_far (&bb_counter, stats(fs)->uninsertable_leaves, 1, fsck_quiet (fs)); + + next: + brelse (bh); + j ++; + } + + fsck_progress ("\n"); + + + if (bb_counter != stats(fs)->uninsertable_leaves) + die ("take_bad_blocks_put_into_tree: found bad block %d, must be %d", + bb_counter, stats(fs)->uninsertable_leaves); + +} diff --git a/fsck/pass4.c b/fsck/pass4.c new file mode 100644 index 0000000..acbdffe --- /dev/null +++ b/fsck/pass4.c @@ -0,0 +1,79 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser + */ +#include "fsck.h" + + +void get_next_key (struct path * path, int i, struct key * key) +{ + struct buffer_head * bh = PATH_PLAST_BUFFER (path); + struct key * rkey; + + + if (i < B_NR_ITEMS (bh) - 1) { + /* next item is in this block */ + copy_key (key, B_N_PKEY (bh, i + 1)); + return; + } + + rkey = uget_rkey (path); + if (rkey) { + /* got next item key from right delimiting key */ + copy_key (key, rkey); + } else { + /* there is no next item */ + memset (key, 0xff, KEY_SIZE); + } +} + + +int pass_4_check_unaccessed_items (void) +{ + struct key key; + struct path path; + int i; + struct buffer_head * bh; + struct item_head * ih; + unsigned long items; + + path.path_length = ILLEGAL_PATH_ELEMENT_OFFSET; + key = root_dir_key; + + fsck_progress ("Pass 4 - "); + items = 0; + + while (usearch_by_key (fs, &key, &path) == ITEM_FOUND) { + bh = PATH_PLAST_BUFFER (&path); + + /* print ~ how many leaves were scanned and how fast it was */ + if (!fsck_quiet (fs)) + print_how_fast (0, items++, 50); + + for (i = get_item_pos (&path), ih = get_ih (&path); i < B_NR_ITEMS (bh); i ++, ih ++) { + + + if (!is_item_reachable (ih)) { + + get_next_key (&path, i, &key); + + stats(fs)->deleted_items ++; + + PATH_LAST_POSITION (&path) = i; + reiserfsck_delete_item (&path, 0); + + goto cont; + } + } + get_next_key (&path, i - 1, &key); + pathrelse (&path); + + cont: + } + + pathrelse (&path); + + fsck_progress ("done\n"); + stage_report (4, fs); + + return 0; +} diff --git a/fsck/reiserfsck.8 b/fsck/reiserfsck.8 new file mode 100644 index 0000000..bec3c9a --- /dev/null +++ b/fsck/reiserfsck.8 @@ -0,0 +1,91 @@ +.\" -*- nroff -*- +.\" Copyright 1996-2001 Hans Reiser. +.\" +.TH REISERFSCK 8 "March 2001" "Reiserfsprogs-3.x.0j" +.SH NAME +reiserfsck \- check a Linux Reiserfs file system +.SH SYNOPSIS +.B reiserfsck +[ +.B -arvixoV +] [ +.B -l logfilename +] [ +.B --rebuild-tree +] [ +.B --check +] [ +.B --rebuild-sb +] [ +.B --interactive +] [ +\fB --logfile \fI logfilename +] [ +.B --fix-fixable +] +.I device +.SH DESCRIPTION +It looks for reiserfs filesystem on a device, replays transactions +which are to be replayed and either check or repair the filesystem +.TP +.I device +is the special file corresponding to the device (e.g /dev/hdXX for +IDE disk partition or /dev/sdXX for SCSI disk partition). +.SH OPTIONS +.TP +.B --check +This checks filesystem consistency. This is a default action. It may +be used on a filesystem mounted read-only +.TP +.B --rebuild-tree +This rebuilds filesystem tree using leaf nodes found on the +device. Normally, you do not need this, but if you have to rebuild +tree - please backup whole partition first or at least the most +important data if you can mount the partition. +.TP +.B --rebuild-sb +.TP +.B --interactive, -i +This makes \fBreiserfsck\fR to stop after each pass completed. +.TP +.B --quiet, -q +have \fBreiserfsck\fR to not reflect its progress +.TP +.B --nolog, -n +have \fBreiserfsck\fR to not log anything +.TP +\fB--logfile \fIfilename\fR, \fB-l \fI filename +have \fBreiserfsck\fR to put info about found corruptions in logfile +rather than on stderr +.TP +.B --fix-fixable, -x +have \fBreiserfsck\fR to recover corruptions which can be fixed w/o +--rebuild-tree when it is runnig in check mode. Corruptions which can +be fixed so far: bad pointers to data blocks, wrong directory's +st_size and st_blocks, directories entries pointing to nowhere can be +deleted +.TP +.B --fix-non-critical, -o +have \fBreiserfsck\fR to fix: file sizes when they are bigger than +real file size, set file mode to regular file when mode looks wrong +and to try to recover "objectid sharing" problem +.TP +.B -a +When it is set - \fBreiserfsck\fR assumes that it is called by fsck -A +and just returns even if filesystem seems not umounted cleanly +.TP +.B -r +ignored +.TP +.B -V +Prints version and exits +.SH AUTHOR +This version of \fBreiserfsck\fR has been written by Hans Reiser +<reiser@idiom.com>. +.SH BUGS +There are probably few of them. Please, report bugs to Hans Reiser <reiser@idiom.com>. +.SH TODO +Faster recovering, signal handling, i/o error handling, etc. +.SH SEE ALSO +.BR mkreiserfs (8), +.BR debugreiserfs (8) diff --git a/fsck/segments.c b/fsck/segments.c new file mode 100644 index 0000000..73a2173 --- /dev/null +++ b/fsck/segments.c @@ -0,0 +1,202 @@ +/* + * Copyright 1998 Hans Reiser + */ +/*#include <stdio.h> +#include <string.h>*/ +/*#include <asm/bitops.h> +#include "../include/reiserfs_fs.h" +#include "../include/reiserfs_fs_sb.h" +#include "../include/reiserfslib.h"*/ +#include "fsck.h" + + +/* there is a situation, when we overwrite contents of unformatted + node with direct item. One unformatted node can be overwritten + several times by direct items */ +/* +struct overwritten_unfm_segment { + int ous_begin; + int ous_end; + struct overwritten_unfm_segment * ous_next; +}; +*/ +struct overwritten_unfm { + unsigned long ou_unfm_ptr; /* block number of unfm node */ + unsigned long ou_dir_id; + unsigned long ou_objectid; /* key corresponding to an unfm node */ + unsigned long ou_offset; + + struct overwritten_unfm_segment * ou_segments; /* list of segmens, than have been overwritten in ths unfm node */ +}; + +struct overwritten_unfm ** g_overwritten_unfms; +int g_overwritten_unfms_amount; /* number of unformatted nodes, which contain direct items */ + + +/* adds segment to the single linked list of segments sorted by begin + field. Retuns pointer to first element of list */ +static struct overwritten_unfm_segment * add_segment (struct overwritten_unfm_segment * first, int begin, int end) +{ + struct overwritten_unfm_segment * new, * next, * prev; + + new = getmem (sizeof (struct overwritten_unfm_segment)); + new->ous_begin = begin; + new->ous_end = end; + new->ous_next = 0; + + next = first; + prev = 0; + while (next) { + if (next->ous_begin > begin) + break; + prev = next; + next = next->ous_next; + } + + if (prev == 0) { + /* insert into head of list */ + first = new; + } else { + prev->ous_next = new; + } + new->ous_next = next; + return first; +} + + +/* input parameter + `list_head` - first element of overlapping segments sorted by left edge + `unoverwritten_segment` - returned by previous call of get_unoverwritten_segment or (-2,-2) if called first time + */ +/* returns + 1 and segment unoverwritten by elements of list `list_head` + 0 if there isno such segment + */ +int get_unoverwritten_segment (struct overwritten_unfm_segment * list_head, struct overwritten_unfm_segment * unoverwritten_segment) +{ + int end; + + /* look for segment, which has begin field greater than end of previous interval */ + while (list_head->ous_begin <= unoverwritten_segment->ous_end) { + list_head = list_head->ous_next; + } + /* look for the end of the continuous region covered by otrezkami */ + end = list_head->ous_end; + while (list_head->ous_next) { + if (list_head->ous_next->ous_begin > end + 1) + /* intreval found */ + break; + if (list_head->ous_next->ous_end > end) + end = list_head->ous_next->ous_end; + list_head = list_head->ous_next; + } + /* ok, between segment and segment->next we have an interval (segment->next != 0) */ + if (list_head->ous_next != 0) { + unoverwritten_segment->ous_begin = end + 1; + unoverwritten_segment->ous_end = list_head->ous_next->ous_begin - 1; + return 1; + } + return 0; +} + + +void print_segments (struct overwritten_unfm_segment * list_head) +{ + struct overwritten_unfm_segment * cur; + + cur = list_head; + while (cur) { + printf ("%s%d %d%s", cur == list_head ? "(" : "", cur->ous_begin, cur->ous_end, cur->ous_next ? ", " : ")\n"); + cur = cur->ous_next; + } +} + + +/* this prepare list of segments to extracting of unoverwritten segments */ +struct overwritten_unfm_segment * find_overwritten_unfm (unsigned long unfm, int length, struct overwritten_unfm_segment * segment_to_init) +{ + int i; + + for (i = 0; i < g_overwritten_unfms_amount && g_overwritten_unfms[i] != 0; i ++) + if (g_overwritten_unfms[i]->ou_unfm_ptr == unfm) { + if (g_overwritten_unfms[i]->ou_segments == 0) + die ("find_overwritten_unfm: no segment found"); + g_overwritten_unfms[i]->ou_segments = add_segment (g_overwritten_unfms[i]->ou_segments, -1, -1); + add_segment (g_overwritten_unfms[i]->ou_segments, length, length); + segment_to_init->ous_begin = -2; + segment_to_init->ous_end = -2; + return g_overwritten_unfms[i]->ou_segments; + } + return 0; +} + +struct overwritten_unfm * look_for_overwritten_unfm (__u32 unfm) +{ + int i; + + for (i = 0; i < g_overwritten_unfms_amount && g_overwritten_unfms[i] != 0; i ++) + if (g_overwritten_unfms[i]->ou_unfm_ptr == unfm) + return g_overwritten_unfms[i]; + return 0; +} + +#define GROW_BY 10 +struct overwritten_unfm * add_overwritten_unfm (unsigned long unfm, struct item_head * direct_ih) +{ + int i; + + for (i = 0; i < g_overwritten_unfms_amount && g_overwritten_unfms[i] != 0; i ++) { + if (g_overwritten_unfms[i]->ou_unfm_ptr == unfm) + return g_overwritten_unfms[i]; + } + + if (i == g_overwritten_unfms_amount) { + g_overwritten_unfms = expandmem (g_overwritten_unfms, sizeof (struct overwritten_unfm *) * i, + sizeof (struct overwritten_unfm *) * GROW_BY); + g_overwritten_unfms_amount += GROW_BY; + } + g_overwritten_unfms[i] = getmem (sizeof (struct overwritten_unfm)); + g_overwritten_unfms[i]->ou_unfm_ptr = unfm; + g_overwritten_unfms[i]->ou_dir_id = direct_ih->ih_key.k_dir_id; + g_overwritten_unfms[i]->ou_objectid = direct_ih->ih_key.k_objectid; + g_overwritten_unfms[i]->ou_offset = get_offset(&direct_ih->ih_key) - (get_offset(&direct_ih->ih_key) - 1) % fs->s_blocksize; + return g_overwritten_unfms[i]; +} + + +void save_unfm_overwriting (unsigned long unfm, struct item_head * direct_ih) +{ + struct overwritten_unfm * ov_unfm; + + /* add new overwritten unfm or return existing one */ + ov_unfm = add_overwritten_unfm (unfm, direct_ih); + ov_unfm->ou_segments = add_segment (ov_unfm->ou_segments, (get_offset(&direct_ih->ih_key) - 1) % fs->s_blocksize, + (get_offset(&direct_ih->ih_key) - 1) % fs->s_blocksize + ih_item_len (direct_ih) - 1); +} + + +void free_overwritten_unfms (void) +{ + int i; + + for (i = 0; i < g_overwritten_unfms_amount && g_overwritten_unfms[i]; i ++) { + /* free all segments */ + while (g_overwritten_unfms[i]->ou_segments) { + struct overwritten_unfm_segment * tmp; + + tmp = g_overwritten_unfms[i]->ou_segments->ous_next; + freemem (g_overwritten_unfms[i]->ou_segments); + g_overwritten_unfms[i]->ou_segments = tmp; + } + /* free struct overwritten_unfm */ + freemem (g_overwritten_unfms[i]); + } + + /* free array of pointers to overwritten unfms */ + if (g_overwritten_unfms) + freemem (g_overwritten_unfms); +} + + + + diff --git a/fsck/semantic.c b/fsck/semantic.c new file mode 100644 index 0000000..a541e3b --- /dev/null +++ b/fsck/semantic.c @@ -0,0 +1,1346 @@ +/* + * Copyright 1996-1999 Hans Reiser + */ +#include "fsck.h" + + +struct key root_dir_key = {REISERFS_ROOT_PARENT_OBJECTID, + REISERFS_ROOT_OBJECTID, {{0, 0},}}; +struct key parent_root_dir_key = {0, REISERFS_ROOT_PARENT_OBJECTID, {{0, 0},}}; +struct key lost_found_dir_key = {REISERFS_ROOT_OBJECTID, 0, {{0, 0}, }}; + + +struct path_key +{ + struct short_key + { + __u32 k_dir_id; + __u32 k_objectid; + } key; + struct path_key * next, * prev; +}; + +struct path_key * head_key = NULL; +struct path_key * tail_key = NULL; + +void check_path_key(struct key * key) +{ + struct path_key * cur = head_key; + + while(cur != NULL) + { + if (!comp_short_keys(&cur->key, key)) + die("\nsemantic check: loop found %k", key); + cur = cur->next; + } +} + +void add_path_key(struct key * key) +{ + check_path_key(key); + + if (tail_key == NULL) + { + tail_key = getmem(sizeof(struct path_key)); + head_key = tail_key; + tail_key->prev = NULL; + }else{ + tail_key->next = getmem(sizeof(struct path_key)); + tail_key->next->prev = tail_key; + tail_key = tail_key->next; + } + copy_short_key (&tail_key->key, key); + tail_key->next = NULL; +} + +void del_path_key() +{ + if (tail_key == NULL) + die("wrong path_key structure"); + + if (tail_key->prev == NULL) + { + freemem(tail_key); + tail_key = head_key = NULL; + }else{ + tail_key = tail_key->prev; + freemem(tail_key->next); + tail_key->next = NULL; + } +} + +/* semantic pass progress */ +static void print_name (char * dir_name, int len) +{ + int i; + + if (fsck_quiet (fs)) + return; + printf("/"); + for (i = 0; i<len; i++, dir_name++) + printf ("%c", *dir_name); + fflush (stdout); +} + +static void erase_name (int len) +{ + int i; + + if (fsck_quiet (fs)) + return; + for (i = 0; i<=len; i++) + printf("\b"); + for (i = 0; i<=len; i++) + printf(" "); + for (i = 0; i<=len; i++) + printf("\b"); + fflush (stdout); +} + + +void get_set_sd_field (int field, struct item_head * ih, void * sd, + void * value, int set) +{ + if (ih_key_format (ih) == KEY_FORMAT_1) { + struct stat_data_v1 * sd_v1 = sd; + + switch (field) { + case GET_SD_MODE: + if (set) + sd_v1->sd_mode = cpu_to_le16 (*(__u16 *)value); + else + *(__u16 *)value = le16_to_cpu (sd_v1->sd_mode); + break; + + case GET_SD_SIZE: + /* value must point to 64 bit int */ + if (set) + sd_v1->sd_size = cpu_to_le32 (*(__u64 *)value); + else + *(__u64 *)value = le32_to_cpu (sd_v1->sd_size); + break; + + case GET_SD_BLOCKS: + if (set) + sd_v1->u.sd_blocks = cpu_to_le32 (*(__u32 *)value); + else + *(__u32 *)value = le32_to_cpu (sd_v1->u.sd_blocks); + break; + + case GET_SD_NLINK: + /* value must point to 32 bit int */ + if (set) + sd_v1->sd_nlink = cpu_to_le16 (*(__u32 *)value); + else + *(__u32 *)value = le16_to_cpu (sd_v1->sd_nlink); + break; + + case GET_SD_FIRST_DIRECT_BYTE: + if (set) + sd_v1->sd_first_direct_byte = cpu_to_le32 (*(__u32 *)value); + else + *(__u32 *)value = le32_to_cpu (sd_v1->sd_first_direct_byte); + break; + + default: + reiserfs_panic ("get_set_sd_field: unknown field of old stat data"); + } + } else { + struct stat_data * sd_v2 = sd; + + switch (field) { + case GET_SD_MODE: + if (set) + sd_v2->sd_mode = cpu_to_le16 (*(__u16 *)value); + else + *(__u16 *)value = le16_to_cpu (sd_v2->sd_mode); + break; + + case GET_SD_SIZE: + if (set) + sd_v2->sd_size = cpu_to_le64 (*(__u64 *)value); + else + *(__u64 *)value = le64_to_cpu (sd_v2->sd_size); + break; + + case GET_SD_BLOCKS: + if (set) + sd_v2->sd_blocks = cpu_to_le32 (*(__u32 *)value); + else + *(__u32 *)value = le32_to_cpu (sd_v2->sd_blocks); + break; + + case GET_SD_NLINK: + if (set) + sd_v2->sd_nlink = cpu_to_le32 (*(__u32 *)value); + else + *(__u32 *)value = le32_to_cpu (sd_v2->sd_nlink); + break; + + case GET_SD_FIRST_DIRECT_BYTE: + default: + reiserfs_panic ("get_set_sd_field: unknown field of new stat data"); + } + } +} + + + +/* *size is "real" file size, sd_size - size from stat data */ +static int wrong_st_size (struct key * key, loff_t max_file_size, int blocksize, + __u64 * size, __u64 sd_size, int is_dir) +{ + if (sd_size <= max_file_size) { + if (sd_size == *size) + return 0; + + if (is_dir) { + /* directory size must match to the sum of length of its entries */ + fsck_log ("dir %K has wrong sd_size %Ld, has to be %Ld\n", + key, sd_size, *size); + return 1; + } + + if (sd_size > *size) { + /* size in stat data can be bigger than size calculated by items */ + if (fsck_fix_non_critical (fs)) { + /* but it -o is given - fix that */ + fsck_log ("file %K has too big file size sd_size %Ld - fixed to %Ld\n", + key, sd_size, *size); + stats(fs)->fixed_sizes ++; + return 1; + } + *size = sd_size; + return 0; + } + + if (!(*size % blocksize)) { + /* last item is indirect */ + if (((sd_size & ~(blocksize - 1)) == (*size - blocksize)) && sd_size % blocksize) { + /* size in stat data is correct */ + *size = sd_size; + return 0; + } + } else { + /* last item is a direct one */ + if (!(*size % 8)) { + if (((sd_size & ~7) == (*size - 8)) && sd_size % 8) { + /* size in stat data is correct */ + *size = sd_size; + return 0; + } + } + } + } + + fsck_log ("file %K has wrong sd_size %Ld, has to be %Ld\n", + key, sd_size, *size); + stats(fs)->fixed_sizes ++; + return 1; +} + + +/* sd_blocks is 32 bit only */ +static int wrong_st_blocks (struct key * key, __u32 blocks, __u32 sd_blocks, int is_dir) +{ + if (blocks == sd_blocks) + return 0; + + if (!is_dir || blocks != _ROUND_UP (sd_blocks, fs->s_blocksize / 512)) { + /*fsck_log ("%s %K has wrong sd_blocks %d, has to be %d\n", + is_dir ? "dir" : "file", key, sd_blocks, blocks);*/ + return 1; + } else + return 0; +} + + +/* only regular files and symlinks may have items but stat + data. Symlink shold have body */ +static int wrong_mode (struct key * key, mode_t * mode, __u64 real_size) +{ + if (!fsck_fix_non_critical (fs)) + return 0; + + if (ftypelet (*mode) != '?') { + /* mode looks reasonable */ + if (S_ISREG (*mode) || S_ISLNK (*mode)) + return 0; + + /* device, pipe, socket have no items */ + if (!real_size) + return 0 ; + } + /* there are items, so change file mode to regular file. Otherwise + - file bodies do not get deleted */ + fsck_log ("file %K (%M) has body, mode fixed to %M\n", + key, *mode, (S_IFREG | 0600)); + *mode = (S_IFREG | 0600); + return 1; +} + + +/* key is a key of last file item */ +static int wrong_first_direct_byte (struct key * key, int blocksize, + __u32 * first_direct_byte, + __u32 sd_first_direct_byte, __u32 size) +{ + if (!size || is_indirect_key (key)) { + /* there is no direct item */ + *first_direct_byte = NO_BYTES_IN_DIRECT_ITEM; + if (sd_first_direct_byte != NO_BYTES_IN_DIRECT_ITEM) { + return 1; + } + return 0; + } + + /* there is direct item */ + *first_direct_byte = (get_offset (key) & ~(blocksize - 1)) + 1; + if (*first_direct_byte != sd_first_direct_byte) { + fsck_log ("file %k has wrong first direct byte %d, has to be %d\n", + key, sd_first_direct_byte, *first_direct_byte); + return 1; + } + return 0; +} + + +/* return values for check_regular_file and check_semantic_tree */ +#define OK 0 +#define STAT_DATA_NOT_FOUND -1 +#define DIRECTORY_HAS_NO_ITEMS -2 +#define RELOCATED -3 + + + +/* delete all items (but directory ones) with the same key 'ih' has + (including stat data of not a directory) and put them back at the + other place */ +void relocate_dir (struct item_head * ih, int change_ih) +{ + struct key key; + struct key * rkey; + struct path path; + struct item_head * path_ih; + struct si * si; + __u32 new_objectid; + + + /* starting with the leftmost one - look for all items of file, + store them and delete */ + key = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA); + + si = 0; + while (1) { + usearch_by_key (fs, &key, &path); + if (get_item_pos (&path) == B_NR_ITEMS (get_bh (&path))) { + rkey = uget_rkey (&path); + if (rkey && !not_of_one_file (&key, rkey)) { + /* file continues in the right neighbor */ + key = *rkey; + pathrelse (&path); + continue; + } + /* there is no more items of a directory */ + pathrelse (&path); + break; + } + + path_ih = get_ih (&path); + if (not_of_one_file (&key, &(path_ih->ih_key))) { + /* there are no more item with this key */ + pathrelse (&path); + break; + } + + /* ok, item found, but make sure that it is not a directory one */ + if ((is_stat_data_ih (path_ih) && not_a_directory (get_item (&path))) || + is_direct_ih (path_ih) || is_indirect_ih (path_ih)) { + /* item of not a directory found. Leave it in the + tree. FIXME: should not happen */ + key = path_ih->ih_key; + set_offset (KEY_FORMAT_1, &key, get_offset (&key) + 1); + pathrelse (&path); + continue; + } + + /* directory stat data ro directory item */ + si = save_and_delete_file_item (si, &path); + } + + + if (!si) { + fsck_progress ("relocate_dir: no directory %K items found\n", &key); + return; + } + + + /* get new objectid for relocation or get objectid with which file + was relocated already */ + new_objectid = objectid_for_relocation (&ih->ih_key); + ih->ih_key.k_objectid = new_objectid; + + /* put all items removed back into tree */ + while (si) { + fsck_log ("relocate_dir: move %H to ", &si->si_ih); + si->si_ih.ih_key.k_objectid = new_objectid; + fsck_log ("%H\n", &si->si_ih); + if (get_offset (&(si->si_ih.ih_key)) == DOT_OFFSET) { + /* fix "." entry to point to a directtory properly */ + struct reiserfs_de_head * deh; + + deh = (struct reiserfs_de_head *)si->si_dnm_data; + deh->deh_objectid = new_objectid; + } + insert_item_separately (&(si->si_ih), si->si_dnm_data, 1/*was in tree*/); + si = remove_saved_item (si); + } +} + + + +/* path is path to stat data. If file will be relocated - new_ih will contain + a key file was relocated with */ +int rebuild_check_regular_file (struct path * path, void * sd, + struct item_head * new_ih) +{ + int is_new_file; + struct key key, sd_key; + mode_t mode; + __u32 nlink; + __u64 real_size, saved_size; + __u32 blocks, saved_blocks; /* proper values and value in stat data */ + __u32 first_direct_byte, saved_first_direct_byte; + + struct buffer_head * bh; + struct item_head * ih; + int fix_sd; + int symlnk = 0; + int retval; + + retval = OK; + + /* stat data of a file */ + ih = get_ih (path); + bh = get_bh (path); + + if (new_ih) { + /* this objectid is used already */ + *new_ih = *ih; + pathrelse (path); + relocate_file (new_ih, 1); + stats(fs)->oid_sharing_files_relocated ++; + retval = RELOCATED; + if (usearch_by_key (fs, &(new_ih->ih_key), path) == ITEM_NOT_FOUND) + reiserfs_panic ("rebuild_check_regular_file: could not find stat data of relocated file"); + /* stat data is marked unreachable again due to relocation, fix that */ + ih = get_ih (path); + bh = get_bh (path); + mark_item_reachable (ih, bh); + sd = get_item (path); + } + + /* check and set nlink first */ + get_sd_nlink (ih, sd, &nlink); + nlink ++; + set_sd_nlink (ih, sd, &nlink); + mark_buffer_dirty (bh); + + if (nlink > 1) + return OK; + + /* firts name of a file found */ + if (ih_item_len (ih) == SD_SIZE) + is_new_file = 1; + else + is_new_file = 0; + + get_sd_mode (ih, sd, &mode); + get_sd_size (ih, sd, &saved_size); + get_sd_blocks (ih, sd, &saved_blocks); + if (!is_new_file) + get_sd_first_direct_byte (ih, sd, &saved_first_direct_byte); + + /* we met this file first time */ + if (S_ISREG (mode)) { + stats(fs)->regular_files ++; + } else if (S_ISLNK (mode)) { + symlnk = 1; + stats(fs)->symlinks ++; + } else { + stats(fs)->others ++; + } + + + key = ih->ih_key; /*??*/ + sd_key = key; /*??*/ + pathrelse (path); + + if (are_file_items_correct (&key, is_new_file ? KEY_FORMAT_2 : KEY_FORMAT_1, + &real_size, &blocks, 1/*mark items reachable*/, + symlnk, saved_size) != 1) { + /* unpassed items will be deleted in pass 4 as they left unaccessed */ + stats(fs)->broken_files ++; + } + + fix_sd = 0; + + fix_sd += wrong_mode (&sd_key, &mode, real_size); + if (!is_new_file) + fix_sd += wrong_first_direct_byte (&key, fs->s_blocksize, + &first_direct_byte, saved_first_direct_byte, real_size); + fix_sd += wrong_st_size (&sd_key, is_new_file ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, + fs->s_blocksize, &real_size, saved_size, 0/*not dir*/); + if (!is_new_file && (S_ISREG (mode) || S_ISLNK (mode))) + /* old stat data shares sd_block and sd_dev. We do not want to wipe + put sd_dev for device files */ + fix_sd += wrong_st_blocks (&sd_key, blocks, saved_blocks, 0/*not dir*/); + + if (fix_sd) { + /* find stat data and correct it */ + if (usearch_by_key (fs, &sd_key, path) != ITEM_FOUND) + die ("check_regular_file: stat data not found"); + + bh = get_bh (path); + ih = get_ih (path); + sd = get_item (path); + set_sd_size (ih, sd, &real_size); + set_sd_blocks (ih, sd, &blocks); + set_sd_mode (ih, sd, &mode); + if (!is_new_file) + set_sd_first_direct_byte (ih, sd, &first_direct_byte); + mark_buffer_dirty (bh); + } + + return retval; +} + + +static int is_rootdir_key (struct key * key) +{ + if (comp_keys (key, &root_dir_key)) + return 0; + return 1; +} + + +/* returns buffer, containing found directory item.*/ +static char * get_next_directory_item (struct key * key, /* on return this + will contain key + of next item in + the tree */ + struct key * parent, + struct item_head * ih,/*not in tree*/ + int * pos_in_item) +{ + INITIALIZE_PATH (path); + char * dir_item; + struct key * rdkey; + struct buffer_head * bh; + struct reiserfs_de_head * deh; + int i; + int retval; + + + if ((retval = usearch_by_entry_key (fs, key, &path)) != POSITION_FOUND) { + die ("get_next_directory_item: %k is not found", key); + } +#if 0 + if (get_offset (key) != DOT_OFFSET) + /* we always search for existing key, but "." */ + die ("get_next_directory_item: %k is not found", key); + + pathrelse (&path); + + if (fsck_mode (fs) == FSCK_CHECK) { + fsck_log ("get_next_directory_item: directory has no \".\" entry %k\n", + key); + pathrelse (&path); + return 0; + } + + fsck_log ("making \".\" and/or \"..\" for %K\n", key); + reiserfs_add_entry (fs, key, ".", key, 1 << IH_Unreachable); + reiserfs_add_entry (fs, key, "..", parent, 1 << IH_Unreachable); + + + /* we have fixed a directory, search its first item again */ + usearch_by_entry_key (fs, key, &path); + } +#endif + + /* leaf containing directory item */ + bh = PATH_PLAST_BUFFER (&path); + *pos_in_item = path.pos_in_item; + *ih = *get_ih (&path); + deh = B_I_DEH (bh, ih); + + /* make sure, that ".." exists as well */ + if (get_offset (key) == DOT_OFFSET) { + if (ih_entry_count (ih) < 2) { + fsck_progress ("1. Does this ever happen?\n"); + pathrelse (&path); + return 0; + } + if (name_length (ih, deh + 1, 1) != 2 || + strncmp (name_in_entry (deh + 1, 1), "..", 2)) { + fsck_progress ("2. Does this ever happen?\n"); + fsck_log ("get_next_directory_item: \"..\" not found in %H\n", ih); + pathrelse (&path); + return 0; + } + } + + /* mark hidden entries as visible, set "." and ".." correctly */ + deh += *pos_in_item; + for (i = *pos_in_item; i < ih_entry_count (ih); i ++, deh ++) { + int namelen; + char * name; + + name = name_in_entry (deh, i); + namelen = name_length (ih, deh, i); + if (de_hidden (deh)) + reiserfs_panic ("get_next_directory_item: item %k: hidden entry %d \'%.*s\'\n", + key, i, namelen, name); + + if (deh->deh_offset == DOT_OFFSET) { + if (not_of_one_file (&(deh->deh_dir_id), key)) + //deh->deh_objectid != REISERFS_ROOT_PARENT_OBJECTID)/*????*/ { + reiserfs_panic ("get_next_directory_item: wrong \".\" found %k\n", key); + } + + if (deh->deh_offset == DOT_DOT_OFFSET) { + /* set ".." so that it points to the correct parent directory */ + if (comp_short_keys (&(deh->deh_dir_id), parent) && + deh->deh_objectid != REISERFS_ROOT_PARENT_OBJECTID)/*???*/ { + /* FIXME */ + fsck_log ("get_next_directory_item: %k: \"..\" pointes to [%K], " + "should point to [%K]", + key, (struct key *)(&(deh->deh_dir_id)), parent); + if (fsck_mode (fs) == FSCK_REBUILD) { + deh->deh_dir_id = parent->k_dir_id; + deh->deh_objectid = parent->k_objectid; + mark_buffer_dirty (bh); + fsck_log (" - fixed\n"); + } else + fsck_log ("\n"); + + } + } + } + + /* copy directory item to the temporary buffer */ + dir_item = getmem (ih_item_len (ih)); + memcpy (dir_item, B_I_PITEM (bh, ih), ih_item_len (ih)); + + + /* next item key */ + if (PATH_LAST_POSITION (&path) == (B_NR_ITEMS (bh) - 1) && + (rdkey = uget_rkey (&path))) + copy_key (key, rdkey); + else { + key->k_dir_id = 0; + key->k_objectid = 0; + } + + if (fsck_mode (fs) != FSCK_CHECK) + mark_item_reachable (get_ih (&path), bh); + pathrelse (&path); + + return dir_item; +} + + +// get key of an object pointed by direntry and the key of the entry itself +static void get_object_key (struct reiserfs_de_head * deh, struct key * key, + struct key * entry_key, struct item_head * ih) +{ + key->k_dir_id = deh->deh_dir_id; + key->k_objectid = deh->deh_objectid; + key->u.k_offset_v1.k_offset = SD_OFFSET; + key->u.k_offset_v1.k_uniqueness = V1_SD_UNIQUENESS; + + entry_key->k_dir_id = ih->ih_key.k_dir_id; + entry_key->k_objectid = ih->ih_key.k_objectid; + entry_key->u.k_offset_v1.k_offset = deh->deh_offset; + entry_key->u.k_offset_v1.k_uniqueness = DIRENTRY_UNIQUENESS; +} + + +/* check recursively the semantic tree. Returns OK if entry points to good + object, STAT_DATA_NOT_FOUND if stat data was not found or RELOCATED when + file was relocated because its objectid was already marked as used by + another file */ +int rebuild_semantic_pass (struct key * key, struct key * parent, int dot_dot, + struct item_head * new_ih) +{ + struct path path; + void * sd; + int is_new_dir; + __u32 nlink; + struct buffer_head * bh; + struct item_head * ih; + int retval, retval1; + char * dir_item; + int pos_in_item; + struct item_head tmp_ih; + struct key item_key, entry_key, object_key; + __u64 dir_size; + __u32 blocks; + __u64 saved_size; + __u32 saved_blocks; + int fix_sd; + int relocate; + + + retval = OK; + + start_again: /* when directory was relocated */ + + if (!KEY_IS_STAT_DATA_KEY (key)) + reiserfs_panic ("rebuild_semantic_pass: key %k must be key of a stat data", + key); + + /* look for stat data of an object */ + if (usearch_by_key (fs, key, &path) == ITEM_NOT_FOUND) { + pathrelse (&path); + if (is_rootdir_key (key)) + /* root directory has to exist at this point */ + reiserfs_panic ("rebuild_semantic_pass: root directory not found"); + + return STAT_DATA_NOT_FOUND; + } + + + /* stat data has been found */ + bh = get_bh (&path); + ih = get_ih (&path); + sd = get_item(&path); + + /* */ + get_sd_nlink (ih, sd, &nlink); + + relocate = 0; + if (!nlink) { + /* we reached the stat data for the first time */ + if (is_objectid_really_used (semantic_id_map (fs), ih->ih_key.k_objectid, &pos_in_item)) { + /* calculate number of found files/dirs who are using objectid + which is used by another file */ + stats(fs)->oid_sharing ++; + if (fsck_fix_non_critical (fs)) + /* this works for files only */ + relocate = 1; + } else + mark_objectid_really_used (semantic_id_map (fs), ih->ih_key.k_objectid); + + mark_item_reachable (ih, bh); + } + + + if (not_a_directory (sd)) { + retval = rebuild_check_regular_file (&path, sd, relocate ? new_ih : 0); + pathrelse (&path); + return retval; + } + + if (relocate) { + if (!new_ih) + reiserfs_panic ("rebuild_semantic_pass: can not relocate %K", + &ih->ih_key); + *new_ih = *ih; + pathrelse (&path); + stats(fs)->oid_sharing_dirs_relocated ++; + relocate_dir (new_ih, 1); + *key = new_ih->ih_key; + retval = RELOCATED; + goto start_again; + } + + /* stat data of a directory found */ + if (nlink) { + /* we saw this directory already */ + if (!dot_dot) { + /* this name is not ".." - and hard links are not allowed on + directories */ + pathrelse (&path); + return STAT_DATA_NOT_FOUND; + } else { + /* ".." found */ + nlink ++; + set_sd_nlink (ih, sd, &nlink); + mark_buffer_dirty (bh); + pathrelse (&path); + return OK; + } + } + + + /* we see the directory first time */ + stats(fs)->directories ++; + nlink = 2; + if (key->k_objectid == REISERFS_ROOT_OBJECTID) + nlink ++; + set_sd_nlink (ih, sd, &nlink); + mark_buffer_dirty (bh); + + if (ih_item_len (ih) == SD_SIZE) + is_new_dir = 1; + else + is_new_dir = 0; + + /* release path pointing to stat data */ + pathrelse (&path); + + + /* make sure that "." and ".." exist */ + reiserfs_add_entry (fs, key, ".", key, 1 << IH_Unreachable); + reiserfs_add_entry (fs, key, "..", parent, 1 << IH_Unreachable); + + item_key = *key; + item_key.u.k_offset_v1.k_offset = DOT_OFFSET; + item_key.u.k_offset_v1.k_uniqueness = DIRENTRY_UNIQUENESS; + + /* save stat data's size and st_blocks */ + get_sd_size (ih, sd, &saved_size); + get_sd_blocks (ih, sd, &saved_blocks); + + dir_size = 0; + while ((dir_item = get_next_directory_item (&item_key, parent, &tmp_ih, &pos_in_item)) != 0) { + /* dir_item is copy of the item in separately allocated memory, + item_key is a key of next item in the tree */ + int i; + struct reiserfs_de_head * deh = (struct reiserfs_de_head *)dir_item + pos_in_item; + + + for (i = pos_in_item; i < ih_entry_count (&tmp_ih); i ++, deh ++) { + char * name; + int namelen; + struct item_head relocated_ih; + + name = name_in_entry (deh, i); + namelen = name_length (&tmp_ih, deh, i); + + if (is_dot (name, namelen)) { + dir_size += DEH_SIZE + entry_length (&tmp_ih, deh, i); + continue; + } + + print_name (name, namelen); + + if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) + reiserfs_panic ("rebuild_semantic_pass: name has to be hashed properly"); + + get_object_key (deh, &object_key, &entry_key, &tmp_ih); + + retval1 = rebuild_semantic_pass (&object_key, key, is_dot_dot (name, namelen), &relocated_ih); + + erase_name (namelen); + + switch (retval1) { + case OK: + dir_size += DEH_SIZE + entry_length (&tmp_ih, deh, i); + break; + + case STAT_DATA_NOT_FOUND: + case DIRECTORY_HAS_NO_ITEMS: + if (get_offset (&entry_key) == DOT_DOT_OFFSET && object_key.k_objectid == REISERFS_ROOT_PARENT_OBJECTID) { + /* ".." of root directory can not be found */ + dir_size += DEH_SIZE + entry_length (&tmp_ih, deh, i); + continue; + } + fsck_log ("name \"%.*s\" in directory %K points to nowhere %K - removed\n", + namelen, name, &tmp_ih.ih_key, (struct key *)&(deh->deh_dir_id)); + reiserfs_remove_entry (fs, &entry_key); + stats(fs)->deleted_entries ++; + break; + + case RELOCATED: + /* file was relocated, update key in corresponding directory entry */ + if (_search_by_entry_key (fs, &entry_key, &path) != POSITION_FOUND) { + fsck_progress ("could not find name of relocated file\n"); + } else { + /* update key dir entry points to */ + struct reiserfs_de_head * tmp_deh; + + tmp_deh = B_I_DEH (get_bh (&path), get_ih (&path)) + path.pos_in_item; + fsck_log ("name \"%.*s\" of dir %K pointing to %K updated to point to ", + namelen, name, &tmp_ih.ih_key, &tmp_deh->deh_dir_id); + tmp_deh->deh_dir_id = cpu_to_le32 (relocated_ih.ih_key.k_dir_id); + tmp_deh->deh_objectid = cpu_to_le32 (relocated_ih.ih_key.k_objectid); + fsck_log ("%K\n", &tmp_deh->deh_dir_id); + mark_buffer_dirty (get_bh (&path)); + } + dir_size += DEH_SIZE + entry_length (&tmp_ih, deh, i); + pathrelse (&path); + break; + } + } /* for */ + + freemem (dir_item); + + if (not_of_one_file (&item_key, key)) + /* next key is not of this directory */ + break; + + } /* while (dir_item) */ + + + if (dir_size == 0) + /* FIXME: is it possible? */ + return DIRECTORY_HAS_NO_ITEMS; + + /* calc correct value of sd_blocks field of stat data */ + blocks = dir_size2st_blocks (fs->s_blocksize, dir_size); + + fix_sd = 0; + fix_sd += wrong_st_blocks (key, blocks, saved_blocks, 1/*dir*/); + fix_sd += wrong_st_size (key, is_new_dir ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, + fs->s_blocksize, &dir_size, saved_size, 1/*dir*/); + + if (fix_sd) { + /* we have to fix either sd_size or sd_blocks, so look for stat data again */ + if (usearch_by_key (fs, key, &path) != ITEM_FOUND) + die ("rebuild_semantic_pass: stat data not found"); + + bh = get_bh (&path); + ih = get_ih (&path); + sd = get_item (&path); + + set_sd_size (ih, sd, &dir_size); + set_sd_blocks (ih, sd, &blocks); + mark_buffer_dirty (bh); + pathrelse (&path); + } + + return retval; +} + + +int is_dot (char * name, int namelen) +{ + return (namelen == 1 && name[0] == '.') ? 1 : 0; +} + + +int is_dot_dot (char * name, int namelen) +{ + return (namelen == 2 && name[0] == '.' && name[1] == '.') ? 1 : 0; +} + + +int not_a_directory (void * sd) +{ + /* mode is at the same place and of the same size in both stat + datas (v1 and v2) */ + struct stat_data_v1 * sd_v1 = sd; + + return !(S_ISDIR (le16_to_cpu (sd_v1->sd_mode))); +} + + + + +void zero_nlink (struct item_head * ih, void * sd) +{ + int zero = 0; + + if (ih_item_len (ih) == SD_V1_SIZE && ih_key_format (ih) != KEY_FORMAT_1) { + fsck_log ("zero_nlink: %H had wrong keys format %d, fixed to %d", + ih, ih_key_format (ih), KEY_FORMAT_1); + set_key_format (ih, KEY_FORMAT_1); + } + if (ih_item_len (ih) == SD_SIZE && ih_key_format (ih) != KEY_FORMAT_2) { + fsck_log ("zero_nlink: %H had wrong keys format %d, fixed to %d", + ih, ih_key_format (ih), KEY_FORMAT_2); + set_key_format (ih, KEY_FORMAT_2); + } + + set_sd_nlink (ih, sd, &zero); +} + + +/* inserts new or old stat data of a directory (unreachable, nlinks == 0) */ +void create_dir_sd (reiserfs_filsys_t fs, + struct path * path, struct key * key) +{ + struct item_head ih; + struct stat_data sd; + int key_format; + + if (SB_VERSION(fs) == REISERFS_VERSION_2) + key_format = KEY_FORMAT_2; + else + key_format = KEY_FORMAT_1; + + make_dir_stat_data (fs->s_blocksize, key_format, key->k_dir_id, + key->k_objectid, &ih, &sd); + + /* set nlink count to 0 and make the item unreachable */ + zero_nlink (&ih, &sd); + mark_item_unreachable (&ih); + + reiserfs_insert_item (fs, path, &ih, &sd); +} + + +static void make_sure_root_dir_exists (reiserfs_filsys_t fs) +{ + INITIALIZE_PATH (path); + + /* is there root's stat data */ + if (usearch_by_key (fs, &root_dir_key, &path) == ITEM_NOT_FOUND) { + create_dir_sd (fs, &path, &root_dir_key); + mark_objectid_really_used (proper_id_map (fs), REISERFS_ROOT_OBJECTID); + } else + pathrelse (&path); + + /* add "." and ".." if any of them do not exist. Last two + parameters say: 0 - entry is not added on lost_found pass and 1 + - mark item unreachable */ + reiserfs_add_entry (fs, &root_dir_key, ".", &root_dir_key, + 1 << IH_Unreachable); + reiserfs_add_entry (fs, &root_dir_key, "..", &parent_root_dir_key, + 1 << IH_Unreachable); +} + + +/* mkreiserfs should have created this */ +static void make_sure_lost_found_exists (reiserfs_filsys_t fs) +{ + int retval; + INITIALIZE_PATH (path); + int gen_counter; + + /* look for "lost+found" in the root directory */ + lost_found_dir_key.k_objectid = reiserfs_find_entry (fs, &root_dir_key, + "lost+found", &gen_counter); + if (!lost_found_dir_key.k_objectid) { + lost_found_dir_key.k_objectid = get_unused_objectid (fs); + if (!lost_found_dir_key.k_objectid) { + fsck_progress ("make_sure_lost_found_exists: could not get objectid" + " for \"/lost+found\", will not link lost files\n"); + return; + } + } + + /* look for stat data of "lost+found" */ + retval = usearch_by_key (fs, &lost_found_dir_key, &path); + if (retval == ITEM_NOT_FOUND) + create_dir_sd (fs, &path, &lost_found_dir_key); + else { + if (not_a_directory (get_item (&path))) { + fsck_progress ("make_sure_lost_found_exists: \"/lost+found\" is " + "not a directory, will not link lost files\n"); + lost_found_dir_key.k_objectid = 0; + pathrelse (&path); + return; + } + pathrelse (&path); + } + + /* add "." and ".." if any of them do not exist */ + reiserfs_add_entry (fs, &lost_found_dir_key, ".", &lost_found_dir_key, + 1 << IH_Unreachable); + reiserfs_add_entry (fs, &lost_found_dir_key, "..", &root_dir_key, + 1 << IH_Unreachable); + + reiserfs_add_entry (fs, &root_dir_key, "lost+found", &lost_found_dir_key, + 1 << IH_Unreachable); + + return; +} + + +/* this is part of rebuild tree */ +void pass_3_semantic (void) +{ + fsck_progress ("Pass 3 (semantic):\n"); + + /* when warnings go not to stderr - separate then in the log */ + if (fsck_log_file (fs) != stderr) + fsck_log ("####### Pass 3 #########\n"); + + if (!fs->s_hash_function) + reiserfs_panic ("Hash function should be selected already"); + + make_sure_root_dir_exists (fs); + make_sure_lost_found_exists (fs); + + /* link all relocated files into root directory */ + link_relocated_files (); + + rebuild_semantic_pass (&root_dir_key, &parent_root_dir_key, 0/*!dot_dot*/, 0/*reloc_ih*/); + stage_report (3, fs); + +} + + +/* path is path to stat data. If file will be relocated - new_ih will contain + a key file was relocated with */ +static int check_check_regular_file (struct path * path, void * sd) +{ + int is_new_file; + struct key key, sd_key; + mode_t mode; + __u32 nlink; + __u64 real_size, saved_size; + __u32 blocks, saved_blocks; /* proper values and value in stat data */ + __u32 first_direct_byte, saved_first_direct_byte; + + struct buffer_head * bh; + struct item_head * ih; + int fix_sd; + int symlnk = 0; + + + ih = get_ih (path); + bh = get_bh (path); + + if (ih_item_len (ih) == SD_SIZE) + is_new_file = 1; + else + is_new_file = 0; + + + get_sd_nlink (ih, sd, &nlink); + get_sd_mode (ih, sd, &mode); + get_sd_size (ih, sd, &saved_size); + get_sd_blocks (ih, sd, &saved_blocks); + if (!is_new_file) + get_sd_first_direct_byte (ih, sd, &saved_first_direct_byte); + + if (S_ISREG (mode)) { + /* fixme: this could be wrong due to hard links */ + stats(fs)->regular_files ++; + } else if (S_ISLNK (mode)) { + symlnk = 1; + stats(fs)->symlinks ++; + } else { + stats(fs)->others ++; + } + + + key = ih->ih_key; /*??*/ + sd_key = key; /*??*/ + pathrelse (path); + + if (are_file_items_correct (&key, is_new_file ? KEY_FORMAT_2 : KEY_FORMAT_1, + &real_size, &blocks, 0/*do not mark items reachable*/, + symlnk, saved_size) != 1) { + fsck_log ("check_regular_file: broken file found %K\n", key); + } else { + fix_sd = 0; + + fix_sd += wrong_mode (&sd_key, &mode, real_size); + if (!is_new_file) + fix_sd += wrong_first_direct_byte (&key, fs->s_blocksize, + &first_direct_byte, saved_first_direct_byte, real_size); + fix_sd += wrong_st_size (&sd_key, is_new_file ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, + fs->s_blocksize, &real_size, saved_size, 0/*not dir*/); + if (!is_new_file && (S_ISREG (mode) || S_ISLNK (mode))) + /* old stat data shares sd_block and sd_dev. We do not want to wipe + put sd_dev for device files */ + fix_sd += wrong_st_blocks (&sd_key, blocks, saved_blocks, 0/*not dir*/); + + if (fix_sd && fsck_fix_fixable (fs)) { + /* find stat data and correct it */ + if (usearch_by_key (fs, &sd_key, path) != ITEM_FOUND) + die ("check_regular_file: stat data not found"); + + bh = get_bh (path); + ih = get_ih (path); + sd = get_item (path); + set_sd_size (ih, sd, &real_size); + set_sd_blocks (ih, sd, &blocks); + set_sd_mode (ih, sd, &mode); + if (!is_new_file) + set_sd_first_direct_byte (ih, sd, &first_direct_byte); + mark_buffer_dirty (bh); + } + } + return OK; +} + + +/* semantic pass of --check */ +static int check_semantic_pass (struct key * key, struct key * parent) +{ + struct path path; + void * sd; + int is_new_dir; + struct buffer_head * bh; + struct item_head * ih; + int retval; + char * dir_item; + int pos_in_item; + struct item_head tmp_ih; + struct key next_item_key, entry_key, object_key; + __u64 dir_size = 0; + __u32 blocks; + __u64 saved_size; + __u32 saved_blocks; + int fix_sd; + + + if (!KEY_IS_STAT_DATA_KEY (key)) + die ("check_semantic_pass: key must be key of a stat data"); + + /* look for stat data of an object */ + if (usearch_by_key (fs, key, &path) == ITEM_NOT_FOUND) { + pathrelse (&path); + return STAT_DATA_NOT_FOUND; + } + + /* stat data has been found */ + sd = get_item(&path); + + if (not_a_directory (sd)) { + retval = check_check_regular_file (&path, sd); + pathrelse (&path); + return retval; + } + + ih = get_ih (&path); + /* directory stat data found */ + if (ih_item_len (ih) == SD_SIZE) + is_new_dir = 1; + else + is_new_dir = 0; + + /* save stat data's size and st_blocks */ + get_sd_size (ih, sd, &saved_size); + get_sd_blocks (ih, sd, &saved_blocks); + + /* release path pointing to stat data */ + pathrelse (&path); + + stats(fs)->directories ++; + next_item_key = *key; + next_item_key.u.k_offset_v1.k_offset = DOT_OFFSET; + next_item_key.u.k_offset_v1.k_uniqueness = DIRENTRY_UNIQUENESS; + + + dir_size = 0; + while ((dir_item = get_next_directory_item (&next_item_key, parent, &tmp_ih, &pos_in_item)) != 0) { + /* dir_item is copy of the item in separately allocated memory, + item_key is a key of next item in the tree */ + int i; + struct reiserfs_de_head * deh = (struct reiserfs_de_head *)dir_item + pos_in_item; + + + for (i = pos_in_item; i < ih_entry_count (&tmp_ih); i ++, deh ++) { + char * name; + int namelen; + + name = name_in_entry (deh, i); + namelen = name_length (&tmp_ih, deh, i); + + print_name (name, namelen); + + if (!is_properly_hashed (fs, name, namelen, deh_offset (deh))) { + fsck_log ("check_semantic_pass: hash mismatch detected (%.*s)\n", namelen, name); + } + get_object_key (deh, &object_key, &entry_key, &tmp_ih); + + if (is_dot (name, namelen) || is_dot_dot (name, namelen)) { + /* do not go through "." and ".." */ + retval = OK; + } else { + add_path_key (&object_key); + retval = check_semantic_pass (&object_key, key); + del_path_key (); + } + + erase_name (namelen); + + /* check what check_semantic_tree returned */ + switch (retval) { + case OK: + dir_size += DEH_SIZE + entry_length (&tmp_ih, deh, i); + break; + + case STAT_DATA_NOT_FOUND: + fsck_log ("check_semantic_pass: name \"%.*s\" in directory %K points to nowhere", + namelen, name, &tmp_ih.ih_key); + if (fsck_fix_fixable (fs)) { + reiserfs_remove_entry (fs, &entry_key); + stats(fs)->deleted_entries ++; + fsck_log (" - removed"); + } + fsck_log ("\n"); + break; + + case DIRECTORY_HAS_NO_ITEMS: + fsck_log ("check_semantic_pass: name \"%.*s\" in directory %K points dir without body\n", + namelen, name, &tmp_ih.ih_key); + /* fixme: stat data should be deleted as well */ + /* + if (fsck_fix_fixable (fs)) { + reiserfs_remove_entry (fs, &entry_key); + stats(fs)->deleted_entries ++; + fsck_log (" - removed"); + } + fsck_log ("\n");*/ + break; + + case RELOCATED: + /* fixme: we could also relocate file */ + reiserfs_panic ("check_semantic_pass: relocation in check mode is not ready"); + } + } /* for */ + + freemem (dir_item); + + if (not_of_one_file (&next_item_key, key)) + /* next key is not of this directory */ + break; + + } /* while (dir_item) */ + + + if (dir_size == 0) + /* FIXME: is it possible? */ + return DIRECTORY_HAS_NO_ITEMS; + + /* calc correct value of sd_blocks field of stat data */ + blocks = dir_size2st_blocks (fs->s_blocksize, dir_size); + + fix_sd = 0; + fix_sd += wrong_st_blocks (key, blocks, saved_blocks, 1/*dir*/); + fix_sd += wrong_st_size (key, is_new_dir ? MAX_FILE_SIZE_V2 : MAX_FILE_SIZE_V1, + fs->s_blocksize, &dir_size, saved_size, 1/*dir*/); + + if (fix_sd && fsck_fix_fixable (fs)) { + /* we have to fix either sd_size or sd_blocks, so look for stat data again */ + if (usearch_by_key (fs, key, &path) != ITEM_FOUND) + die ("check_semantic_tree: stat data not found"); + + bh = get_bh (&path); + ih = get_ih (&path); + sd = get_item (&path); + + set_sd_size (ih, sd, &dir_size); + set_sd_blocks (ih, sd, &blocks); + mark_buffer_dirty (bh); + pathrelse (&path); + } + + return OK; +} + + +/* called when --check is given */ +void semantic_check (void) +{ + fsck_progress ("Checking Semantic tree..."); + + if (check_semantic_pass (&root_dir_key, &parent_root_dir_key) != OK) + die ("check_semantic_tree: no root directory found"); + + fsck_progress ("ok\n"); + +} + + + diff --git a/fsck/ubitmap.c b/fsck/ubitmap.c new file mode 100644 index 0000000..76e5b78 --- /dev/null +++ b/fsck/ubitmap.c @@ -0,0 +1,189 @@ +/* + * Copyright 1996-1999 Hans Reiser + */ + +#include "fsck.h" + +/* g_disk_bitmap initially contains copy of disk bitmaps + (cautious version of it); + + g_new_bitmap initially has marked only super block, bitmap blocks + and bits after the end of bitmap + + in pass 1 we go through g_disk_bitmap. + + If block does not look like formatted node, we skip it. + + If block contains internal node, put 0 in g_disk_bitmap if block is + not used in new tree yet. + + If block contains leaf and is used already (by an indirect item + handled already to this time) save all items. They will be inserted + into tree after pass 1. + + If block looking like leaf is not used in the new tree, try to + insert in into tree. If it is not possible, mark block in + g_uninsertable_leaf_bitmap. Blocks marked in this bitmap will be inserted into tree in pass 2. They can not be + + This means, that in pass 1 when we have + found block containing the internal nodes we mark it in + g_disk_bitmap as free (reiserfs_free_internal_block). When block + gets into new tree it is marked in g_new_bitmap (mark_block_used) + When collecting resources for do_balance, we mark new blocks with + mark_block_used. After do_balance we unmark unused new blocks in + g_new_bitmap (bitmap.c:/reiserfs_free_block) + + Allocating of new blocks: look for 0 bit in g_disk_bitmap + (find_zero_bit_in_bitmap), make sure, that g_new_bitmap contains 0 + at the corresponding bit (is_block_used). + + */ + + +int is_to_be_read (reiserfs_filsys_t fs, unsigned long block) +{ + return reiserfs_bitmap_test_bit (fsck_disk_bitmap (fs), block); +} + + +/* is blocks used (marked by 1 in new bitmap) in the tree which is being built + (as leaf, internal, bitmap, or unformatted node) */ +int is_block_used (unsigned long block) +{ + return reiserfs_bitmap_test_bit (fsck_new_bitmap (fs), block); +} + + +void mark_block_used (unsigned long block) +{ + if (!block) + return; + if (is_block_used (block)) + die ("mark_block_used: (%lu) used already", block); + + reiserfs_bitmap_set_bit (fsck_new_bitmap (fs), block); +} + + +void mark_block_free (unsigned long block) +{ + if (!is_block_used (block)) + die ("mark_block_used: (%lu) is free", block); + + reiserfs_bitmap_clear_bit (fsck_new_bitmap (fs), block); +} + + +int is_block_uninsertable (unsigned long block) +{ + return !reiserfs_bitmap_test_bit (uninsertable_leaf_bitmap, block); +} + + +/* uninsertable block is marked by bit clearing */ +void mark_block_uninsertable (unsigned long block) +{ + if (is_block_uninsertable (block)) + die ("mark_block_uninsertable: (%lu) is uninsertable already", block); + + reiserfs_bitmap_clear_bit (uninsertable_leaf_bitmap, block); +} + + +int reiserfsck_reiserfs_new_blocknrs (reiserfs_filsys_t fs, + unsigned long * free_blocknrs, + unsigned long start, int amount_needed) +{ + int i; + + if (!are_there_allocable_blocks (amount_needed)) + die ("out of disk space"); + for (i = 0; i < amount_needed; i ++) { + free_blocknrs[i] = alloc_block (); + if (!free_blocknrs[i]) + die ("reiserfs_new_blocknrs: 0 is allocated"); + mark_block_used (free_blocknrs[i]); + } + return CARRY_ON; +} + + +// FIXME: do you check readability of a block? If f_read fails - you +// free block in bitmap or if you mark bad blocks used to avoid their +// allocation in future you should have bad block counter in a super +// block. Another minor issue: users of _get_new_buffer expect buffer +// to be filled with 0s +struct buffer_head * reiserfsck_get_new_buffer (unsigned long start) +{ + unsigned long blocknr = 0; + struct buffer_head * bh = NULL; + + reiserfs_new_blocknrs (fs, &blocknr, start, 1); + bh = getblk (fs->s_dev, blocknr, fs->s_blocksize); + return bh; +} + + +/* free block in new bitmap */ +int reiserfsck_reiserfs_free_block (reiserfs_filsys_t fs, unsigned long block) +{ + mark_block_free (block); + + /* put it back to pool of blocks for allocation */ + make_allocable (block); + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fsck/ufile.c b/fsck/ufile.c new file mode 100644 index 0000000..7037c5a --- /dev/null +++ b/fsck/ufile.c @@ -0,0 +1,1069 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser + */ +#include "fsck.h" + + + + +static int do_items_have_the_same_type (struct item_head * ih, struct key * key) +{ + return (get_type (&ih->ih_key) == get_type (key)) ? 1 : 0; +} + +static int are_items_in_the_same_node (struct path * path) +{ + return (PATH_LAST_POSITION (path) < B_NR_ITEMS (PATH_PLAST_BUFFER (path)) - 1) ? 1 : 0; +} + + +/* FIXME: there is get_next_key in pass4.c */ +static struct key * get_next_key_2 (struct path * path) +{ + if (PATH_LAST_POSITION (path) < B_NR_ITEMS (get_bh (path)) - 1) + return B_N_PKEY (get_bh (path), PATH_LAST_POSITION (path) + 1); + return uget_rkey (path); +} + + +int do_make_tails () +{ + return 1;/*SB_MAKE_TAIL_FLAG (&g_sb) == MAKE_TAILS ? YES : NO;*/ +} + + +static void cut_last_unfm_pointer (struct path * path, struct item_head * ih) +{ + set_free_space(ih, 0); + if (I_UNFM_NUM (ih) == 1) + reiserfsck_delete_item (path, 0); + else + reiserfsck_cut_from_item (path, -UNFM_P_SIZE); +} + + +// we use this to convert symlinks back to direct items if they were +// direct2indirect converted on tree building +static unsigned long indirect_to_direct (struct path * path, __u64 *symlink_size) +{ + struct buffer_head * bh = PATH_PLAST_BUFFER (path); + struct item_head * ih = PATH_PITEM_HEAD (path); + unsigned long unfm_ptr; + struct buffer_head * unfm_bh = 0; + struct item_head ins_ih; + char * buf; + int len; + __u32 * indirect; + char bad_link[] = "broken_link"; + +/* add_event (INDIRECT_TO_DIRECT);*/ + + + /* direct item to insert */ + set_key_format (&ins_ih, ih_key_format (ih)); + ins_ih.ih_key.k_dir_id = ih->ih_key.k_dir_id; + ins_ih.ih_key.k_objectid = ih->ih_key.k_objectid; + set_type_and_offset (ih_key_format (ih), &ins_ih.ih_key, + get_offset (&ih->ih_key) + (I_UNFM_NUM (ih) - 1) * bh->b_size, TYPE_DIRECT); + + // we do not know what length this item should be + indirect = get_item (path); + unfm_ptr = le32_to_cpu (indirect [I_UNFM_NUM (ih) - 1]); + if (unfm_ptr && (unfm_bh = bread (bh->b_dev, unfm_ptr, bh->b_size))) { + buf = unfm_bh->b_data; + // get length of direct item + for (len = 0; buf[len] && len < bh->b_size; len ++); + } else { + fsck_log ("indirect_to_direct: could not read block %lu, " + "making (%K) bad link instead\n", unfm_ptr, &ih->ih_key); + buf = bad_link; + len = strlen (bad_link); + } + + if (len > MAX_DIRECT_ITEM_LEN (fs->s_blocksize)) { + fsck_log ("indirect_to_direct: symlink %K seems too long %d, " + "Cutting it down to %d byte\n", + &ih->ih_key, len, MAX_DIRECT_ITEM_LEN (fs->s_blocksize) - 8); + len = MAX_DIRECT_ITEM_LEN (fs->s_blocksize) - 8; + } + + if (!len) { + buf = bad_link; + len = strlen (bad_link); + } + + *symlink_size = len; + + ins_ih.ih_item_len = cpu_to_le16 ((ih_key_format (ih) == KEY_FORMAT_2) ? ROUND_UP(len) : len); + set_free_space (&ins_ih, MAX_US_INT); + + + // last last unformatted node pointer + path->pos_in_item = I_UNFM_NUM (ih) - 1; + cut_last_unfm_pointer (path, ih); + + /* insert direct item */ + if (usearch_by_key (fs, &(ins_ih.ih_key), path) == ITEM_FOUND) + die ("indirect_to_direct: key must be not found"); + reiserfsck_insert_item (path, &ins_ih, (const char *)(buf)); + + brelse (unfm_bh); + + /* put to stat data offset of first byte in direct item */ + return get_offset (&ins_ih.ih_key); //offset; +} + + +extern inline __u64 get_min_bytes_number (struct item_head * ih, int blocksize) +{ + switch (get_type (&ih->ih_key)) { + case TYPE_DIRECT: + if (SB_VERSION(fs) == REISERFS_VERSION_2) + return ROUND_UP(ih_item_len (ih) - 8); + else + return ih_item_len (ih); + case TYPE_INDIRECT: + return (I_UNFM_NUM(ih) - 1) * blocksize; + } + fsck_log ("get_min_bytes_number: called for wrong type of item %H\n", ih); + return 0; +} + + +/* returns 1 when file looks correct, -1 if directory items appeared + there, 0 - only holes in the file found */ +/* when it returns, key->k_offset is offset of the last item of file */ +int are_file_items_correct (struct key * key, int key_version, __u64 * size, + /*__u64 * min_size,*/ __u32 * blocks, + int mark_passed_items, int symlink, __u64 symlink_size) +{ + struct path path; + int retval, i; + struct item_head * ih; + struct key * next_key; + int had_direct = 0; + + set_offset (key_version, key, 1); + set_type (key_version, key, TYPE_DIRECT); + + *size = 0; + /* *min_size = 0;*/ + *blocks = 0; + + path.path_length = ILLEGAL_PATH_ELEMENT_OFFSET; + + do { + retval = usearch_by_position (fs, key, key_version, &path); + if (retval == POSITION_FOUND && path.pos_in_item != 0) + die ("are_file_items_correct: all bytes we look for must be found at position 0"); + + switch (retval) { + case POSITION_FOUND:/**/ + + ih = PATH_PITEM_HEAD (&path); + + set_type (ih_key_format (ih), key, get_type (&ih->ih_key)); + + if (mark_passed_items == 1) { + mark_item_reachable (ih, PATH_PLAST_BUFFER (&path)); + } + // does not change path + next_key = get_next_key_2 (&path); + + if (get_type (&ih->ih_key) == TYPE_INDIRECT) + { + if (symlink) + *blocks = 1; + else + for (i = 0; i < I_UNFM_NUM (ih); i ++) + { + __u32 * ind = (__u32 *)get_item(&path); + + if (ind[i] != 0) + *blocks += (fs->s_blocksize >> 9); + } + + }else if ((get_type (&ih->ih_key) == TYPE_DIRECT) && !(had_direct)) + { + if (symlink) + *blocks = (fs->s_blocksize >> 9); + else + *blocks += (fs->s_blocksize >> 9); + had_direct++; + } + + if (next_key == 0 || not_of_one_file (key, next_key) || + (!is_indirect_key (next_key) && !is_direct_key(next_key) ) ) + { + /* next item does not exists or is of another object, + therefore all items of file are correct */ + + /* *min_size = get_offset (key) + get_min_bytes_number (ih, fs->s_blocksize);*/ + *size = get_offset (key) + get_bytes_number (ih, fs->s_blocksize) - 1; + + + /* here is a problem: if file system being repaired was full + enough, then we should avoid indirect_to_direct + conversions. This is because unformatted node we have to + free will not get into pool of free blocks, but new direct + item is very likely of big size, therefore it may require + allocation of new blocks. So, skip it for now */ + if (symlink && is_indirect_ih (ih)) { +// struct key sd_key; + unsigned long first_direct_byte; + + if (fsck_mode (fs) == FSCK_CHECK) { + fsck_log ("are_file_items_correct: symlink found in indirect item %K\n", &ih->ih_key); + } else { + first_direct_byte = indirect_to_direct (&path, &symlink_size); + + /* last item of the file is direct item */ + set_offset (key_version, key, first_direct_byte); + set_type (key_version, key, TYPE_DIRECT); + *size = symlink_size; + } + } else + pathrelse (&path); + return 1; + } + + /* next item is item of this file */ + if ((is_indirect_ih (ih) && + (get_offset (&ih->ih_key) + fs->s_blocksize * I_UNFM_NUM (ih) != get_offset (next_key))) || + (is_direct_ih (ih) && + (get_offset (&ih->ih_key) + ih_item_len (ih) != get_offset (next_key)))) + { + /* next item has incorrect offset (hole or overlapping) */ + *size = get_offset (&ih->ih_key) + get_bytes_number (ih, fs->s_blocksize) - 1; + /**min_size = *size;*/ + pathrelse (&path); + return 0; + } + if (do_items_have_the_same_type (ih, next_key) == 1 && are_items_in_the_same_node (&path) == 1) + { + /* two indirect items or two direct items in the same leaf. FIXME: Second will be deleted */ + *size = get_offset (&ih->ih_key) + get_bytes_number (ih, fs->s_blocksize) - 1; + /**min_size = *size;*/ + pathrelse (&path); + return 0; + } + + /* items are of different types or are in different nodes */ + if (get_offset (&ih->ih_key) + get_bytes_number (ih, fs->s_blocksize) != get_offset (next_key)) + { + /* indirect item free space is not set properly */ + if (!is_indirect_ih (ih) ) //|| get_ih_free_space(ih) == 0) + fsck_log ("are_file_items_correct: " + "item must be indirect and must have invalid free space (%H)", ih); + + if (fsck_mode (fs) != FSCK_CHECK) + { + set_free_space(ih, 0); + mark_buffer_dirty (PATH_PLAST_BUFFER (&path)); + } + } + + /* next item exists */ + set_type_and_offset(key_version, key, get_offset (next_key), get_type(next_key)); + + if (comp_keys (key, next_key)) + die ("are_file_items_correct: keys do not match %k and %k", key, next_key); + pathrelse (&path); + break; + + case POSITION_NOT_FOUND: + // we always must have next key found. Exception is first + // byte. It does not have to exist + + if (get_offset (key) != 1) + die ("are_file_items_correct: key not found %byte can be not found only when it is first byte of file"); + pathrelse (&path); + return 0; + + case FILE_NOT_FOUND: + if (get_offset (key) != 1) + die ("are_file_items_correct: there is no items of this file, byte 0 found though"); + pathrelse (&path); + return 1; + + case DIRECTORY_FOUND: + pathrelse (&path); + return -1; + } + } while (1); + + die ("are_file_items_correct: code can not reach here"); + return 0; +} + + +/* delete all items and put them back (after that file should have + correct sequence of items.It is very similar to + pass2.c:relocate_file () and should_relocate () */ +static void rewrite_file (struct item_head * ih) +{ + struct key key; + struct key * rkey; + struct path path; + struct item_head * path_ih; + struct si * si; + + /* starting with the leftmost one - look for all items of file, + store and delete and */ + key = ih->ih_key; + set_type_and_offset (KEY_FORMAT_1, &key, SD_OFFSET, TYPE_STAT_DATA); + + si = 0; + while (1) { + usearch_by_key (fs, &key, &path); + if (get_item_pos (&path) == B_NR_ITEMS (get_bh (&path))) { + rkey = uget_rkey (&path); + if (rkey && !not_of_one_file (&key, rkey)) { + /* file continues in the right neighbor */ + copy_key (&key, rkey); + pathrelse (&path); + continue; + } + /* there is no more items with this key */ + pathrelse (&path); + break; + } + + path_ih = get_ih (&path); + if (not_of_one_file (&key, &(path_ih->ih_key))) { + /* there are no more item with this key */ + pathrelse (&path); + break; + } + + /* ok, item found, but make sure that it is not a directory one */ + if ((is_stat_data_ih (path_ih) && !not_a_directory (get_item (&path))) || + (is_direntry_ih (path_ih))) + reiserfs_panic ("rewrite_file: no directory items of %K are expected", + &key); + + si = save_and_delete_file_item (si, &path); + } + + /* put all items back into tree */ + while (si) { + insert_item_separately (&(si->si_ih), si->si_dnm_data, 1/*was in tree*/); + si = remove_saved_item (si); + } +} + + +/* file must have correct sequence of items and tail must be stored in + unformatted pointer */ +static int make_file_writeable (struct item_head * ih) +{ + struct key key; + __u64 size;/*, min_size;*/ + __u32 blocks; + int retval; + + copy_key (&key, &(ih->ih_key)); + + retval = are_file_items_correct (&key, ih_key_format (ih), &size,/* &min_size, */ + &blocks, 0/*do not mark accessed*/, 0, 0); + if (retval == 1) + /* file looks correct */ + return 1; + + rewrite_file (ih); + stats(fs)->rewritten ++; + +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + copy_key (&key, &(ih->ih_key)); + size = 0; + if (are_file_items_correct (&key, ih_key_format (ih), &size, &blocks, + 0/*do not mark accessed*/, 0, 0) == 0) { + fsck_progress ("file still incorrect %K\n", &key); + } +/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ + + return 1; +} + + +/* this inserts __first__ indirect item (having k_offset == 1 and only + one unfm pointer) into tree */ +static int create_first_item_of_file (struct item_head * ih, char * item, struct path * path, + int *pos_in_coming_item, int was_in_tree) +{ + __u32 unfm_ptr; + struct buffer_head * unbh; + struct item_head indih; + int retval; + __u32 free_sp = 0; + + if (get_offset (&ih->ih_key) > fs->s_blocksize) { + /* insert indirect item containing 0 unfm pointer */ + unfm_ptr = 0; + set_free_space (&indih, 0); + free_sp = 0; + retval = 0; + } else { + if (is_direct_ih (ih)) { + /* copy direct item to new unformatted node. Save information about it */ + //__u64 len = get_bytes_number(0, ih, item, CHECK_FREE_BYTES); + __u64 len = get_bytes_number (ih, fs->s_blocksize); + + unbh = reiserfsck_get_new_buffer (PATH_PLAST_BUFFER (path)->b_blocknr); + memset (unbh->b_data, 0, unbh->b_size); + unfm_ptr = cpu_to_le32 (unbh->b_blocknr); +/* this is for check only */ + /*mark_block_unformatted (le32_to_cpu (unfm_ptr));*/ + memcpy (unbh->b_data + get_offset (&ih->ih_key) - 1, item, len); + + save_unfm_overwriting (le32_to_cpu (unfm_ptr), ih); + + set_free_space (&indih, fs->s_blocksize - len - (get_offset (&ih->ih_key) - 1)); + free_sp = fs->s_blocksize - len - (get_offset (&ih->ih_key) - 1); + mark_buffer_dirty (unbh); +// mark_buffer_uptodate (unbh, 0); + mark_buffer_uptodate (unbh, 1); + brelse (unbh); + + retval = len; + } else { + /* take first unformatted pointer from an indirect item */ + unfm_ptr = cpu_to_le32 (*(__u32 *)item);/*B_I_POS_UNFM_POINTER (bh, ih, 0);*/ + if (!was_in_tree) { + if (still_bad_unfm_ptr_2 (unfm_ptr)) + die ("create_first_item_of_file: bad unfm pointer %d", unfm_ptr); + mark_block_used (unfm_ptr); + } + + //free_sp = ih_get_free_space(0, ih, item); + free_sp = ih_free_space (ih); + set_free_space (&indih, ((ih_item_len(ih) == UNFM_P_SIZE) ? free_sp /*get_ih_free_space(ih)*/ : 0)); + if (ih_item_len (ih) != UNFM_P_SIZE) + free_sp = 0; +// free_sp = ((ih->ih_item_len == UNFM_P_SIZE) ? ih->u.ih_free_space : 0); + retval = fs->s_blocksize - free_sp; + (*pos_in_coming_item) ++; + } + } + set_key_format (&indih, ih_key_format (ih)); + //ih_version(&indih) = ih_version(ih); + copy_key (&(indih.ih_key), &(ih->ih_key)); + set_offset (key_format (&(ih->ih_key)), &indih.ih_key, 1); + set_type (key_format (&(ih->ih_key)), &indih.ih_key, TYPE_INDIRECT); + + indih.ih_item_len = cpu_to_le16 (UNFM_P_SIZE); + mark_item_unreachable (&indih); + reiserfsck_insert_item (path, &indih, (const char *)&unfm_ptr); + + return retval; +} + + +/* path points to first part of tail. Function copies file tail into unformatted node and returns + its block number. If we are going to overwrite direct item then keep free space (keep_free_space + == YES). Else (we will append file) set free space to 0 */ +/* we convert direct item that is on the path to indirect. we need a number of free block for + unformatted node. reiserfs_new_blocknrs will start from block number returned by this function */ +static unsigned long block_to_start (struct path * path) +{ + struct buffer_head * bh; + struct item_head * ih; + + bh = PATH_PLAST_BUFFER (path); + ih = PATH_PITEM_HEAD (path); + if (get_offset(&ih->ih_key) == 1 || PATH_LAST_POSITION (path) == 0) + return bh->b_blocknr; + + ih --; + return (B_I_POS_UNFM_POINTER (bh, ih, I_UNFM_NUM (ih) - 1)) ?: bh->b_blocknr; +} + + +static void direct2indirect2 (unsigned long unfm, struct path * path, int keep_free_space) +{ + struct item_head * ih; + struct key key; + struct buffer_head * unbh; + struct unfm_nodeinfo ni; + int copied = 0; + + ih = PATH_PITEM_HEAD (path); + copy_key (&key, &(ih->ih_key)); + + if (get_offset (&key) % fs->s_blocksize != 1) { + /* look for first part of tail */ + pathrelse (path); + set_offset (key_format (&key), &key, (get_offset (&key) & ~(fs->s_blocksize - 1)) + 1); + if (usearch_by_key (fs, &key, path) != ITEM_FOUND) + die ("direct2indirect: can not find first part of tail"); + } + + unbh = reiserfsck_get_new_buffer (unfm ?: block_to_start (path)); + memset (unbh->b_data, 0, unbh->b_size); + + /* delete parts of tail coping their contents to new buffer */ + do { + //__u64 len = get_bytes_number(PATH_PLAST_BUFFER(path), ih, 0, CHECK_FREE_BYTES); + __u64 len; + + ih = PATH_PITEM_HEAD (path); + + len = get_bytes_number(ih, fs->s_blocksize); + + memcpy (unbh->b_data + copied, B_I_PITEM (PATH_PLAST_BUFFER (path), ih), len); + + save_unfm_overwriting (unbh->b_blocknr, ih); + copied += len; + set_offset (key_format (&key), &key, get_offset (&key) + len); +// set_offset (ih_key_format (ih), &key, get_offset (&key) + len); + + reiserfsck_delete_item (path, 0); + + } while (usearch_by_key (fs, &key, path) == ITEM_FOUND); + ih = PATH_PITEM_HEAD (path); + + pathrelse (path); + + /* paste or insert pointer to the unformatted node */ + set_offset (key_format (&key), &key, get_offset (&key) - copied); +// set_offset (ih_key_format (ih), &key, get_offset (&key) - copied); +// key.k_offset -= copied; + ni.unfm_nodenum = cpu_to_le32 (unbh->b_blocknr); + ni.unfm_freespace = (keep_free_space == 1) ? (fs->s_blocksize - copied) : 0; + +/* this is for check only */ + /*mark_block_unformatted (ni.unfm_nodenum);*/ + + if (usearch_by_position (fs, &key, key_format (&key), path) == FILE_NOT_FOUND) { + struct item_head insih; + + copy_key (&(insih.ih_key), &key); + set_key_format (&insih, key_format (&key)); + set_type (ih_key_format (&insih), &insih.ih_key, TYPE_INDIRECT); + set_free_space (&insih, ni.unfm_freespace); +// insih.u.ih_free_space = ni.unfm_freespace; + mark_item_unreachable (&insih); + insih.ih_item_len = cpu_to_le16 (UNFM_P_SIZE); + reiserfsck_insert_item (path, &insih, (const char *)&(ni.unfm_nodenum)); + } else { + ih = PATH_PITEM_HEAD (path); + + if (!is_indirect_ih (ih) || get_offset (&key) != get_bytes_number (ih, fs->s_blocksize) + 1) + die ("direct2indirect: incorrect item found"); + reiserfsck_paste_into_item (path, (const char *)&ni, UNFM_P_SIZE); + } + + mark_buffer_dirty (unbh); +// mark_buffer_uptodate (unbh, 0); + mark_buffer_uptodate (unbh, 1); + brelse (unbh); + + if (usearch_by_position (fs, &key, ih_key_format (ih), path) != POSITION_FOUND || !is_indirect_ih (PATH_PITEM_HEAD (path))) + die ("direct2indirect: position not found"); + return; +} + + + + +static int append_to_unformatted_node (struct item_head * comingih, struct item_head * ih, char * item, + struct path * path, __u16 * free_sp, __u64 coming_len) +{ + struct buffer_head * bh, * unbh; + __u64 end_of_data; //ih->u.ih_free_space; + __u64 offset = get_offset (&comingih->ih_key) % fs->s_blocksize - 1; + int zero_number; + __u32 unfm_ptr; + + /* append to free space of the last unformatted node of indirect item ih */ + if (*free_sp /*ih->u.ih_free_space*/ < coming_len) + { + + *free_sp = get_offset (&ih->ih_key) + fs->s_blocksize * I_UNFM_NUM (ih) - get_offset (&comingih->ih_key); + if (*free_sp < coming_len) + die ("reiserfsck_append_file: there is no enough free space in unformatted node"); + } + + end_of_data = fs->s_blocksize - *free_sp; + zero_number = offset - end_of_data; + + bh = PATH_PLAST_BUFFER (path); + + unfm_ptr = B_I_POS_UNFM_POINTER (bh, ih, I_UNFM_NUM (ih) - 1); + + /*if (unfm_ptr != 0 && unfm_ptr < SB_BLOCK_COUNT (fs))*/ + if (unfm_ptr && !not_data_block (fs, unfm_ptr)) + { + unbh = bread (fs->s_dev, unfm_ptr, fs->s_blocksize); + if (!is_block_used (unfm_ptr)) + die ("append_to_unformatted_node: unused block %d", unfm_ptr); + if (unbh == 0) + unfm_ptr = 0; + } else { + /* indirect item points to block which can not be pointed or to 0, in + any case we have to allocate new node */ + /*if (unfm_ptr == 0 || unfm_ptr >= SB_BLOCK_COUNT (fs)) {*/ + unbh = reiserfsck_get_new_buffer (bh->b_blocknr); + memset (unbh->b_data, 0, unbh->b_size); + B_I_POS_UNFM_POINTER (bh, ih, I_UNFM_NUM (ih) - 1) = unbh->b_blocknr; + /*mark_block_unformatted (unbh->b_blocknr);*/ + mark_buffer_dirty (bh); + } + memset (unbh->b_data + end_of_data, 0, zero_number); + memcpy (unbh->b_data + offset, item, coming_len); + + save_unfm_overwriting (unbh->b_blocknr, comingih); + + *free_sp /*ih->u.ih_free_space*/ -= (zero_number + coming_len); + set_free_space(ih, ih_free_space(ih) - (zero_number + coming_len)); + memset (unbh->b_data + offset + coming_len, 0, *free_sp); +// mark_buffer_uptodate (unbh, 0); + mark_buffer_uptodate (unbh, 1); + mark_buffer_dirty (unbh); + brelse (unbh); + pathrelse (path); + return coming_len; +} + + +static void adjust_free_space (struct buffer_head * bh, struct item_head * ih, struct item_head * comingih, __u16 *free_sp) +{ + // printf ("adjust_free_space does nothing\n"); + return; + if (is_indirect_ih (comingih)) { + set_free_space(ih, 0);//?? + *free_sp = (__u16)0; + } else { + if (get_offset (&comingih->ih_key) < get_offset (&ih->ih_key) + fs->s_blocksize * I_UNFM_NUM (ih)) + { + /* append to the last unformatted node */ + set_free_space (ih, fs->s_blocksize - get_offset(&ih->ih_key) % fs->s_blocksize + 1);//?? + *free_sp = (__u16)fs->s_blocksize - get_offset(&ih->ih_key) % fs->s_blocksize + 1; + } + else + { + set_free_space(ih,0);//?? + *free_sp =0; + } + } + mark_buffer_dirty (bh); +} + + +/* this appends file with one unformatted node pointer (since balancing + algorithm limitation). This pointer can be 0, or new allocated block or + pointer from indirect item that is being inserted into tree */ +int reiserfsck_append_file (struct item_head * comingih, char * item, int pos, struct path * path, + int was_in_tree) +{ + struct unfm_nodeinfo ni; + struct buffer_head * unbh; + int retval; + struct item_head * ih = PATH_PITEM_HEAD (path); + __u16 keep_free_space; + __u32 bytes_number; + + if (!is_indirect_ih (ih)) + die ("reiserfsck_append_file: can not append to non-indirect item"); + + //keep_free_space = ih_get_free_space(PATH_PLAST_BUFFER (path), PATH_PITEM_HEAD(path), 0); + keep_free_space = ih_free_space (ih); + + if (get_offset (&ih->ih_key) + get_bytes_number (ih, fs->s_blocksize) + //get_bytes_number (PATH_PLAST_BUFFER (path), PATH_PITEM_HEAD(path), 0, CHECK_FREE_BYTES) + != get_offset (&comingih->ih_key)){ + adjust_free_space (PATH_PLAST_BUFFER (path), ih, comingih, &keep_free_space); + } + + if (is_direct_ih (comingih)) { + //__u64 coming_len = get_bytes_number (0,comingih, item, CHECK_FREE_BYTES); + __u64 coming_len = get_bytes_number (comingih, fs->s_blocksize); + + if (get_offset (&comingih->ih_key) < get_offset (&ih->ih_key) + fs->s_blocksize * I_UNFM_NUM (ih)) { + /* direct item fits to free space of indirect item */ + return append_to_unformatted_node (comingih, ih, item, path, &keep_free_space, coming_len); + } + + unbh = reiserfsck_get_new_buffer (PATH_PLAST_BUFFER (path)->b_blocknr); + memset (unbh->b_data, 0, unbh->b_size); + /* this is for check only */ + /*mark_block_unformatted (unbh->b_blocknr);*/ + memcpy (unbh->b_data + get_offset (&comingih->ih_key) % unbh->b_size - 1, item, coming_len); + + save_unfm_overwriting (unbh->b_blocknr, comingih); + + mark_buffer_dirty (unbh); +// mark_buffer_uptodate (unbh, 0); + mark_buffer_uptodate (unbh, 1); + + ni.unfm_nodenum = unbh->b_blocknr; + ni.unfm_freespace = fs->s_blocksize - coming_len - (get_offset (&comingih->ih_key) % unbh->b_size - 1); + brelse (unbh); + retval = coming_len; + } else { + /* coming item is indirect item */ + //bytes_number = get_bytes_number (PATH_PLAST_BUFFER (path), PATH_PITEM_HEAD(path), 0, CHECK_FREE_BYTES); + bytes_number = get_bytes_number (ih, fs->s_blocksize); + if (get_offset (&comingih->ih_key) + pos * fs->s_blocksize != get_offset (&ih->ih_key) + bytes_number) + fsck_progress ("reiserfsck_append_file: can not append indirect item (%H) to the %H", + comingih, ih); + + /* take unformatted pointer from an indirect item */ + ni.unfm_nodenum = *(__u32 *)(item + pos * UNFM_P_SIZE);/*B_I_POS_UNFM_POINTER (bh, ih, pos);*/ + + if (!was_in_tree) { + if (still_bad_unfm_ptr_2 (ni.unfm_nodenum)) + die ("reiserfsck_append_file: bad unfm pointer"); + mark_block_used (ni.unfm_nodenum); + } + + ni.unfm_freespace = ((pos == (I_UNFM_NUM (comingih) - 1)) ? + //ih_get_free_space(0, comingih, item) /*comingih->u.ih_free_space*/ : 0); + ih_free_space (comingih) /*comingih->u.ih_free_space*/ : 0); + retval = fs->s_blocksize - ni.unfm_freespace; + } + + reiserfsck_paste_into_item (path, (const char *)&ni, UNFM_P_SIZE); + return retval; +} + + +int must_there_be_a_hole (struct item_head * comingih, struct path * path) +{ + struct item_head * ih = PATH_PITEM_HEAD (path); + int keep_free_space; + + if (is_direct_ih (ih)) { + direct2indirect2 (0, path, keep_free_space = 1); + ih = PATH_PITEM_HEAD (path); + } + + path->pos_in_item = I_UNFM_NUM (ih); + if (get_offset (&ih->ih_key) + (I_UNFM_NUM (ih) + 1) * fs->s_blocksize <= get_offset (&comingih->ih_key)) + return 1; + + return 0; +} + + +int reiserfs_append_zero_unfm_ptr (struct path * path) +{ + struct unfm_nodeinfo ni; + int keep_free_space; + + ni.unfm_nodenum = 0; + ni.unfm_freespace = 0; + + if (is_direct_ih (PATH_PITEM_HEAD (path))) + /* convert direct item to indirect */ + direct2indirect2 (0, path, keep_free_space = 0); + + reiserfsck_paste_into_item (path, (const char *)&ni, UNFM_P_SIZE); + return 0; +} + + +/* write direct item to unformatted node */ +/* coming item is direct */ +static int overwrite_by_direct_item (struct item_head * comingih, char * item, struct path * path) +{ + __u32 unfm_ptr; + struct buffer_head * unbh, * bh; + struct item_head * ih; + int offset; + __u64 coming_len = get_bytes_number (comingih, fs->s_blocksize); + + + bh = PATH_PLAST_BUFFER (path); + ih = PATH_PITEM_HEAD (path); + + unfm_ptr = le32_to_cpu (B_I_POS_UNFM_POINTER (bh, ih, path->pos_in_item)); + unbh = 0; + + if (unfm_ptr != 0 && unfm_ptr < SB_BLOCK_COUNT (fs)) { + /**/ + unbh = bread (fs->s_dev, unfm_ptr, bh->b_size); + if (!is_block_used (unfm_ptr)) + die ("overwrite_by_direct_item: unused block %d", unfm_ptr); + if (unbh == 0) + unfm_ptr = 0; + } + if (unfm_ptr == 0 || unfm_ptr >= SB_BLOCK_COUNT (fs)) + { + unbh = reiserfsck_get_new_buffer (bh->b_blocknr); + memset (unbh->b_data, 0, unbh->b_size); + B_I_POS_UNFM_POINTER (bh, ih, path->pos_in_item) = cpu_to_le32 (unbh->b_blocknr); + mark_buffer_dirty (bh); + } + + if (!unbh) { + die ("overwrite_by_direct_item: could not put direct item in"); + } + + offset = (get_offset (&comingih->ih_key) % bh->b_size) - 1; + if (offset + coming_len > MAX_DIRECT_ITEM_LEN (bh->b_size)) + die ("overwrite_by_direct_item: direct item too long (offset=%lu, length=%u)", + get_offset (&comingih->ih_key), coming_len); + + memcpy (unbh->b_data + offset, item, coming_len); + + save_unfm_overwriting (unbh->b_blocknr, comingih); + + if ((path->pos_in_item == (I_UNFM_NUM (ih) - 1)) && + (bh->b_size - ih_free_space (ih)) < (offset + coming_len)) { + set_free_space (ih, bh->b_size - (offset + coming_len)) ; + mark_buffer_dirty (bh); + } + mark_buffer_dirty (unbh); +// mark_buffer_uptodate (unbh, 0); + mark_buffer_uptodate (unbh, 1); + brelse (unbh); + return coming_len; +} + + + +void overwrite_unfm_by_unfm (unsigned long unfm_in_tree, unsigned long coming_unfm, int bytes_in_unfm) +{ + struct overwritten_unfm_segment * unfm_os_list;/* list of overwritten segments of the unformatted node */ + struct overwritten_unfm_segment unoverwritten_segment; + struct buffer_head * bh_in_tree, * coming_bh; + + if (!test_bit (coming_unfm % (fs->s_blocksize * 8), SB_AP_BITMAP (fs)[coming_unfm / (fs->s_blocksize * 8)]->b_data)) + /* block (pointed by indirect item) is free, we do not have to keep its contents */ + return; + + /* coming block is marked as used in disk bitmap. Put its contents to block in tree preserving + everything, what has been overwritten there by direct items */ + unfm_os_list = find_overwritten_unfm (unfm_in_tree, bytes_in_unfm, &unoverwritten_segment); + if (unfm_os_list) { + /* add_event (UNFM_OVERWRITING_UNFM);*/ + bh_in_tree = bread (fs->s_dev, unfm_in_tree, fs->s_blocksize); + coming_bh = bread (fs->s_dev, coming_unfm, fs->s_blocksize); + if (bh_in_tree == 0 || coming_bh == 0) + return; + + while (get_unoverwritten_segment (unfm_os_list, &unoverwritten_segment)) { + if (unoverwritten_segment.ous_begin < 0 || unoverwritten_segment.ous_end > bytes_in_unfm - 1 || + unoverwritten_segment.ous_begin > unoverwritten_segment.ous_end) + die ("overwrite_unfm_by_unfm: invalid segment found (%d %d)", unoverwritten_segment.ous_begin, unoverwritten_segment.ous_end); + + memcpy (bh_in_tree->b_data + unoverwritten_segment.ous_begin, coming_bh->b_data + unoverwritten_segment.ous_begin, + unoverwritten_segment.ous_end - unoverwritten_segment.ous_begin + 1); + mark_buffer_dirty (bh_in_tree); + } + + brelse (bh_in_tree); + brelse (coming_bh); + } +} + + +/* put unformatted node pointers from incoming item over the in-tree ones */ +static int overwrite_by_indirect_item (struct item_head * comingih, __u32 * coming_item, struct path * path, int * pos_in_coming_item) +{ + struct buffer_head * bh = PATH_PLAST_BUFFER (path); + struct item_head * ih = PATH_PITEM_HEAD (path); + int written; + __u32 * item_in_tree; + int src_unfm_ptrs, dest_unfm_ptrs, to_copy; + int i; + __u16 free_sp; + + + item_in_tree = (__u32 *)B_I_PITEM (bh, ih) + path->pos_in_item; + coming_item += *pos_in_coming_item; + + dest_unfm_ptrs = I_UNFM_NUM (ih) - path->pos_in_item; + src_unfm_ptrs = I_UNFM_NUM (comingih) - *pos_in_coming_item; + + if (dest_unfm_ptrs >= src_unfm_ptrs) { + /* whole coming item (comingih) fits into item in tree (ih) starting with path->pos_in_item */ + + //free_sp = ih_get_free_space(0, comingih, (char *)coming_item); + free_sp = ih_free_space (comingih); + + written = get_bytes_number (comingih, fs->s_blocksize) - + free_sp - *pos_in_coming_item * fs->s_blocksize; + *pos_in_coming_item = I_UNFM_NUM (comingih); + to_copy = src_unfm_ptrs; + if (dest_unfm_ptrs == src_unfm_ptrs) + set_free_space(ih, free_sp); //comingih->u.ih_free_space; + } else { + /* only part of coming item overlaps item in the tree */ + *pos_in_coming_item += dest_unfm_ptrs; + written = dest_unfm_ptrs * fs->s_blocksize; + to_copy = dest_unfm_ptrs; + set_free_space(ih, 0); + } + + for (i = 0; i < to_copy; i ++) { + if (!is_block_used (coming_item[i]) && !is_block_uninsertable (coming_item[i])) { + if (item_in_tree[i]) { + /* do not overwrite unformatted pointer. We must save everything what is there already from + direct items */ + overwrite_unfm_by_unfm (item_in_tree[i], coming_item[i], fs->s_blocksize); + } else { + item_in_tree[i] = coming_item[i]; + mark_block_used (coming_item[i]); + } + } + } + mark_buffer_dirty (bh); + return written; +} + + +static int reiserfsck_overwrite_file (struct item_head * comingih, char * item, + struct path * path, int * pos_in_coming_item, + int was_in_tree) +{ + __u32 unfm_ptr; + int written = 0; + int keep_free_space; + struct item_head * ih = PATH_PITEM_HEAD (path); + + + if (not_of_one_file (ih, &(comingih->ih_key))) + die ("reiserfsck_overwrite_file: found [%lu %lu], new item [%lu %lu]", + ih->ih_key.k_dir_id, ih->ih_key.k_objectid, + comingih->ih_key.k_dir_id, comingih->ih_key.k_objectid); + + if (is_direct_ih (ih)) { + unfm_ptr = 0; + if (is_indirect_ih (comingih)) { + if (get_offset (&ih->ih_key) % fs->s_blocksize != 1) + die ("reiserfsck_overwrite_file: second part of tail can not be overwritten by indirect item"); + /* use pointer from coming indirect item */ + unfm_ptr = le32_to_cpu (*(__u32 *)(item + *pos_in_coming_item * UNFM_P_SIZE)); + if (!was_in_tree) { + if (still_bad_unfm_ptr_2 (unfm_ptr)) + die ("reiserfsck_overwrite_file: still bad "); + } + } + /* */ + direct2indirect2 (le32_to_cpu (unfm_ptr), path, keep_free_space = 1); + } + if (is_direct_ih (comingih)) + { + written = overwrite_by_direct_item (comingih, item, path); + } else { + if (was_in_tree) + die ("reiserfsck_overwrite_file: item we are going to overwrite with could not be in the tree yet"); + written = overwrite_by_indirect_item (comingih, (__u32 *)item, path, pos_in_coming_item); + } + + return written; +} + + +/* + */ +int reiserfsck_file_write (struct item_head * ih, char * item, int was_in_tree) +{ + struct path path; + struct item_head * path_ih; + int count, pos_in_coming_item; + int retval; + struct key key; + int written; + + + if (make_file_writeable (ih) == -1) { + /* write was not completed. Skip that item. Maybe it should be + saved to lost_found */ + fsck_progress ("reiserfsck_file_write: skip writing %H\n", ih); + return 0; + } + + count = get_bytes_number (ih, fs->s_blocksize); + pos_in_coming_item = 0; + + copy_key (&key, &(ih->ih_key)); + + while (count) { + + retval = usearch_by_position (fs, &key, key_format (&key), &path); + + if (retval == DIRECTORY_FOUND) + reiserfs_panic ("directory found %k", key); + + + if (retval == POSITION_FOUND) { + written = reiserfsck_overwrite_file (ih, item, &path, &pos_in_coming_item, was_in_tree); + count -= written; + set_offset (key_format (&key), &key, get_offset (&key) + written); + } + if (retval == FILE_NOT_FOUND) { + written = create_first_item_of_file (ih, item, &path, &pos_in_coming_item, was_in_tree); + count -= written; + + set_offset (key_format (&key), &key, get_offset (&key) + written ); + } + if (retval == POSITION_NOT_FOUND) { + + path_ih = PATH_PITEM_HEAD (&path); + + if (must_there_be_a_hole (ih, &path) == 1) + { + reiserfs_append_zero_unfm_ptr (&path); + }else { + count -= reiserfsck_append_file (ih, item, pos_in_coming_item, &path, was_in_tree); + set_offset (key_format (&key), &key, get_offset (&key) + fs->s_blocksize); + pos_in_coming_item ++; + } + } + if (count < 0) + die ("reiserfsck_file_write: count < 0 (%d)", count); + pathrelse (&path); + } + + return get_bytes_number (ih, fs->s_blocksize); +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fsck/uobjectid.c b/fsck/uobjectid.c new file mode 100644 index 0000000..ee9a07f --- /dev/null +++ b/fsck/uobjectid.c @@ -0,0 +1,391 @@ +/* + * Copyright 1996-2001 Hans Reiser + */ +#include "fsck.h" + + +/* when --check fsck builds a map of objectids of files it finds in the tree + when --rebuild-tree - fsck builds map of objectids it inserts into tree + FIXME: objectid gets into map when stat data item +*/ + + + +/* is it marked used in super block's objectid map */ +int is_objectid_used (reiserfs_filsys_t s, __u32 objectid) +{ + __u32 * objectid_map; + int i = 0; + + objectid_map = (__u32 *)((char *)(s->s_rs) + (sb_size(s))); + + while (i < SB_OBJECTID_MAP_SIZE (s)) { + if (objectid == objectid_map[i]) { + return 1; /* objectid is used */ + } + + if (objectid > objectid_map[i] && objectid < objectid_map[i+1]) { + return 1; /* objectid is used */ + } + + if (objectid < objectid_map[i]) + break; + + i += 2; + } + + /* objectid is free */ + return 0; +} + + + +/* true objectid map */ + + +/* size of 1 piece of map */ +#define MAP_SIZE 4096 /* must be n * 2 * sizeof(__u32) */ +#define MAX_MAP_SIZE 1 /* % of available memory? */ + + + +/* increase area by MAP_SIZE bytes */ +static void grow_id_map (struct id_map * map) +{ + if (map->m_page_count && ((map->m_page_count % 5) == 0)) { + fsck_log ("grow_id_map: objectid map expanded: used %lu, %d blocks\n", + map->m_used_slots_count, map->m_page_count); + } + map->m_begin = expandmem (map->m_begin, map->m_page_count * MAP_SIZE, MAP_SIZE); + map->m_page_count ++; +} + + +static void try_to_shrink_id_map (struct id_map * map) +{ + if (map->m_used_slots_count * sizeof(__u32) <= (map->m_page_count - 1) * MAP_SIZE) { + if (map->m_page_count && ((map->m_page_count % 5) == 0)) + fsck_log ("shrink_id_map: objectid map shrinked: used %lu, %d blocks\n", + map->m_used_slots_count, map->m_page_count); + map->m_begin = expandmem (map->m_begin, map->m_page_count * MAP_SIZE, + -MAP_SIZE); + map->m_page_count--; + } +} + + +/* ubin_search_id is used to find id in the map (or proper place to + insert the new id). if smth goes wrong or ubin_search_id stops + working properly check_id_search_result should help to find raised + problems */ +static void check_id_search_result(struct id_map * map, int res, __u32 pos, + __u32 id) +{ + if (res != ITEM_FOUND && res != ITEM_NOT_FOUND) + die("check_id_search_result: get wrong result from ubin_search (%d)", res); + + if (res == 1 && *(map->m_begin + pos) != id) + die("check_id_search_result: wrong id found %u %u", id, *(map->m_begin + pos)); + + if (res == 1) + { + if (pos > map->m_used_slots_count) + die("check_id_search_result: get bad position (%u), used %u", + pos, map->m_used_slots_count); + if (pos >= 0 && pos <= map->m_used_slots_count && *(map->m_begin + pos - 1) >= id) + die("check_id_search_result: previous id (%u) not less than (%u)", + *(map->m_begin + pos - 1), id); + if (pos >= 0 && pos < map->m_used_slots_count && *(map->m_begin + pos) < id) + die("check_id_search_result: found id (%u) not much than (%u)", + *(map->m_begin + pos), id); + } +} + + +static int comp_ids (void * p1, void * p2) +{ + __u32 * id1, * id2; + + id1 = p1; + id2 = p2; + + if ( *id1 < *id2 ) + return -1; + if ( *id1 > *id2 ) + return 1; + return 0; +} + + +/* */ +struct id_map * init_id_map (void) +{ + struct id_map * map; + + map = getmem (sizeof (struct id_map)); + map->m_begin = NULL; + map->m_used_slots_count = 0; + map->m_page_count = 0; + mark_objectid_really_used (map, 1); + return map; +} + + +/* free whole map */ +void free_id_map (struct id_map ** map) +{ + freemem ((*map)->m_begin); + freemem (*map); + *map = 0; +} + + +/* return 1 if id is marked used, 0 otherwise */ +int is_objectid_really_used (struct id_map * map, __u32 id, int * ppos) +{ + int res; + + *ppos = 0; + + if (map->m_begin == NULL) + return 0; + + /* smth exists in the map, find proper place to insert or this id */ + res = ubin_search (&id, map->m_begin, map->m_used_slots_count, sizeof (__u32), ppos, comp_ids); +#if 1 + check_id_search_result (map, res, *ppos, id); +#endif + /* *ppos is position in objectid map of the element which is equal id + or position of an element which is smallest and greater than id */ + if (res == ITEM_NOT_FOUND) + /* id is not found in the map. if returned position is odd - + id is marked used */ + return (*ppos % 2); + + /* if returned position is odd - id is marked free */ + return !(*ppos % 2); +} + + +static void check_objectid_map (struct id_map * map) +{ + int i; + + for (i = 1; i < map->m_used_slots_count; i ++) + if (map->m_begin [i - 1] >= map->m_begin [i]) + die ("check_objectid_map: map corrupted"); +} + + +/* returns 1 objectid is marked used already, 0 otherwise */ +int mark_objectid_really_used (struct id_map * map, __u32 id) +{ + int pos; + + + /* check whether id is used and get place if used or place to insert if not */ + if (is_objectid_really_used (map, id, &pos) == 1) + return 1; + + map->objectids_marked ++; + if (pos % 2 == 0){ + /* id not found in the map. why? is_id_used() knows */ + + if (map->m_begin == NULL) + /* map is empty */ + grow_id_map (map); + + /* id + 1 is used, change id + 1 to id and exit */ + if ( id + 1 == le32_to_cpu (map->m_begin[pos]) ) { + /* we can mark id as used w/o expanding of id map */ + map->m_begin[pos] = cpu_to_le32 (id); + + check_objectid_map (map); + return 0; + } + + if (map->m_page_count * MAP_SIZE == map->m_used_slots_count * sizeof(__u32)) + /* fixme: do not grow too much */ + grow_id_map (map); + + if (map->m_used_slots_count - pos > 0) + memmove (map->m_begin + pos + 2, map->m_begin + pos, (map->m_used_slots_count - pos) * sizeof (__u32)); + + map->m_used_slots_count += 2; + map->m_begin[pos] = cpu_to_le32 (id); + map->m_begin[pos+1] = cpu_to_le32 (id + 1); + + check_objectid_map (map); + + return 0; + } + + /* id found in the map. pos is odd position () */ + map->m_begin[pos] = cpu_to_le32 (id + 1); + + /* if end id of current interval == start id of next interval we + eliminated a sequence of unused objectids */ + if (pos + 1 < map->m_used_slots_count && + map->m_begin[pos + 1] == map->m_begin[pos]) { + memmove (map->m_begin + pos, map->m_begin + pos + 2, (map->m_used_slots_count - pos - 2) * sizeof (__u32)); + map->m_used_slots_count -= 2; + try_to_shrink_id_map (map); + } + + check_objectid_map (map); + + return 0; +} + + +static __u32 get_free_id (reiserfs_filsys_t fs) +{ + struct id_map * map; + + map = proper_id_map (fs); + + /* If map is not NULL return the second element (first position in + the map). This allocates the first unused objectid. That is to + say, the first entry on the objectid map is the first unused + objectid. */ + if (map->m_begin == NULL) { + fprintf (stderr, "get_free_id: hmm, 1 is allocated as objectid\n"); + return 1; + } + return (le32_to_cpu (map->m_begin[1])); +} + + +__u32 get_unused_objectid (reiserfs_filsys_t fs) +{ + __u32 objectid; + + objectid = get_free_id (fs); + if (mark_objectid_really_used (proper_id_map (fs), objectid)) + die ("get_unused_objectid: could not mark %lu used", objectid); + + return objectid; +} + + +#define objectid_map(fs) ((char *)((char *)((fs)->s_rs) + sb_size (fs))) + + +#if 0 +/* returns 0 if on-disk objectid map matches to the correct one, 1 + otherwise */ +int compare_id_maps (reiserfs_filsys_t fs) +{ + struct id_map * map; + int disk_size; + + map = proper_id_map (fs); + + disk_size = rs_objectid_map_size (fs->s_rs); + if (disk_size != map->m_used_slots_count || + memcmp ((char *)((char *)((fs)->s_rs) + sb_size (fs)), map->m_begin, sizeof(__u32) * disk_size)) { + fprintf (stderr, "Objectid maps mismatch\n"); + return 1; + } + + return 0; +} + + +/* copy objectid map into buffer containing super block */ +void correct_objectid_map (reiserfs_filsys_t fs) +{ + struct id_map * map; + int size, disk_max; + + map = proper_id_map (fs); + + size = map->m_used_slots_count; + disk_max = rs_objectid_map_max_size (fs->s_rs); + if (disk_max < size) { + size = disk_max; + } else { + memset (fu_objectid_map (fs) + size, 0, (disk_max - size) * sizeof (__u32)); + } + + memcpy (fu_objectid_map (fs), map->m_begin, size * sizeof (__u32)); + set_objectid_map_size (fs->s_rs, size); + mark_buffer_dirty (SB_BUFFER_WITH_SB (fs)); + +/* + if (fs->fu_job->verbose) + fprintf (stderr, "Objectid map corrected\n"); +*/ +} +#endif + + +#if 0 +/* print the map of objectids */ +void print_objectid_list () +{ + int i; + printf ("\n control id map: all %d, used:%d", id_map.m_page_count * MAP_SIZE, id_map.m_used_slots_count); + + for (i = 0; i < id_map.m_used_slots_count; i += 2) + printf ("\n[%u-%u]", id_map.m_begin[i], id_map.m_begin[i + 1] - 1); +} + +/* print on-disk map of objectids */ +void print_disk_objectid_list (void) +{ + int i; + __u32 * objectid_map = (__u32 *)((char *)SB_DISK_SUPER_BLOCK (&g_sb) + (sb_size(&g_sb))); + printf ("\n on-disk id map. used:%lu", SB_OBJECTID_MAP_SIZE(&g_sb)); + + for (i = 0; i < SB_OBJECTID_MAP_SIZE(&g_sb); i += 2) + printf ("\n[%u-%u]", objectid_map[i], objectid_map[i + 1] - 1); +} +#endif + + +void flush_objectid_map (struct id_map * map, reiserfs_filsys_t fs) +{ + int size, max; + int sb_size; + __u32 * sb_objectid_map; + + sb_size = (is_reiser2fs_magic_string (fs->s_rs) ? SB_SIZE : SB_SIZE_V1); + sb_objectid_map = (__u32 *)((char *)(fs->s_rs) + sb_size); + + check_objectid_map (map); + + max = ((fs->s_blocksize - sb_size) >> 3 << 1); + set_objectid_map_max_size (fs->s_rs, max); + if (map->m_used_slots_count > max) + size = max; + else + size = map->m_used_slots_count; + + memcpy (sb_objectid_map, map->m_begin, size * sizeof (__u32)); + memset (sb_objectid_map + size, 0, (max - size) * sizeof (__u32)); + + set_objectid_map_size (fs->s_rs, size); + if (size == max) + /*((__u32 *)((char *)(fs->s_rs) + sb_size))*/ + sb_objectid_map [max - 1] = map->m_begin [map->m_used_slots_count - 1]; + + check_objectid_map (map); + +} + + +void fetch_objectid_map (struct id_map * map, reiserfs_filsys_t fs) +{ + int sb_size; + __u32 * sb_objectid_map; + + sb_size = (is_reiser2fs_magic_string (fs->s_rs) ? SB_SIZE : SB_SIZE_V1); + sb_objectid_map = (__u32 *)((char *)(fs->s_rs) + sb_size); + + if (map->m_page_count != 1) + die ("fetch_objectid_map: can not fetch long map"); + grow_id_map (map); + memcpy (map->m_begin, sb_objectid_map, rs_objectid_map_size (fs->s_rs) * sizeof (__u32)); + map->m_used_slots_count = rs_objectid_map_size (fs->s_rs); +} diff --git a/fsck/ustree.c b/fsck/ustree.c new file mode 100644 index 0000000..68e7f48 --- /dev/null +++ b/fsck/ustree.c @@ -0,0 +1,704 @@ +/* + * Copyright 1996-2001 Hans Reiser + */ +#include "fsck.h" + + +/* key1 and key2 are pointer to deh_offset of the struct reiserfs_de_head */ +int comp_dir_entries (void * key1, void * key2) +{ + __u32 off1, off2; + + off1 = le32_to_cpu (*(__u32 *)key1); + off2 = le32_to_cpu (*(__u32 *)key2); + + if (off1 < off2) + return -1; + if (off1 > off2) + return 1; + return 0; +} + + +void init_tb_struct (struct tree_balance * tb, struct super_block * s, struct path * path, int size) +{ + memset (tb, '\0', sizeof(struct tree_balance)); + tb->tb_sb = s; + tb->tb_path = path; + + PATH_OFFSET_PBUFFER(path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL; + PATH_OFFSET_POSITION(path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0; + tb->insert_size[0] = size; +} + + +struct tree_balance * cur_tb = 0; + +void reiserfsck_paste_into_item (struct path * path, const char * body, int size) +{ + struct tree_balance tb; + + init_tb_struct (&tb, fs, path, size); + if (fix_nodes (/*tb.transaction_handle,*/ M_PASTE, &tb, 0/*ih*/) != CARRY_ON) + //fix_nodes(options, tree_balance, ih_to_option, body_to_option) + + die ("reiserfsck_paste_into_item: fix_nodes failed"); + + do_balance (/*tb.transaction_handle,*/ &tb, 0, body, M_PASTE, 0/*zero num*/); +} + + +void reiserfsck_insert_item (struct path * path, struct item_head * ih, const char * body) +{ + struct tree_balance tb; + + init_tb_struct (&tb, fs, path, IH_SIZE + ih_item_len(ih)); + if (fix_nodes (/*tb.transaction_handle,*/ M_INSERT, &tb, ih/*, body*/) != CARRY_ON) + die ("reiserfsck_insert_item: fix_nodes failed"); + do_balance (/*tb.transaction_handle,*/ &tb, ih, body, M_INSERT, 0/*zero num*/); +} + + +static void free_unformatted_nodes (struct item_head * ih, struct buffer_head * bh) +{ + __u32 * punfm = (__u32 *)B_I_PITEM (bh, ih); + int i; + + for (i = 0; i < I_UNFM_NUM (ih); i ++, punfm ++) + if (*punfm) { + struct buffer_head * to_be_forgotten; + + to_be_forgotten = find_buffer (fs->s_dev, *punfm, fs->s_blocksize); + if (to_be_forgotten) { + //atomic_inc(&to_be_forgotten->b_count); + to_be_forgotten->b_count ++; + bforget (to_be_forgotten); + } + + reiserfs_free_block (fs, *punfm); + } +} + + +void reiserfsck_delete_item (struct path * path, int temporary) +{ + struct tree_balance tb; + struct item_head * ih = PATH_PITEM_HEAD (path); + + if (is_indirect_ih (ih) && !temporary) + free_unformatted_nodes (ih, PATH_PLAST_BUFFER (path)); + + init_tb_struct (&tb, fs, path, -(IH_SIZE + ih_item_len(ih))); + + if (fix_nodes (/*tb.transaction_handle,*/ M_DELETE, &tb, 0/*ih*/) != CARRY_ON) + die ("reiserfsck_delete_item: fix_nodes failed"); + + do_balance (/*tb.transaction_handle,*/ &tb, 0, 0, M_DELETE, 0/*zero num*/); +} + + +void reiserfsck_cut_from_item (struct path * path, int cut_size) +{ + struct tree_balance tb; + struct item_head * ih; + + if (cut_size >= 0) + die ("reiserfsck_cut_from_item: cut size == %d", cut_size); + + if (is_indirect_ih (ih = PATH_PITEM_HEAD (path))) { + __u32 unfm_ptr = B_I_POS_UNFM_POINTER (PATH_PLAST_BUFFER (path), ih, I_UNFM_NUM (ih) - 1); + if (unfm_ptr) { + struct buffer_head * to_be_forgotten; + + to_be_forgotten = find_buffer (fs->s_dev, le32_to_cpu (unfm_ptr), fs->s_blocksize); + if (to_be_forgotten) { + //atomic_inc(&to_be_forgotten->b_count); + to_be_forgotten->b_count ++; + bforget (to_be_forgotten); + } + reiserfs_free_block (fs, le32_to_cpu (unfm_ptr)); + } + } + + + init_tb_struct (&tb, fs, path, cut_size); + + if (fix_nodes (/*tb.transaction_handle,*/ M_CUT, &tb, 0) != CARRY_ON) + die ("reiserfsck_cut_from_item: fix_nodes failed"); + + do_balance (/*tb.transaction_handle,*/ &tb, 0, 0, M_CUT, 0/*zero num*/); +} + + +/* uget_lkey is utils clone of stree.c/get_lkey */ +struct key * uget_lkey (struct path * path) +{ + int pos, offset = path->path_length; + struct buffer_head * bh; + + if (offset < FIRST_PATH_ELEMENT_OFFSET) + die ("uget_lkey: illegal offset in the path (%d)", offset); + + + /* While not higher in path than first element. */ + while (offset-- > FIRST_PATH_ELEMENT_OFFSET) { + if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset)) ) + die ("uget_lkey: parent is not uptodate"); + + /* Parent at the path is not in the tree now. */ + if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset))) + die ("uget_lkey: buffer on the path is not in tree"); + + /* Check whether position in the parent is correct. */ + if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh)) + die ("uget_lkey: invalid position (%d) in the path", pos); + + /* Check whether parent at the path really points to the child. */ + if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr) + die ("uget_lkey: invalid block number (%d). Must be %d", + B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr); + + /* Return delimiting key if position in the parent is not equal to zero. */ + if (pos) + return B_N_PDELIM_KEY(bh, pos - 1); + } + + /* there is no left delimiting key */ + return 0; +} + + +/* uget_rkey is utils clone of stree.c/get_rkey */ +struct key * uget_rkey (struct path * path) +{ + int pos, offset = path->path_length; + struct buffer_head * bh; + + if (offset < FIRST_PATH_ELEMENT_OFFSET) + die ("uget_rkey: illegal offset in the path (%d)", offset); + + while (offset-- > FIRST_PATH_ELEMENT_OFFSET) { + if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset))) + die ("uget_rkey: parent is not uptodate"); + + /* Parent at the path is not in the tree now. */ + if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset))) + die ("uget_rkey: buffer on the path is not in tree"); + + /* Check whether position in the parrent is correct. */ + if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh)) + die ("uget_rkey: invalid position (%d) in the path", pos); + + /* Check whether parent at the path really points to the child. */ + if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr) + die ("uget_rkey: invalid block number (%d). Must be %d", + B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr); + + /* Return delimiting key if position in the parent is not the last one. */ + if (pos != B_NR_ITEMS (bh)) + return B_N_PDELIM_KEY(bh, pos); + } + + /* there is no right delimiting key */ + return 0; +} + + +inline int ubin_search (void * key, void * base, int num, int width, __u32 *ppos, comp_function_t comp_func) +{ + __u32 rbound, lbound, j; + + lbound = 0; + + if (num == 0){ + *ppos = 0; + return ITEM_NOT_FOUND; + } + + rbound = num - 1; + + for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { + switch (comp_func ((void *)((char *)base + j * width), key ) ) { + case -1:/* second is greater */ + lbound = j + 1; + continue; + + case 1: /* first is greater */ + if (j == 0){ + *ppos = lbound; + return ITEM_NOT_FOUND; + } + rbound = j - 1; + continue; + + case 0: + *ppos = j; + return ITEM_FOUND; + } + } + + *ppos = lbound; + return ITEM_NOT_FOUND; +} + + +/* this searches in tree through items */ +int usearch_by_key (struct super_block * s, struct key * key, struct path * path) +{ + struct buffer_head * bh; + unsigned long block = SB_ROOT_BLOCK (s); + struct path_element * curr; + int retval; + + path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET; + while (1) { + curr = PATH_OFFSET_PELEMENT (path, ++ path->path_length); + bh = curr->pe_buffer = bread (s->s_dev, block, s->s_blocksize); + if (bh == 0) + reiserfs_panic ("usearch_by_key: unable to read %lu block on device 0x%x\n",block, s->s_dev); + retval = ubin_search (key, B_N_PKEY (bh, 0), B_NR_ITEMS (bh), + is_leaf_node (bh) ? IH_SIZE : KEY_SIZE, &(curr->pe_position), comp_keys); + if (retval == ITEM_FOUND) { + /* key found, return if this is leaf level */ + if (is_leaf_node (bh)) { + path->pos_in_item = 0; + return ITEM_FOUND; + } + curr->pe_position ++; + } else { + /* key not found in the node */ + if (is_leaf_node (bh)) + return ITEM_NOT_FOUND; + } + block = B_N_CHILD_NUM (bh, curr->pe_position); + } + die ("search_by_key: you can not get here"); + return 0; +} + + +/* key is key of directory entry. This searches in tree through items and in + the found directory item as well */ +int usearch_by_entry_key (struct super_block * s, struct key * key, struct path * path) +{ + struct buffer_head * bh; + struct item_head * ih; + struct key tmpkey; + + if (usearch_by_key (s, key, path) == ITEM_FOUND) { + /* entry found */ + path->pos_in_item = 0; + return POSITION_FOUND; + } + + bh = PATH_PLAST_BUFFER (path); + + if (PATH_LAST_POSITION (path) == 0) { + /* previous item does not exist, that means we are in leftmost leaf of + the tree */ + if (uget_lkey (path) != 0) + die ("search_by_entry_key: invalid position after search_by_key"); + + if (not_of_one_file (B_N_PKEY (bh, 0), key)) { + path->pos_in_item = 0; + return DIRECTORY_NOT_FOUND; + } + + if (!is_direntry_ih (get_ih (path))) { + fsck_progress ("search_by_entry_key: directory expected to have this key %K\n", key); + return REGULAR_FILE_FOUND; + } + + /* position for name insertion is found */ + path->pos_in_item = 0; + return POSITION_NOT_FOUND; + } + + /* take previous item */ + PATH_LAST_POSITION (path) --; + ih = PATH_PITEM_HEAD (path); + if (not_of_one_file (&(ih->ih_key), key) || !is_direntry_ih(ih)) { + /* previous item belongs to another object or is stat data, check next + item */ + + PATH_LAST_POSITION (path) ++; + if (PATH_LAST_POSITION (path) < B_NR_ITEMS (bh)) + { + /* found item is not last item of the node */ + struct item_head * next_ih = B_N_PITEM_HEAD (bh, PATH_LAST_POSITION (path)); + + if (not_of_one_file (&(next_ih->ih_key), key)) + { + path->pos_in_item = 0; + return DIRECTORY_NOT_FOUND; + } + if (!is_direntry_ih(next_ih)) + { + /* there is an item in the tree, but it is not a directory item */ + reiserfs_warning (stderr, "search_by_entry_key: directory expected to have this key %k\n", + key); + return REGULAR_FILE_FOUND; + } + } else { + /* found item is last item of the node */ + struct key * next_key = uget_rkey (path); + + if (next_key == 0 || not_of_one_file (next_key, key)) + { + /* there is not any part of such directory in the tree */ + path->pos_in_item = 0; + return DIRECTORY_NOT_FOUND; + } + + if (!is_direntry_key (next_key)) + { + /* there is an item in the tree, but it is not a directory item */ + fsck_progress ("search_by_entry_key: directory expected to have this key %k\n", + key); + return REGULAR_FILE_FOUND; + } + + // we got right delimiting key - search for it - the entry will be + // pasted in position 0 + copy_key (&tmpkey, next_key); + pathrelse (path); + if (usearch_by_key (s, &tmpkey, path) != ITEM_FOUND || PATH_LAST_POSITION (path) != 0) + die ("search_by_entry_key: item not found by corresponding delimiting key"); + } + + /* next item is the part of this directory */ + path->pos_in_item = 0; + + return POSITION_NOT_FOUND; + } + + /* previous item is part of desired directory */ + if (ubin_search (&(key->u.k_offset_v1.k_offset), B_I_DEH (bh, ih), ih_entry_count (ih), + DEH_SIZE, &(path->pos_in_item), comp_dir_entries) == ITEM_FOUND) + return POSITION_FOUND; + + return POSITION_NOT_FOUND; +} + + +/* key is key of byte in the regular file. This searches in tree + through items and in the found item as well */ +int usearch_by_position (struct super_block * s, struct key * key, int version, struct path * path) +{ + struct buffer_head * bh; + struct item_head * ih; + + if (usearch_by_key (s, key, path) == ITEM_FOUND) + { + ih = PATH_PITEM_HEAD (path); + + if (!is_direct_ih(ih) && !is_indirect_ih(ih)) + return DIRECTORY_FOUND; + path->pos_in_item = 0; + return POSITION_FOUND; + } + + bh = PATH_PLAST_BUFFER (path); + ih = PATH_PITEM_HEAD (path); + + + if ( (PATH_LAST_POSITION(path) < B_NR_ITEMS (bh)) && + !not_of_one_file (&ih->ih_key, key) && + (get_offset(&ih->ih_key) == get_offset(key)) ) + { + + if (!is_direct_ih(ih) && !is_indirect_ih(ih)) + return DIRECTORY_FOUND; + path->pos_in_item = 0; + + + return POSITION_FOUND; + } + + if (PATH_LAST_POSITION (path) == 0) { + /* previous item does not exist, that means we are in leftmost leaf of the tree */ + if (!not_of_one_file (B_N_PKEY (bh, 0), key)) { + if (!is_direct_ih(ih) && !is_indirect_ih (ih)) + return DIRECTORY_FOUND; + return POSITION_NOT_FOUND; + } + return FILE_NOT_FOUND; + } + + + /* take previous item */ + PATH_LAST_POSITION (path) --; + ih = PATH_PITEM_HEAD (path); + + if (not_of_one_file (&ih->ih_key, key) || is_stat_data_ih(ih)) { + struct key * next_key; + + /* previous item belongs to another object or is a stat data, check next item */ + PATH_LAST_POSITION (path) ++; + if (PATH_LAST_POSITION (path) < B_NR_ITEMS (PATH_PLAST_BUFFER (path))) + /* next key is in the same node */ + next_key = B_N_PKEY (PATH_PLAST_BUFFER (path), PATH_LAST_POSITION (path)); + else + next_key = uget_rkey (path); + if (next_key == 0 || not_of_one_file (next_key, key)) { + /* there is no any part of such file in the tree */ + path->pos_in_item = 0; + return FILE_NOT_FOUND; + } + + if (is_direntry_key (next_key)) { + fsck_log ("\nusearch_by_position: looking for %k found a directory with the same key\n", next_key); + return DIRECTORY_FOUND; + } + /* next item is the part of this file */ + path->pos_in_item = 0; + if ( get_offset(next_key) == get_offset(key) ) { + pathrelse(path); + if (usearch_by_key (s, next_key, path) != ITEM_FOUND) { + reiserfs_panic ("usearch_by_position: keys must be equals %k %k", + next_key, &PATH_PITEM_HEAD (path)->ih_key); + } + return POSITION_FOUND; + } + + return POSITION_NOT_FOUND; + } + + if (is_direntry_ih(ih)) { + return DIRECTORY_FOUND; + } + if (is_stat_data_ih(ih)) { + PATH_LAST_POSITION (path) ++; + return FILE_NOT_FOUND; + } + + /* previous item is part of desired file */ + + + //if (is_key_in_item (bh,ih,key,bh->b_size)) { + if (I_K_KEY_IN_ITEM (ih, key, bh->b_size)) { + path->pos_in_item = get_offset (key) - get_offset (&ih->ih_key); + if (is_indirect_ih (ih) ) + path->pos_in_item /= bh->b_size; + return POSITION_FOUND; + } + + path->pos_in_item = is_indirect_ih (ih) ? I_UNFM_NUM (ih) : ih_item_len (ih); + return POSITION_NOT_FOUND; +} + + +static unsigned long first_child (struct buffer_head * bh) +{ + return child_block_number (bh, 0); +} + +#if 0 +static unsigned long last_child (struct buffer_head * bh) +{ + return child_block_number (bh, node_item_number (bh)); +} +#endif + +static unsigned long get_child (int pos, struct buffer_head * parent) +{ + if (pos == -1) + return -1; + + if (pos > B_NR_ITEMS (parent)) + die ("get_child: no child found, should not happen: %d of %d", pos, B_NR_ITEMS (parent)); + return child_block_number (parent, pos); + +/* + for (i = 0; i < B_NR_ITEMS (parent); i ++) + { + if (child_block_number (parent, i) == block) + return child_block_number (parent, i + 1); + } + die ("next_child: no child found: should not happen"); + return 0; + */ +} + + +static void print (int cur, int total) +{ + printf ("/%3d (of %3d)", cur, total);fflush (stdout); +} + + +/* erase /XXX(of XXX) */ +static void erase (void) +{ + printf ("\b\b\b\b\b\b\b\b\b\b\b\b\b"); + printf (" "); + printf ("\b\b\b\b\b\b\b\b\b\b\b\b\b"); + fflush (stdout); +} + +#if 0 +void pass_through_tree2 (struct super_block * s, do_after_read_t action1, + do_on_full_path_t action2) +{ + struct buffer_head * path[MAX_HEIGHT] = {0,}; + int total[MAX_HEIGHT] = {0,}; + int cur[MAX_HEIGHT] = {0,}; + int h = 0; + unsigned long block = SB_ROOT_BLOCK (s); + int del_p; + + if (block >= SB_BLOCK_COUNT (s) || not_data_block (s, block)) + return; + + while ( 1 ) { + if (path[h]) + die ("pass_through_tree: empty slot expected"); + if (h) + print (cur[h - 1], total[h - 1]); + + path[h] = bread (s->s_dev, block, s->s_blocksize); + get_child (-1, path[h]); + + if (path[h] == 0) + reiserfs_warning ("pass_through_tree: unable to read %lu block on device 0x%x\n", + block, s->s_dev); + + del_p = 0; + if (path[h] && action1) { + if (action1 (s, path, h)) { + ; +#if 0 + // something wrong with a buffer we just have read + if (opt_fsck_mode == FSCK_FAST_REBUILD){ + //need to change the way we are going on + del_p = 1; + if (h == 0) + break; + } else { + ; + //reiserfs_panic (s, "Run reiserfsck with --rebuild-tree\n"); + } +#endif + } + } + + if (!path[h] || is_leaf_node (path[h])) + { + if (path[h] && action2) { + if (action2 (s, path, h)) { + ; +#if 0 + if (opt_fsck_mode == FSCK_FAST_REBUILD) { + //need to change the way we are going on + del_p = 1; + if (h == 0) + break; + } else { + ; + //reiserfs_panic (s, "Run reiserfsck with --rebuild-tree\n"); + } +#endif + } + } + + if (path[h]) + brelse (path[h]); + if (h) + erase (); + + while (h && (!path[h-1] || cur[h-1] == total[h-1] )) + { + path[h] = 0; + h --; + if (path[h]) + brelse (path[h]); + if (h) + erase (); + } + + if (h == 0) { + path[h] = 0; + break; + } + + if (path[h]) + cur[h - 1] ++; + if (del_p){ + total[h-1]--; + del_p = 0; + } + block = get_child (cur[h - 1] - 1, path[h-1]); + path[h] = 0; + continue; + } + total[h] = B_NR_ITEMS (path[h]) + 1; + cur[h] = 1; + block = first_child (path[h]); + h ++; + } +} +#endif + +void pass_through_tree (struct super_block * s, do_after_read_t action1, + do_on_full_path_t action2) +{ + struct buffer_head * path[MAX_HEIGHT] = {0,}; + int total[MAX_HEIGHT] = {0,}; + int cur[MAX_HEIGHT] = {0,}; + int h = 0; + unsigned long block = SB_ROOT_BLOCK (s); + + + if (block >= SB_BLOCK_COUNT (s) || not_data_block (s, block)) { + fsck_progress ("\nBad root block %lu. (--rebuild-tree did not complete)\n", block); + return; + } + + + while ( 1 ) { + if (path[h]) + die ("pass_through_tree: empty slot expected"); + if (h) + print (cur[h - 1], total[h - 1]); + + path[h] = bread (s->s_dev, block, s->s_blocksize); + if (path[h] == 0) + /* FIXME: handle case when read failed */ + die ("pass_through_tree: unable to read %lu block on device 0x%x\n", + block, s->s_dev); + + if (action1) + action1 (s, path, h); + + if (is_leaf_node (path[h])) { + if (action2) + action2 (s, path, h); + + brelse (path[h]); + if (h) + erase (); + + while (h && (cur[h-1] == total[h-1])) { + path[h] = 0; + h --; + brelse (path[h]); + if (h) + erase (); + } + + if (h == 0) { + path[h] = 0; + break; + } + + block = get_child (cur[h - 1], path[h-1]); + cur[h - 1] ++; + path[h] = 0; + continue; + } + total[h] = B_NR_ITEMS (path[h]) + 1; + cur[h] = 1; + block = first_child (path[h]); + h ++; + } +} diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..0de55ab --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1 @@ +noinst_HEADERS = io.h misc.h reiserfs_fs.h reiserfs_lib.h diff --git a/include/Makefile.in b/include/Makefile.in new file mode 100644 index 0000000..813e729 --- /dev/null +++ b/include/Makefile.in @@ -0,0 +1,197 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +noinst_HEADERS = io.h misc.h reiserfs_fs.h reiserfs_lib.h +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps include/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = include + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(HEADERS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-tags clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-tags maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: tags mostlyclean-tags distclean-tags clean-tags \ +maintainer-clean-tags distdir info-am info dvi-am dvi check check-am \ +installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/include/io.h b/include/io.h new file mode 100644 index 0000000..3ffbbfe --- /dev/null +++ b/include/io.h @@ -0,0 +1,57 @@ +struct buffer_head { + unsigned long b_blocknr; + unsigned short b_dev; + unsigned long b_size; + char * b_data; + unsigned long b_state; + unsigned int b_count; + unsigned int b_list ; + void (*b_end_io)(struct buffer_head *bh, int uptodate); + + struct buffer_head * b_next; + struct buffer_head * b_prev; + struct buffer_head * b_hash_next; + struct buffer_head * b_hash_prev; +}; + +#define BH_Uptodate 0 +#define BH_Dirty 1 +#define BH_Lock 2 + + +#define buffer_uptodate(bh) test_bit(BH_Uptodate, &(bh)->b_state) +#define buffer_dirty(bh) test_bit(BH_Dirty, &(bh)->b_state) +#define buffer_locked(bh) test_bit(BH_Lock, &(bh)->b_state) +#define buffer_clean(bh) !test_bit(BH_Dirty, &(bh)->b_state) +#define mark_buffer_dirty(bh) set_bit(BH_Dirty, &(bh)->b_state) +/* +printf ("%s:%s:%u %p %p %p\n", +__FILE__, __FUNCTION__, __LINE__, + __builtin_return_address (0), + __builtin_return_address (1), + __builtin_return_address (2)); +*/ + +#define mark_buffer_uptodate(bh,i) set_bit(BH_Uptodate, &(bh)->b_state) +#define mark_buffer_clean(bh) clear_bit(BH_Dirty, &(bh)->b_state) + + +void __wait_on_buffer (struct buffer_head * bh); +struct buffer_head * getblk (int dev, int block, int size); +struct buffer_head * reiserfs_getblk (int dev, int block, int size, int *repeat); + +struct buffer_head * find_buffer (int dev, int block, int size); +struct buffer_head * get_hash_table(dev_t dev, int block, int size); +struct buffer_head * bread (int dev, unsigned long block, size_t size); +struct buffer_head * reiserfs_bread (int dev, int block, int size, int *repeat); +int valid_offset (int fd, loff_t offset); +int bwrite (struct buffer_head * bh); +void brelse (struct buffer_head * bh); +void bforget (struct buffer_head * bh); +void check_and_free_buffer_mem (void); + +void flush_buffers (void); +void free_buffers (void); + +loff_t reiserfs_llseek (unsigned int fd, loff_t offset, unsigned int origin); + diff --git a/include/misc.h b/include/misc.h new file mode 100644 index 0000000..91df4db --- /dev/null +++ b/include/misc.h @@ -0,0 +1,42 @@ +/* + * Copyright 1996-2000 Hans Reiser + */ + +/* nothing abount reiserfs here */ + +void die (char * fmt, ...); +void * getmem (int size); +void freemem (void * p); +void checkmem (char * p, int size); +void * expandmem (void * p, int size, int by); +int is_mounted (char * device_name); +int is_mounted_read_only (char * device_name); +void check_and_free_mem (void); +char * kdevname (int dev); + + +int set_bit (int nr, void * addr); +int clear_bit (int nr, void * addr); +int test_bit(int nr, const void * addr); +int find_first_zero_bit (const void *vaddr, unsigned size); +int find_next_zero_bit (const void *vaddr, unsigned size, unsigned offset); + +void print_how_far (unsigned long * passed, unsigned long total, int inc, int quiet); +void print_how_fast (unsigned long total, + unsigned long passed, int cursor_pos); +int user_confirmed (char * q, char * yes); + + +/* +int test_and_set_bit (int nr, void * addr); +int test_and_clear_bit (int nr, void * addr); +*/ +inline __u32 cpu_to_le32 (__u32 val); +inline __u32 le32_to_cpu (__u32 val); +inline __u16 cpu_to_le16 (__u16 val); +inline __u16 le16_to_cpu (__u16 val); +inline __u64 cpu_to_le64 (__u64 val); +inline __u64 le64_to_cpu (__u64 val); + +unsigned long count_blocks (char * filename, int blocksize, int fd); + diff --git a/include/reiserfs_fs.h b/include/reiserfs_fs.h new file mode 100644 index 0000000..101a151 --- /dev/null +++ b/include/reiserfs_fs.h @@ -0,0 +1,1392 @@ +/* + * Copyright 1996-2001 by Hans Reiser, licensing governed by reiserfs/README + */ + +/* + * include/linux/reiser_fs.h + * + * Reiser File System constants and structures + * + */ + +/* in reading the #defines, it may help to understand that they employ + the following abbreviations: + +B = Buffer +I = Item header +H = Height within the tree (should be changed to LEV) +N = Number of the item in the node +STAT = stat data +DEH = Directory Entry Header +EC = Entry Count +E = Entry number +UL = Unsigned Long +BLKH = BLocK Header +UNFM = UNForMatted node +DC = Disk Child +P = Path + +These #defines are named by concatenating these abbreviations, where +first comes the arguments, and last comes the return value, of the +macro. + +*/ + +#include <limits.h> + +/* NEW_GET_NEW_BUFFER will try to allocate new blocks better */ +/*#define NEW_GET_NEW_BUFFER*/ +#define OLD_GET_NEW_BUFFER + +/* n must be power of 2 */ +#define _ROUND_UP(x,n) (((x)+(n)-1u) & ~((n)-1u)) + +// to be ok for alpha and others we have to align structures to 8 byte +// boundary. +// FIXME: do not change 4 by anything else: there is code which relies on that +#define ROUND_UP(x) _ROUND_UP(x,8LL) + + + + +/***************************************************************************/ +/* SUPER BLOCK */ +/***************************************************************************/ + +#define UNSET_HASH 0 // read_super will guess about, what hash names + // in directories were sorted with +#define TEA_HASH 1 +#define YURA_HASH 2 +#define R5_HASH 3 +#define DEFAULT_HASH R5_HASH + + +// can be used only to complete indirect to direct convertion and for +// nothing else +#define RESERVED_SPACE 20 + + +/* super block of prejournalled version */ +struct reiserfs_super_block_v0 +{ + __u32 s_block_count; + __u32 s_free_blocks; + __u32 s_root_block; + __u16 s_blocksize; + __u16 s_oid_maxsize; + __u16 s_oid_cursize; + __u16 s_state; + char s_magic[16]; + __u16 s_tree_height; + __u16 s_bmap_nr; + __u16 s_reserved; +}; + + +/* this is the super from 3.5.X, where X >= 10 */ +struct reiserfs_super_block_v1 +{ + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + __u32 s_journal_block; /* journal block number */ + __u32 s_journal_dev; /* journal device number */ + __u32 s_orig_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ + __u32 s_journal_trans_max ; /* max number of blocks in a transaction. */ + __u32 s_journal_block_count ; /* total size of the journal. can change over time */ + __u32 s_journal_max_batch ; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age ; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age ; /* in seconds, how old can a transaction be */ + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array, see get_objectid() commentary */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_state; /* valid or error */ + char s_magic[10]; /* reiserfs magic string indicates that file system is reiserfs */ + __u16 s_fsck_state; /* when fsck managed to build the tree - it puts */ + __u32 s_hash_function_code; /* indicate, what hash fuction is being use to sort names in a directory*/ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ + __u16 s_version; +}; + +#define SB_SIZE_V1 (sizeof(struct reiserfs_super_block_v1)) /* 76 bytes */ + + +/* Structure of super block on disk, a version of which in RAM is often + accessed as s->u.reiserfs_sb.s_rs the version in RAM is part of a larger + structure containing fields never written to disk. */ +struct reiserfs_super_block +{ + struct reiserfs_super_block_v1 s_v1; + char s_unused[128] ; /* zero filled by mkreiserfs */ +}; + +#define SB_SIZE (sizeof(struct reiserfs_super_block)) /* 204 bytes */ + + +typedef __u32 (*hashf_t) (const char *, int); + + + + + + +#define SB_BUFFER_WITH_SB(s) ((s)->s_sbh) +#define SB_AP_BITMAP(s) ((s)->s_ap_bitmap) + +#define SB_DISK_SUPER_BLOCK(s) (&((s)->s_rs->s_v1)) +#define SB_JOURNAL_BLOCK(s) le32_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_journal_block)) +#define SB_JOURNAL_SIZE(s) le32_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_orig_journal_size)) +#define SB_BLOCK_COUNT(s) le32_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_block_count)) +#define SB_FREE_BLOCKS(s) le32_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_free_blocks)) +#define SB_REISERFS_MAGIC(s) (SB_DISK_SUPER_BLOCK(s)->s_magic) +#define SB_ROOT_BLOCK(s) le32_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_root_block)) +#define SB_TREE_HEIGHT(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_tree_height)) +#define SB_REISERFS_STATE(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_state)) +#define SB_VERSION(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_version)) +#define SB_BMAP_NR(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_bmap_nr)) +#define SB_OBJECTID_MAP_SIZE(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_oid_cursize)) +#define SB_OBJECTID_MAP_MAXSIZE(s) le16_to_cpu ((SB_DISK_SUPER_BLOCK(s)->s_oid_maxsize)) + +#define rs_blocksize(rs) le16_to_cpu ((rs)->s_v1.s_blocksize) +#define set_blocksize(rs,n) ((rs)->s_v1.s_blocksize = cpu_to_le16 (n)) + +#define rs_block_count(rs) le32_to_cpu ((rs)->s_v1.s_block_count) +#define set_block_count(rs,n) ((rs)->s_v1.s_block_count = cpu_to_le32 (n)) + +#define rs_journal_dev(rs) le32_to_cpu ((rs)->s_v1.s_journal_dev) +#define set_journal_dev(rs,n) ((rs)->s_v1.s_journal_dev = cpu_to_le32 (n)) + +#define rs_journal_start(rs) le32_to_cpu ((rs)->s_v1.s_journal_block) +#define set_journal_start(rs,n) ((rs)->s_v1.s_journal_block = cpu_to_le32 (n)) + +#define rs_journal_size(rs) le32_to_cpu((rs)->s_v1.s_orig_journal_size) +#define set_journal_size(rs,n) ((rs)->s_v1.s_orig_journal_size = cpu_to_le32(n)) + +#define rs_root_block(rs) le32_to_cpu ((rs)->s_v1.s_root_block) +#define set_root_block(rs,n) ((rs)->s_v1.s_root_block = cpu_to_le32 (n)) + +#define rs_tree_height(rs) le16_to_cpu ((rs)->s_v1.s_tree_height) +#define set_tree_height(rs,n) ((rs)->s_v1.s_tree_height = cpu_to_le16 (n)) + +#define rs_free_blocks(rs) le32_to_cpu ((rs)->s_v1.s_free_blocks) +#define set_free_blocks(rs,n) ((rs)->s_v1.s_free_blocks = cpu_to_le32 (n)) + +#define rs_bmap_nr(rs) le16_to_cpu ((rs)->s_v1.s_bmap_nr) +#define set_bmap_nr(rs,n) ((rs)->s_v1.s_bmap_nr = cpu_to_le16 (n)) + +#define rs_state(rs) le16_to_cpu ((rs)->s_v1.s_state) +#define set_state(rs,n) ((rs)->s_v1.s_state = cpu_to_le16 (n)) + +#define rs_objectid_map_size(rs) (le16_to_cpu ((rs)->s_v1.s_oid_cursize)) +#define set_objectid_map_size(rs,n) ((rs)->s_v1.s_oid_cursize = cpu_to_le16 (n)) + +#define rs_objectid_map_max_size(rs) (le16_to_cpu ((rs)->s_v1.s_oid_maxsize)) +#define set_objectid_map_max_size(rs,n) ((rs)->s_v1.s_oid_maxsize = cpu_to_le16 (n)) + +#define rs_hash(rs) (le32_to_cpu ((rs)->s_v1.s_hash_function_code)) +#define set_hash(rs,n) ((rs)->s_v1.s_hash_function_code = cpu_to_le32 (n)) + +#define rs_version(rs) (le16_to_cpu ((rs)->s_v1.s_version)) +#define set_version(rs,n) ((rs)->s_v1.s_version = cpu_to_le16 (n)) + +#define TREE_IS_BUILT 0xfaaf +#define fsck_state(rs) le16_to_cpu (((rs)->s_v1.s_fsck_state)) +#define set_fsck_state(rs,n) ((rs)->s_v1.s_fsck_state = cpu_to_le16 (n)) + + +#define sb_size(fs) (fs->s_version == REISERFS_VERSION_2) ? SB_SIZE : SB_SIZE_V1 + +/* struct stat_data* access macros */ +/* v1 */ +#define sd_v1_mode(sd) (le16_to_cpu((sd)->sd_mode)) +#define set_sd_v1_mode(sd,n) ((sd)->sd_mode = cpu_to_le16((n))) +#define sd_v1_nlink(sd) (le16_to_cpu((sd)->sd_nlink)) +#define set_sd_v1_nlink(sd,n) ((sd)->sd_nlink = cpu_to_le16((n))) +#define sd_v1_uid(sd) (le16_to_cpu((sd)->sd_uid)) +#define set_sd_v1_uid(sd,n) ((sd)->sd_uid = cpu_to_le16((n))) +#define sd_v1_gid(sd) (le16_to_cpu((sd)->sd_gid)) +#define set_sd_v1_gid(sd,n) ((sd)->sd_gid = cpu_to_le16((n))) +#define sd_v1_size(sd) (le32_to_cpu((sd)->sd_size)) +#define set_sd_v1_size(sd,n) ((sd)->sd_size = cpu_to_le32((n))) +#define sd_v1_atime(sd) (le32_to_cpu((sd)->sd_atime)) +#define set_sd_v1_atime(sd,n) ((sd)->sd_atime = cpu_to_le32((n))) +#define sd_v1_mtime(sd) (le32_to_cpu((sd)->sd_mtime)) +#define set_sd_v1_mtime(sd,n) ((sd)->sd_mtime = cpu_to_le32((n))) +#define sd_v1_ctime(sd) (le32_to_cpu((sd)->sd_ctime)) +#define set_sd_v1_ctime(sd,n) ((sd)->sd_ctime = cpu_to_le32((n))) +#define sd_v1_blocks(sd) (le32_to_cpu((sd)->u.sd_blocks)) +#define set_sd_v1_blocks(sd,n) ((sd)->u.sd_blocks = cpu_to_le32((n))) +#define sd_v1_rdev(sd) (le32_to_cpu((sd)->u.sd_rdev)) +#define set_sd_v1_rdev(sd,n) ((sd)->u.sd_rdev = cpu_to_le32((n))) +#define sd_v1_first_direct_byte(sd) (le32_to_cpu((sd)->sd_first_direct_byte)) +#define set_sd_v1_first_direct_byte(sd,n) \ + ((sd)->sd_first_direct_byte = cpu_to_le32((n))) +/* v2 */ +#define sd_v2_mode(sd) (le16_to_cpu((sd)->sd_mode)) +#define set_sd_v2_mode(sd,n) ((sd)->sd_mode = cpu_to_le16((n))) +#define sd_v2_reserved(sd) (le16_to_cpu((sd)->sd_reserved)) +#define set_sd_v2_reserved(sd,n) ((sd)->sd_reserved = cpu_to_le16((n))) +#define sd_v2_nlink(sd) (le32_to_cpu((sd)->sd_nlink)) +#define set_sd_v2_nlink(sd,n) ((sd)->sd_nlink = cpu_to_le32((n))) +#define sd_v2_size(sd) (le64_to_cpu((sd)->sd_size)) +#define set_sd_v2_size(sd,n) ((sd)->sd_size = cpu_to_le64((n))) +#define sd_v2_uid(sd) (le32_to_cpu((sd)->sd_uid)) +#define set_sd_v2_uid(sd,n) ((sd)->sd_uid = cpu_to_le32((n))) +#define sd_v2_gid(sd) (le32_to_cpu((sd)->sd_gid)) +#define set_sd_v2_gid(sd,n) ((sd)->sd_gid = cpu_to_le32((n))) +#define sd_v2_atime(sd) (le32_to_cpu((sd)->sd_atime)) +#define set_sd_v2_atime(sd,n) ((sd)->sd_atime = cpu_to_le32((n))) +#define sd_v2_mtime(sd) (le32_to_cpu((sd)->sd_mtime)) +#define set_sd_v2_mtime(sd,n) ((sd)->sd_mtime = cpu_to_le32((n))) +#define sd_v2_ctime(sd) (le32_to_cpu((sd)->sd_ctime)) +#define set_sd_v2_ctime(sd,n) ((sd)->sd_ctime = cpu_to_le32((n))) +#define sd_v2_blocks(sd) (le32_to_cpu((sd)->sd_blocks)) +#define set_sd_v2_blocks(sd,n) ((sd)->sd_blocks = cpu_to_le32((n))) +#define sd_v2_rdev(sd) (le32_to_cpu((sd)->u.sd_rdev)) +#define set_sd_v2_rdev(sd,n) ((sd)->u.sd_rdev = cpu_to_le32((n))) + + +/* ReiserFS leaves the first 64k unused, so that partition labels have enough + space. If someone wants to write a fancy bootloader that needs more than + 64k, let us know, and this will be increased in size. This number must be + larger than than the largest block size on any platform, or code will + break. -Hans */ +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) + +/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) + +/* prejournalled reiserfs had signature in the other place in super block */ +#define REISERFS_SUPER_MAGIC_STRING_OFFSET_NJ 20 + +/* f_type of struct statfs will be set at this value by statfs(2) */ +#define REISERFS_SUPER_MAGIC 0x52654973 + +/* various reiserfs signatures. We have 2 so far. ReIsErFs for the system + which is not able to deal with long files and ReIsEr2Fs for another. Those + signature should be looked for at the 64-th and at the 8-th 1k block of the + device */ +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" + + + +/* values for s_version field of struct reiserfs_super_block */ +#define REISERFS_VERSION_1 0 /* old (short) super block, all keys in old + format */ +#define REISERFS_VERSION_2 2 /* new super block, keys may be in new format */ + +/* + * values for s_state field + */ +#define REISERFS_VALID_FS 1 +#define REISERFS_ERROR_FS 2 + + + +/***************************************************************************/ +/* JOURNAL */ +/***************************************************************************/ + +#define JOURNAL_DESC_MAGIC "ReIsErLB" /* ick. magic string to find desc blocks in the journal */ +#define JOURNAL_TRANS_MAX 1024 /* biggest possible single transaction, don't change for now (8/3/99) */ + +/* journal.c see journal.c for all the comments here */ + +#define JOURNAL_TRANS_HALF 1018 /* must be correct to keep the desc and commit structs at 4k */ + +/* first block written in a commit. BUG, not 64bit safe */ +struct reiserfs_journal_desc { + __u32 j_trans_id ; /* id of commit */ + __u32 j_len ; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id ; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF] ; /* real locations for each block */ + char j_magic[12] ; +} ; + +/* last block written in a commit BUG, not 64bit safe */ +struct reiserfs_journal_commit { + __u32 j_trans_id ; /* must match j_trans_id from the desc block */ + __u32 j_len ; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF] ; /* real locations for each block */ + char j_digest[16] ; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +} ; + +/* this header block gets written whenever a transaction is considered fully flushed, and is more recent than the +** last fully flushed transaction. fully flushed means all the log blocks and all the real blocks are on disk, +** and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + __u32 j_last_flush_trans_id ; /* id of last fully flushed transaction */ + __u32 j_first_unflushed_offset ; /* offset in the log of where to start replay after a crash */ + __u32 j_mount_id ; +} ; + + +#define JOURNAL_BLOCK_COUNT 8192 /* number of blocks in the journal */ + + + +#define bh_desc(bh) ((struct reiserfs_journal_desc *)((bh)->b_data)) +#define bh_commit(bh) ((struct reiserfs_journal_commit *)((bh)->b_data)) + + +/***************************************************************************/ +/* KEY & ITEM HEAD */ +/***************************************************************************/ + +struct offset_v1 { + __u32 k_offset; + __u32 k_uniqueness; +} __attribute__ ((__packed__)); + +struct offset_v2 { + __u64 k_offset:60; + __u64 k_type: 4; // TYPE_STAT_DATA | TYPE_INDIRECT | TYPE_DIRECT | TYPE_DIRENTRY +} __attribute__ ((__packed__)); + + +/* Key of the object drop determines its location in the S+tree, and is + composed of 4 components */ +struct key { + __u32 k_dir_id; /* packing locality: by default parent directory object + id */ + __u32 k_objectid; /* object identifier */ + union { + struct offset_v1 k_offset_v1; + struct offset_v2 k_offset_v2; + } u; +} __attribute__ ((__packed__)); + +#define key_dir_id(key) (le32_to_cpu((key)->k_dir_id)) +#define set_key_dir_id(key,n) ((key)->k_dir_id = cpu_to_le32((n))) +#define key_objectid(key) (le32_to_cpu((key)->k_objectid)) +#define set_key_objectid(key,n) ((key)->k_objectid = cpu_to_le32((n))) + + +#define KEY_SIZE (sizeof(struct key)) +#define SHORT_KEY_SIZE 8 + + +// values for k_uniqueness field of the struct key +#define V1_SD_UNIQUENESS 0 +#define V1_DIRENTRY_UNIQUENESS 500 +#define DIRENTRY_UNIQUENESS 500 +#define V1_DIRECT_UNIQUENESS 0xffffffff +#define V1_INDIRECT_UNIQUENESS 0xfffffffe +#define V1_UNKNOWN_UNIQUENESS 555 + +// values for k_type field of the struct key +#define TYPE_STAT_DATA 0 +#define TYPE_INDIRECT 1 +#define TYPE_DIRECT 2 +#define TYPE_DIRENTRY 3 + +#define TYPE_UNKNOWN 15 + + +#define KEY_FORMAT_1 0 +#define KEY_FORMAT_2 1 + + + + + + + /* Our function for comparing keys can compare keys of different + lengths. It takes as a parameter the length of the keys it is to + compare. These defines are used in determining what is to be + passed to it as that parameter. */ +#define REISERFS_FULL_KEY_LEN 4 + +#define REISERFS_SHORT_KEY_LEN 2 + + +/* Everything in the filesystem is stored as a set of items. The item head + contains the key of the item, its free space (for indirect items) and + specifies the location of the item itself within the block. */ + +struct item_head +{ + struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ + + union { + __u16 ih_free_space1; /* The free space in the last unformatted node of + an indirect item if this is an indirect item. + This equals 0xFFFF iff this is a direct item + or stat data item. Note that the key, not this + field, is used to determine the item type, and + thus which field this union contains. */ + __u16 ih_entry_count; /* Iff this is a directory item, this field + equals the number of directory entries in the + directory item. */ + } u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body within the + block */ + struct { + __u16 key_format : 12; /* KEY_FORMAT_1 or KEY_FORMAT_2. This is not + necessary, but we have space, let use it */ + __u16 fsck_need : 4; /* fsck set here its flag (reachable/unreachable) */ + } ih_format; +} __attribute__ ((__packed__)); + + +/* size of item header */ +#define IH_SIZE (sizeof(struct item_head)) + + +#define ih_item_len(ih) (le16_to_cpu((ih)->ih_item_len)) +#define set_ih_item_len(ih,x) ((ih)->ih_item_len = cpu_to_le16 (x)) + +#define ih_location(ih) (le16_to_cpu ((ih)->ih_item_location)) +#define set_ih_location(ih,x) ((ih)->ih_item_location = cpu_to_le16 (x)) + +#define ih_key_format(ih) (le16_to_cpu ((ih)->ih_format.key_format)) +#define set_key_format(ih,x) ((ih)->ih_format.key_format = cpu_to_le16 (x)) + + +// FIXME: ih_free_space does not appear to be very important, but we +// have to make sure that earlier version have no trouble when +// ih_free_space is 0 +#define ih_free_space(ih) 0 // le16_to_cpu (ih->u.ih_free_space) +#define set_free_space(ih,val) ((ih)->u.ih_free_space1 = 0)//val) + +//#define get_ih_free_space(ih) 0 //(ih_key_format (ih) == KEY_FORMAT ? 0 : ih_free_space (ih)) +//#define set_ih_free_space(ih,val) // (ih_free_space (ih) = (ih_version (ih) == ITEM_VERSION_2 ? 0 : val)) + +#define ih_entry_count(ih) (le16_to_cpu ((ih)->u.ih_entry_count)) +//#define set_ih_free_space(ih,x) ((ih)->u.ih_free_space = cpu_to_le16(x)) +#define set_entry_count(ih,x) ((ih)->u.ih_entry_count = cpu_to_le16(x)) + +#define I_K_KEY_IN_ITEM(p_s_ih, p_s_key, n_blocksize) \ + ( ! not_of_one_file(p_s_ih, p_s_key) && \ + I_OFF_BYTE_IN_ITEM(p_s_ih, get_offset (p_s_key), n_blocksize) ) + +#define IH_Bad 0 +#define IH_Unreachable 1 + +/* Bad item flag is set temporary by recover_leaf */ +#define mark_ih_bad(ih) ((ih)->ih_format.fsck_need |= IH_Bad) +#define ih_bad(ih) test_bit (IH_Bad, &((ih)->ih_format.fsck_need)) +#define unmark_item_bad(ih) clear_bit (IH_Bad, &((ih)->ih_format.fsck_need)) + +/* Unreachable bit is set on tree rebuilding and is cleared in semantic pass */ +#define mark_ih_ok(ih) ((ih)->ih_format.fsck_need = 0) +#define ih_reachable(ih) (!(ih)->ih_format.fsck_need & IH_Unreachable) +#define mark_ih_unreachable(ih) ((ih)->ih_format.fsck_need |= IH_Unreachable) + + + +/* maximal length of item */ +#define MAX_ITEM_LEN(block_size) (block_size - BLKH_SIZE - IH_SIZE) +#define MIN_ITEM_LEN 1 + + +/* object identifier for root dir */ +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +/*extern struct key root_key;*/ + + +/* + * Picture represents a leaf of the S+tree + * ______________________________________________________ + * | | Array of | | | + * |Block | Object-Item | F r e e | Objects- | + * | head | Headers | S p a c e | Items | + * |______|_______________|___________________|___________| + */ + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head { + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + __u16 blk_reserved; + struct key not_used; /* Right delimiting key for this block + (supported for leaf level nodes only) */ +}; + +#define BLKH_SIZE (sizeof(struct block_head)) + +/* + * values for blk_type field + */ + +#define FREE_LEVEL 0 /* Node of this level is out of the tree. */ + +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + + +#define is_leaf_block_head(buf) (le16_to_cpu (((struct block_head *)(buf))->blk_level) == DISK_LEAF_NODE_LEVEL) +#define is_internal_block_head(buf) \ +((le16_to_cpu (((struct block_head *)(buf))->blk_level) > DISK_LEAF_NODE_LEVEL) &&\ + (le16_to_cpu (((struct block_head *)(buf))->blk_level) <= MAX_HEIGHT)) + + +/* Given the buffer head of a formatted node, resolve to the block head of that node. */ +#define B_BLK_HEAD(p_s_bh) ((struct block_head *)((p_s_bh)->b_data)) + +/* Number of items that are in buffer. */ +#define node_item_number(bh) (le16_to_cpu ( B_BLK_HEAD(bh)->blk_nr_item )) +#define node_pointer_number(bh) (node_item_number(bh) + 1) +#define node_level(bh) (le16_to_cpu ( B_BLK_HEAD(bh)->blk_level )) +#define node_free_space(bh) (le16_to_cpu ( B_BLK_HEAD(bh)->blk_free_space )) + +#define set_node_item_number(bh,n) (B_BLK_HEAD(bh)->blk_nr_item = cpu_to_le16 (n)) +#define set_node_free_space(bh,n) (B_BLK_HEAD(bh)->blk_free_space = cpu_to_le16 (n)) +#define set_node_level(bh,n) (B_BLK_HEAD(bh)->blk_level = cpu_to_le16 (n)) +#define set_leaf_node_level(bh) set_node_level (bh, DISK_LEAF_NODE_LEVEL) + +#define B_NR_ITEMS(bh) node_item_number(bh) +#define B_LEVEL(bh) node_level(bh) +#define B_FREE_SPACE(bh) node_free_space(bh) + + +#define is_leaf_node(bh) is_leaf_block_head ((bh)->b_data) +#define is_internal_node(bh) is_internal_block_head ((bh)->b_data) + + + + + + +/***************************************************************************/ +/* STAT DATA */ +/***************************************************************************/ + +/* Stat Data on disk (reiserfs version of UFS disk inode minus the address blocks) */ + +/* The sense of adding union to stat data is to keep a value of real number of + blocks used by file. The necessity of adding such information is caused by + existing of files with holes. Reiserfs should keep number of used blocks + for file, but not calculate it from file size (that is not correct for + holed files). Thus we have to add additional information to stat data. + When we have a device special file, there is no need to get number of used + blocks for them, and, accordingly, we doesn't need to keep major and minor + numbers for regular files, which might have holes. So this field is being + overloaded. */ + +struct stat_data_v1 { + __u16 sd_mode; /* file type, permissions */ + __u16 sd_nlink; /* number of hard links */ + __u16 sd_uid; /* owner */ + __u16 sd_gid; /* group */ + __u32 sd_size; /* file size */ + __u32 sd_atime; /* time of last access */ + __u32 sd_mtime; /* time file was last modified */ + __u32 sd_ctime; /* time inode (stat data) was last changed (except + changes to sd_atime and sd_mtime) */ + union { + __u32 sd_rdev; + __u32 sd_blocks; /* number of blocks file uses */ + } u; + __u32 sd_first_direct_byte; /* first byte of file which is stored + in a direct item: except that if it + equals 1 it is a symlink and if it + equals MAX_KEY_OFFSET there is no + direct item. The existence of this + field really grates on me. Let's + replace it with a macro based on + sd_size and our tail suppression + policy. Someday. -Hans */ +} __attribute__ ((__packed__)); +#define SD_V1_SIZE (sizeof(struct stat_data_v1)) + + +/* this is used to check sd_size of stat data v1 */ +#define MAX_FILE_SIZE_V1 0x7fffffff + + +// sd_first_direct_byte is set to this when there are no direct items in a +// file +#define NO_BYTES_IN_DIRECT_ITEM 0xffffffff + + +/* Stat Data on disk (reiserfs version of UFS disk inode minus the + address blocks) */ +struct stat_data { + __u16 sd_mode; /* file type, permissions */ + __u16 sd_reserved; + __u32 sd_nlink; /* 32 bit nlink! */ + __u64 sd_size; /* 64 bit size!*/ + __u32 sd_uid; /* 32 bit uid! */ + __u32 sd_gid; /* 32 bit gid! */ + __u32 sd_atime; /* time of last access */ + __u32 sd_mtime; /* time file was last modified */ + __u32 sd_ctime; /* time inode (stat data) was last changed (except + changes to sd_atime and sd_mtime) */ + __u32 sd_blocks; + union { + __u32 sd_rdev; + //__u32 sd_first_direct_byte; + /* first byte of file which is stored in a direct item: except that if + it equals 1 it is a symlink and if it equals ~(__u32)0 there is no + direct item. The existence of this field really grates on me. Let's + replace it with a macro based on sd_size and our tail suppression + policy? */ + } u; +} __attribute__ ((__packed__)); +// +// this is 44 bytes long +// +#define SD_SIZE (sizeof(struct stat_data)) + +// there are two ways: to check length of item or ih_version field +// (for old stat data it is set to 0 (KEY_FORMAT_1)) +#define stat_data_v1(ih) (ih_key_format (ih) == KEY_FORMAT_1) + +/* this is used to check sd_size of stat data v2: max offset which can + be reached with a key of format 2 is 60 bits */ +#define MAX_FILE_SIZE_V2 0xfffffffffffffffLL + + +/***************************************************************************/ +/* DIRECTORY STRUCTURE */ +/***************************************************************************/ +/* + Picture represents the structure of directory items + ________________________________________________ + | Array of | | | | | | + | directory |N-1| N-2 | .... | 1st |0th| + | entry headers | | | | | | + |_______________|___|_____|________|_______|___| + <---- directory entries ------> + + First directory item has k_offset component 1. We store "." and ".." + in one item, always, we never split "." and ".." into differing + items. This makes, among other things, the code for removing + directories simpler. */ +#define SD_OFFSET 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 + +/* */ +#define FIRST_ITEM_OFFSET 1 + +/* + Q: How to get key of object pointed to by entry from entry? + + A: Each directory entry has its header. This header has deh_dir_id + and deh_objectid fields, those are key of object, entry points to */ + +/* NOT IMPLEMENTED: + Directory will someday contain stat data of object */ + + + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the + object, that is referenced by directory entry */ + __u32 deh_objectid;/* objectid of the object, that is referenced by + directory entry */ + __u16 deh_location;/* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for + future), and 2) whether entry is hidden + (unlinked) */ +} __attribute__ ((__packed__)); +#define DEH_SIZE sizeof(struct reiserfs_de_head) + + +#define deh_offset(deh) (le32_to_cpu ((deh)->deh_offset)) +#define deh_dir_id(deh) (le32_to_cpu ((deh)->deh_dir_id)) +#define deh_objectid(deh) (le32_to_cpu ((deh)->deh_objectid)) +#define deh_location(deh) (le16_to_cpu ((deh)->deh_location)) +#define deh_state(deh) (le16_to_cpu ((deh)->deh_state)) + +/* empty directory contains two entries "." and ".." and their headers */ +#define EMPTY_DIR_SIZE \ +(DEH_SIZE * 2 + ROUND_UP (strlen (".")) + ROUND_UP (strlen (".."))) + +/* old format directories have this size when empty */ +#define EMPTY_DIR_SIZE_V1 (DEH_SIZE * 2 + 3) + +#define DEH_Statdata 0 /* not used now */ +#define DEH_Visible 2 + +#define DEH_Bad_offset 4 /* fsck marks entries to be deleted with this flag */ +#define DEH_Bad_location 5 + +#define mark_de_with_sd(deh) set_bit (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_without_sd(deh) clear_bit (DEH_Statdata, &((deh)->deh_state)) +#define mark_de_visible(deh) set_bit (DEH_Visible, &((deh)->deh_state)) +#define mark_de_hidden(deh) clear_bit (DEH_Visible, &((deh)->deh_state)) +#define mark_de_lost_found(deh) set_bit (DEH_Lost_Found, &((deh)->deh_state)) +#define unmark_de_lost_found(deh) clear_bit (DEH_Lost_Found, &((deh)->deh_state)) + +#define de_with_sd(deh) test_bit (DEH_Statdata, &((deh)->deh_state)) +#define de_visible(deh) test_bit (DEH_Visible, &((deh)->deh_state)) +#define de_hidden(deh) !test_bit (DEH_Visible, &((deh)->deh_state)) + +/* Bad means "hashed unproperly or/and invalid location" */ +#define de_bad_location(deh) test_bit (DEH_Bad_location, &((deh)->deh_state)) +#define mark_de_bad_location(deh) set_bit (DEH_Bad_location, &((deh)->deh_state)) +#define mark_de_good_location(deh) clear_bit (DEH_Bad_location, &((deh)->deh_state)) + +#define de_bad_offset(deh) test_bit (DEH_Bad_offset, &((deh)->deh_state)) +#define mark_de_bad_offset(deh) set_bit (DEH_Bad_offset, &((deh)->deh_state)) + +#define de_bad(deh) (de_bad_location(deh) || de_bad_offset(deh)) + + +/* for directories st_blocks is number of 512 byte units which fit into dir + size round up to blocksize */ +#define dir_size2st_blocks(blocksize,size) \ +((((size) + (blocksize) - 1) / (blocksize)) * ((blocksize) / 512)) + + +/* array of the entry headers */ +#define B_I_DEH(bh,ih) ((struct reiserfs_de_head *)(B_I_PITEM(bh,ih))) + +#define REISERFS_MAX_NAME_LEN(block_size) (block_size - BLKH_SIZE - IH_SIZE - DEH_SIZE) /* -SD_SIZE when entry will contain stat data */ + +/* this structure is used for operations on directory entries. It is + not a disk structure. */ +/* When reiserfs_find_entry or search_by_entry_key find directory + entry, they return filled reiserfs_dir_entry structure */ +struct reiserfs_dir_entry +{ + struct buffer_head * de_bh; + int de_item_num; + struct item_head * de_ih; + int de_entry_num; + struct reiserfs_de_head * de_deh; + int de_entrylen; + int de_namelen; + char * de_name; + char * de_gen_number_bit_string; + + __u32 de_dir_id; + __u32 de_objectid; + + struct key de_entry_key; +}; + + +/* hash value occupies 24 bits starting from 7 up to 30 */ +#define GET_HASH_VALUE(offset) ((offset) & 0x7fffff80) +/* generation number occupies 7 bits starting from 0 up to 6 */ +#define GET_GENERATION_NUMBER(offset) ((offset) & 0x0000007f) + + +/* + * Picture represents an internal node of the reiserfs tree + * ______________________________________________________ + * | | Array of | Array of | Free | + * |block | keys | pointers | space | + * | head | N | N+1 | | + * |______|_______________|___________________|___________| + */ + +/***************************************************************************/ +/* DISK CHILD */ +/***************************************************************************/ +/* Disk child pointer: The pointer from an internal node of the tree + to a node that is on disk. */ +struct disk_child { + __u32 dc_block_number; /* Disk child's block number. */ + __u16 dc_size; /* Disk child's used space. */ + __u16 dc_reserved; +}; + +#define DC_SIZE (sizeof(struct disk_child)) + +/* Get disk child by buffer header and position in the tree node. */ +#define B_N_CHILD(p_s_bh,n_pos) ((struct disk_child *)\ + ((p_s_bh)->b_data + BLKH_SIZE + B_NR_ITEMS(p_s_bh) \ + * KEY_SIZE + DC_SIZE * (n_pos))) + +/* Get disk child number by buffer header and position in the tree node. */ +#define B_N_CHILD_NUM(p_s_bh,n_pos) (B_N_CHILD(p_s_bh,n_pos)->dc_block_number) +#define child_block_number(bh,pos) le32_to_cpu (B_N_CHILD(bh,pos)->dc_block_number) +#define child_block_size(bh,pos) le16_to_cpu (B_N_CHILD(bh,pos)->dc_size) + +#define set_dc_block_number(bh,pos,block) (B_N_CHILD (bh, pos)->dc_block_number = cpu_to_le32 (block)) + + /* maximal value of field child_size in structure disk_child */ + /* child size is the combined size of all items and their headers */ +#define MAX_CHILD_SIZE(bh) ((int)( (bh)->b_size - BLKH_SIZE )) + +/* amount of used space in buffer (not including block head) */ +#define B_CHILD_SIZE(cur) (MAX_CHILD_SIZE(cur)-(B_FREE_SPACE(cur))) + +/* max and min number of keys in internal node */ +#define MAX_NR_KEY(bh) ( (MAX_CHILD_SIZE(bh)-DC_SIZE)/(KEY_SIZE+DC_SIZE) ) +#define MIN_NR_KEY(bh) (MAX_NR_KEY(bh)/2) + + +/***************************************************************************/ +/* PATH STRUCTURES AND DEFINES */ +/***************************************************************************/ + +/* Search_by_key fills up the path from the root to the leaf as it + descends the tree looking for the key. It uses reiserfs_bread to + try to find buffers in the cache given their block number. If it + does not find them in the cache it reads them from disk. For each + node search_by_key finds using reiserfs_bread it then uses + bin_search to look through that node. bin_search will find the + position of the block_number of the next node if it is looking + through an internal node. If it is looking through a leaf node + bin_search will find the position of the item which has key either + equal to given key, or which is the maximal key less than the given + key. */ + +struct path_element { + struct buffer_head * pe_buffer; /* Pointer to the buffer at the path in + the tree. */ + int pe_position; /* Position in the tree node which is placed in the + buffer above. */ +}; + + +#define MAX_HEIGHT 5 /* maximal height of a tree. don't change this without changing JOURNAL_PER_BALANCE_CNT */ +#define EXTENDED_MAX_HEIGHT 7 /* Must be equals MAX_HEIGHT + FIRST_PATH_ELEMENT_OFFSET */ +#define FIRST_PATH_ELEMENT_OFFSET 2 /* Must be equal to at least 2. */ + +#define ILLEGAL_PATH_ELEMENT_OFFSET 1 /* Must be equal to FIRST_PATH_ELEMENT_OFFSET - 1 */ +#define MAX_FEB_SIZE 6 /* this MUST be MAX_HEIGHT + 1. See about FEB below */ + + + +/* We need to keep track of who the ancestors of nodes are. When we + perform a search we record which nodes were visited while + descending the tree looking for the node we searched for. This list + of nodes is called the path. This information is used while + performing balancing. Note that this path information may become + invalid, and this means we must check it when using it to see if it + is still valid. You'll need to read search_by_key and the comments + in it, especially about decrement_counters_in_path(), to understand + this structure. */ +struct path { + int path_length; /* Length of the array above. */ + struct path_element path_elements[EXTENDED_MAX_HEIGHT]; /* Array of the path elements. */ + int pos_in_item; +}; + +#define INITIALIZE_PATH(var) \ +struct path var = {ILLEGAL_PATH_ELEMENT_OFFSET, } + +/* Get path element by path and path position. */ +#define PATH_OFFSET_PELEMENT(p_s_path,n_offset) ((p_s_path)->path_elements +(n_offset)) + +/* Get buffer header at the path by path and path position. */ +#define PATH_OFFSET_PBUFFER(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_buffer) + +/* Get position in the element at the path by path and path position. */ +#define PATH_OFFSET_POSITION(p_s_path,n_offset) (PATH_OFFSET_PELEMENT(p_s_path,n_offset)->pe_position) + + +#define PATH_PLAST_BUFFER(p_s_path) (PATH_OFFSET_PBUFFER((p_s_path), (p_s_path)->path_length)) +#define PATH_LAST_POSITION(p_s_path) (PATH_OFFSET_POSITION((p_s_path), (p_s_path)->path_length)) + + +#define PATH_PITEM_HEAD(p_s_path) B_N_PITEM_HEAD(PATH_PLAST_BUFFER(p_s_path),PATH_LAST_POSITION(p_s_path)) + +/* in do_balance leaf has h == 0 in contrast with path structure, + where root has level == 0. That is why we need these defines */ +#define PATH_H_PBUFFER(p_s_path, h) PATH_OFFSET_PBUFFER (p_s_path, p_s_path->path_length - (h)) /* tb->S[h] */ +#define PATH_H_PPARENT(path, h) PATH_H_PBUFFER (path, (h) + 1) /* tb->F[h] or tb->S[0]->b_parent */ +#define PATH_H_POSITION(path, h) PATH_OFFSET_POSITION (path, path->path_length - (h)) +#define PATH_H_B_ITEM_ORDER(path, h) PATH_H_POSITION(path, h + 1) /* tb->S[h]->b_item_order */ + +#define PATH_H_PATH_OFFSET(p_s_path, n_h) ((p_s_path)->path_length - (n_h)) + +#define get_bh(path) PATH_PLAST_BUFFER(path) +#define get_ih(path) PATH_PITEM_HEAD(path) +#define get_item_pos(path) PATH_LAST_POSITION(path) +#define get_item(path) ((void *)B_N_PITEM(PATH_PLAST_BUFFER(path), PATH_LAST_POSITION (path))) +#define item_moved(ih,path) comp_items(ih, path) +#define path_changed(ih,path) comp_items (ih, path) + + +/***************************************************************************/ +/* MISC */ +/***************************************************************************/ + + +// search_by_key (and clones) and fix_nodes error code +#define CARRY_ON 0 +#define SCHEDULE_OCCURRED 1 +#define PATH_INCORRECT 2 +#define IO_ERROR 3 + +#define NO_DISK_SPACE 4 +#define NO_BALANCING_NEEDED 5 +#define ITEM_FOUND 6 +#define ITEM_NOT_FOUND 7 +#define POSITION_FOUND 8 +#define POSITION_NOT_FOUND 9 +#define GOTO_PREVIOUS_ITEM 10 +#define POSITION_FOUND_INVISIBLE 11 +#define FILE_NOT_FOUND 12 + +// used by fsck +#define DIRECTORY_NOT_FOUND 13 +#define REGULAR_FILE_FOUND 14 +#define DIRECTORY_FOUND 15 + + + +typedef unsigned long b_blocknr_t; +typedef __u32 unp_t; + +struct unfm_nodeinfo { + __u32 unfm_nodenum; + unsigned short unfm_freespace; +}; + +/* when reiserfs_file_write is called with a byte count >= MIN_PACK_ON_CLOSE, +** it sets the inode to pack on close, and when extending the file, will only +** use unformatted nodes. +** +** This is a big speed up for the journal, which is badly hurt by direct->indirect +** conversions (they must be logged). +*/ +#define MIN_PACK_ON_CLOSE 512 + + + /* This is an aggressive tail suppression policy, I am hoping it + improves our benchmarks. The principle behind it is that + percentage space saving is what matters, not absolute space + saving. This is non-intuitive, but it helps to understand it if + you consider that the cost to access 4 blocks is not much more + than the cost to access 1 block, if you have to do a seek and + rotate. A tail risks a non-linear disk access that is + significant as a percentage of total time cost for a 4 block file + and saves an amount of space that is less significant as a + percentage of space, or so goes the hypothesis. -Hans */ +#define STORE_TAIL_IN_UNFM(n_file_size,n_tail_size,n_block_size) \ +\ +( ((n_tail_size) > MAX_DIRECT_ITEM_LEN(n_block_size)) || \ + ( (n_file_size) >= (n_block_size) * 4 ) || \ + ( ( (n_file_size) >= (n_block_size) * 3 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/4) ) || \ + ( ( (n_file_size) >= (n_block_size) * 2 ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size))/2) ) || \ + ( ( (n_file_size) >= (n_block_size) ) && \ + ( (n_tail_size) >= (MAX_DIRECT_ITEM_LEN(n_block_size) * 3)/4) ) ) + + +#define first_direct_byte(inode) ((inode)->u.reiserfs_i.i_first_direct_byte) + +#define has_tail(inode) (first_direct_byte(inode) != NO_BYTES_IN_DIRECT_ITEM) + +#define tail_offset(inode) (first_direct_byte(inode) - 1) + +// mark file as not having tail stored in direct item +#define file_has_no_tail(inode) (first_direct_byte (inode) = NO_BYTES_IN_DIRECT_ITEM) + +#define block_size(inode) ((inode)->i_sb->s_blocksize) +#define file_size(inode) ((inode)->i_size) +#define tail_size(inode) (file_size (inode) & (block_size (inode) - 1)) + +#define tail_has_to_be_packed(inode) (!dont_have_tails ((inode)->i_sb) &&\ +!STORE_TAIL_IN_UNFM(file_size (inode), tail_size(inode), block_size (inode))) + + +/* Size of pointer to the unformatted node. */ +#define UNFM_P_SIZE (sizeof(__u32)) + +#define INODE_PKEY(inode) ((struct key *)((inode)->u.reiserfs_i.i_key)) +#define inode_key_format(inode) ((inode)->u.reiserfs_i.i_key_format) + +//#define MAX_UL_INT ULONG_MAX +//#define MAX_INT INT_MAX +//#define MAX_US_INT USHRT_MAX + +#define MAX_KEY1_OFFSET INT_MAX +#define MAX_KEY2_OFFSET 0xfffffffffffffffLL + + + +#define MAX_KEY_UNIQUENESS UINT_MAX +#define MAX_KEY_OBJECTID UINT_MAX + +#define MAX_B_NUM UINT_MAX +#define MAX_FC_NUM USHRT_MAX + + +/* the purpose is to detect overflow of an unsigned short */ +#define REISERFS_LINK_MAX (USHRT_MAX - 1000) + + +/* The following defines are used in reiserfs_insert_item and reiserfs_append_item */ +#define REISERFS_KERNEL_MEM 0 /* reiserfs kernel memory mode */ +#define REISERFS_USER_MEM 1 /* reiserfs user memory mode */ + + +/***************************************************************************/ +/* FIXATE NODES */ +/***************************************************************************/ + +#define VI_TYPE_STAT_DATA 1 +#define VI_TYPE_DIRECT 2 +#define VI_TYPE_INDIRECT 4 +#define VI_TYPE_DIRECTORY 8 +#define VI_TYPE_FIRST_DIRECTORY_ITEM 16 +#define VI_TYPE_INSERTED_DIRECTORY_ITEM 32 + +#define VI_TYPE_LEFT_MERGEABLE 64 +#define VI_TYPE_RIGHT_MERGEABLE 128 + +/* To make any changes in the tree we always first find node, that contains + item to be changed/deleted or place to insert a new item. We call this node + S. To do balancing we need to decide what we will shift to left/right + neighbor, or to a new node, where new item will be etc. To make this + analysis simpler we build virtual node. Virtual node is an array of items, + that will replace items of node S. (For instance if we are going to delete + an item, virtual node does not contain it). Virtual node keeps information + about item sizes and types, mergeability of first and last items, sizes of + all entries in directory item. We use this array of items when calculating + what we can shift to neighbors and how many nodes we have to have if we do + not any shiftings, if we shift to left/right neighbor or to both. */ +struct virtual_item +{ + unsigned short vi_type; /* item type, mergeability */ + unsigned short vi_item_len; /* length of item that it will have after balancing */ + + short vi_entry_count; /* number of entries in directory item + (including the new one if any, or excluding + entry if it must be cut) */ + unsigned short * vi_entry_sizes; /* array of entry lengths for directory item */ +}; + +struct virtual_node +{ + char * vn_free_ptr; /* this is a pointer to the free space in the buffer */ + unsigned short vn_nr_item; /* number of items in virtual node */ + short vn_size; /* size of node , that node would have if it has unlimited size and no balancing is performed */ + short vn_mode; /* mode of balancing (paste, insert, delete, cut) */ + short vn_affected_item_num; + short vn_pos_in_item; + struct item_head * vn_ins_ih; /* item header of inserted item, 0 for other modes */ + struct virtual_item * vn_vi; /* array of items (including a new one, excluding item to be deleted) */ +}; + + +/***************************************************************************/ +/* TREE BALANCE */ +/***************************************************************************/ + +/* This temporary structure is used in tree balance algorithms, and + constructed as we go to the extent that its various parts are needed. It + contains arrays of nodes that can potentially be involved in the balancing + of node S, and parameters that define how each of the nodes must be + balanced. Note that in these algorithms for balancing the worst case is to + need to balance the current node S and the left and right neighbors and all + of their parents plus create a new node. We implement S1 balancing for the + leaf nodes and S0 balancing for the internal nodes (S1 and S0 are defined + in our papers.)*/ + +#define MAX_FREE_BLOCK 7 /* size of the array of buffers to free at end of do_balance */ + +/* maximum number of FEB blocknrs on a single level */ +#define MAX_AMOUNT_NEEDED 2 + +/* someday somebody will prefix every field in this struct with tb_ */ +struct tree_balance +{ + struct reiserfs_transaction_handle *transaction_handle ; + struct super_block * tb_sb; + struct path * tb_path; + struct buffer_head * L[MAX_HEIGHT]; /* array of left neighbors of nodes in the path */ + struct buffer_head * R[MAX_HEIGHT]; /* array of right neighbors of nodes in the path*/ + struct buffer_head * FL[MAX_HEIGHT]; /* array of fathers of the left neighbors */ + struct buffer_head * FR[MAX_HEIGHT]; /* array of fathers of the right neighbors */ + struct buffer_head * CFL[MAX_HEIGHT]; /* array of common parents of center node and its left neighbor */ + struct buffer_head * CFR[MAX_HEIGHT]; /* array of common parents of center node and its right neighbor */ + + /* array of blocknr's that are free and are the nearest to the left node that are usable + for writing dirty formatted leaves, using the write_next_to algorithm. */ + /*unsigned long free_and_near[MAX_DIRTIABLE];*/ + + struct buffer_head * FEB[MAX_FEB_SIZE]; /* array of empty buffers. Number of buffers in array equals + cur_blknum. */ + struct buffer_head * used[MAX_FEB_SIZE]; + short int lnum[MAX_HEIGHT]; /* array of number of items which must be shifted to the left in + order to balance the current node; for leaves includes item + that will be partially shifted; for internal nodes, it is + the number of child pointers rather than items. It includes + the new item being created. For preserve_shifted() purposes + the code sometimes subtracts one from this number to get the + number of currently existing items being shifted, and even + more often for leaves it subtracts one to get the number of + wholly shifted items for other purposes. */ + short int rnum[MAX_HEIGHT]; /* substitute right for left in comment above */ + short int lkey[MAX_HEIGHT]; /* array indexed by height h mapping the key delimiting L[h] and + S[h] to its item number within the node CFL[h] */ + short int rkey[MAX_HEIGHT]; /* substitute r for l in comment above */ + short int insert_size[MAX_HEIGHT]; /* the number of bytes by we are trying to add or remove from + S[h]. A negative value means removing. */ + short int blknum[MAX_HEIGHT]; /* number of nodes that will replace node S[h] after + balancing on the level h of the tree. If 0 then S is + being deleted, if 1 then S is remaining and no new nodes + are being created, if 2 or 3 then 1 or 2 new nodes is + being created */ + + /* fields that are used only for balancing leaves of the tree */ + short int cur_blknum; /* number of empty blocks having been already allocated */ + short int s0num; /* number of items that fall into left most node when S[0] splits */ + short int s1num; /* number of items that fall into first new node when S[0] splits */ + short int s2num; /* number of items that fall into second new node when S[0] splits */ + short int lbytes; /* number of bytes which can flow to the left neighbor from the left */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + short int rbytes; /* number of bytes which will flow to the right neighbor from the right */ + /* most liquid item that cannot be shifted from S[0] entirely */ + /* if -1 then nothing will be partially shifted */ + short int s1bytes; /* number of bytes which flow to the first new node when S[0] splits */ + /* note: if S[0] splits into 3 nodes, then items do not need to be cut */ + short int s2bytes; + struct buffer_head * buf_to_free[MAX_FREE_BLOCK]; /* buffers which are to be freed after do_balance finishes by unfix_nodes */ + char * vn_buf; /* kmalloced memory. Used to create + virtual node and keep map of + dirtied bitmap blocks */ + int vn_buf_size; /* size of the vn_buf */ + struct virtual_node * tb_vn; /* VN starts after bitmap of bitmap blocks */ +} ; + + + +/* These are modes of balancing */ + +/* When inserting an item. */ +#define M_INSERT 'i' +/* When inserting into (directories only) or appending onto an already + existant item. */ +#define M_PASTE 'p' +/* When deleting an item. */ +#define M_DELETE 'd' +/* When truncating an item or removing an entry from a (directory) item. */ +#define M_CUT 'c' + +/* used when balancing on leaf level skipped (in reiserfsck) */ +#define M_INTERNAL 'n' + +/* When further balancing is not needed, then do_balance does not need + to be called. */ +#define M_SKIP_BALANCING 's' +#define M_CONVERT 'v' + +/* modes of leaf_move_items */ +#define LEAF_FROM_S_TO_L 0 +#define LEAF_FROM_S_TO_R 1 +#define LEAF_FROM_R_TO_L 2 +#define LEAF_FROM_L_TO_R 3 +#define LEAF_FROM_S_TO_SNEW 4 + +#define FIRST_TO_LAST 0 +#define LAST_TO_FIRST 1 + +/* used in do_balance for passing parent of node information that has been + gotten from tb struct */ +struct buffer_info { + struct buffer_head * bi_bh; + struct buffer_head * bi_parent; + int bi_position; +}; + + +/* there are 4 types of items: stat data, directory item, indirect, direct. + FIXME: This table does not describe new key format ++-------------------+------------+--------------+------------+ +| | k_offset | k_uniqueness | mergeable? | ++-------------------+------------+--------------+------------+ +| stat data | 0 | 0 | no | ++-------------------+------------+--------------+------------+ +| 1st directory item| DOT_OFFSET |DIRENTRY_UNIQUENESS| no | +| non 1st directory | hash value | | yes | +| item | | | | ++-------------------+------------+--------------+------------+ +| indirect item | offset + 1 |TYPE_INDIRECT | if this is not the first indirect item of the object ++-------------------+------------+--------------+------------+ +| direct item | offset + 1 |TYPE_DIRECT | if not this is not the first direct item of the object ++-------------------+------------+--------------+------------+ +*/ + + + +#define KEY_IS_STAT_DATA_KEY(p_s_key) ( get_type (p_s_key) == TYPE_STAT_DATA ) +#define KEY_IS_DIRECTORY_KEY(p_s_key) ( get_type (p_s_key) == TYPE_DIRENTRY ) +#define KEY_IS_DIRECT_KEY(p_s_key) ( get_type (p_s_key) == TYPE_DIRECT ) +#define KEY_IS_INDIRECT_KEY(p_s_key) ( get_type (p_s_key) == TYPE_INDIRECT ) + +#define I_IS_STAT_DATA_ITEM(p_s_ih) KEY_IS_STAT_DATA_KEY(&((p_s_ih)->ih_key)) +#define I_IS_DIRECTORY_ITEM(p_s_ih) KEY_IS_DIRECTORY_KEY(&((p_s_ih)->ih_key)) +#define I_IS_DIRECT_ITEM(p_s_ih) KEY_IS_DIRECT_KEY(&((p_s_ih)->ih_key)) +#define I_IS_INDIRECT_ITEM(p_s_ih) KEY_IS_INDIRECT_KEY(&((p_s_ih)->ih_key)) + +#define is_indirect_ih(ih) I_IS_INDIRECT_ITEM(ih) +#define is_direct_ih(ih) I_IS_DIRECT_ITEM(ih) +#define is_direntry_ih(ih) I_IS_DIRECTORY_ITEM(ih) +#define is_stat_data_ih(ih) I_IS_STAT_DATA_ITEM(ih) + +#define is_indirect_key(key) KEY_IS_INDIRECT_KEY(key) +#define is_direct_key(key) KEY_IS_DIRECT_KEY(key) +#define is_direntry_key(key) KEY_IS_DIRECTORY_KEY(key) +#define is_stat_data_key(key) KEY_IS_STAT_DATA_KEY(key) + +#define COMP_KEYS comp_keys + +//#define COMP_SHORT_KEYS comp_short_keys +#define not_of_one_file comp_short_keys + +/* number of blocks pointed to by the indirect item */ +#define I_UNFM_NUM(p_s_ih) ( (p_s_ih)->ih_item_len / UNFM_P_SIZE ) + +/* the used space within the unformatted node corresponding to pos within the item pointed to by ih */ +#define I_POS_UNFM_SIZE(ih,pos,size) (((pos) == I_UNFM_NUM(ih) - 1 ) ? (size) - ih_free_space (ih) : (size)) + +/* check whether byte number 'offset' is in this item */ +#define I_OFF_BYTE_IN_ITEM(p_s_ih, n_offset, n_blocksize) \ + ( get_offset(&(p_s_ih)->ih_key) <= (n_offset) && \ + get_offset(&(p_s_ih)->ih_key) + get_bytes_number(p_s_ih,n_blocksize) > (n_offset) ) + +/* get the item header */ +#define B_N_PITEM_HEAD(bh,item_num) ( (struct item_head * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get key */ +#define B_N_PDELIM_KEY(bh,item_num) ( (struct key * )((bh)->b_data + BLKH_SIZE) + (item_num) ) + +/* get the key */ +#define B_N_PKEY(bh,item_num) ( &(B_N_PITEM_HEAD(bh,item_num)->ih_key) ) + +/* get item body */ +#define B_N_PITEM(bh,item_num) ( (bh)->b_data + B_N_PITEM_HEAD((bh),(item_num))->ih_item_location) + +/* get the stat data by the buffer header and the item order */ +#define B_N_STAT_DATA(bh,nr) \ +( (struct stat_data *)((bh)->b_data+B_N_PITEM_HEAD((bh),(nr))->ih_item_location ) ) + + /* following defines use reiserfs buffer header and item header */ + /* get item body */ +#define B_I_PITEM(bh,ih) ( (bh)->b_data + ih_location(ih)) + +/* get stat-data */ +#define B_I_STAT_DATA(bh, ih) ( (struct stat_data * )B_I_PITEM(bh,ih) ) + +#define MAX_DIRECT_ITEM_LEN(size) ((size) - BLKH_SIZE - 2*IH_SIZE - SD_SIZE - UNFM_P_SIZE) + +/* indirect items consist of entries which contain blocknrs, pos + indicates which entry, and B_I_POS_UNFM_POINTER resolves to the + blocknr contained by the entry pos points to */ +#define B_I_POS_UNFM_POINTER(bh,ih,pos) (*(((__u32 *)B_I_PITEM(bh,ih)) + (pos))) + + + +/***************************************************************************/ +/* FUNCTION DECLARATIONS */ +/***************************************************************************/ + + + + +/* objectid.c */ +__u32 reiserfs_get_unused_objectid (struct reiserfs_transaction_handle *th); +void reiserfs_release_objectid (struct reiserfs_transaction_handle *th, __u32 objectid_to_release); +int reiserfs_convert_objectid_map_v1(struct super_block *s); + + +/* stree.c */ +void padd_item (char * item, int total_length, int length); +int B_IS_IN_TREE(struct buffer_head *); +struct key * get_rkey (struct path * p_s_chk_path, struct super_block * p_s_sb); +int bin_search (void * p_v_key, void * p_v_base, int p_n_num, int p_n_width, int * p_n_pos); +int search_by_key (struct super_block *, struct key *, struct path *, int * , int); +int search_by_entry_key (struct super_block * sb, struct key * key, struct path * path); +int search_for_position_by_key (struct super_block * p_s_sb, struct key * p_s_key, + struct path * p_s_search_path); +int search_by_objectid (struct super_block *, struct key *, struct path *, int *); +void decrement_counters_in_path (struct path * p_s_search_path); +void pathrelse (struct path * p_s_search_path); + + +int is_left_mergeable (struct super_block * s, struct path * path); +int is_right_mergeable (struct super_block * s, struct path * path); +int are_items_mergeable (struct item_head * left, struct item_head * right, int bsize); + + +/* fix_nodes.c */ +void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s); +void reiserfs_kfree (/*const*/ void * vp, size_t size, struct super_block * s); +int fix_nodes (/*struct reiserfs_transaction_handle *th,*/ int n_op_mode, struct tree_balance * p_s_tb, + /*int n_pos_in_item,*/ struct item_head * p_s_ins_ih); +void unfix_nodes (/*struct reiserfs_transaction_handle *th,*/ struct tree_balance *); +void free_buffers_in_tb (struct tree_balance * p_s_tb); +void init_path (struct path *); + +/* prints.c */ +#define PRINT_LEAF_ITEMS 1 /* print all items */ +#define PRINT_ITEM_DETAILS 2 /* print contents of directory items and stat + data items and indirect items */ +#define PRINT_DIRECT_ITEMS 4 /* print contents of direct items */ +void print_tb (int mode, int item_pos, int pos_in_item, struct tree_balance * tb, char * mes); + + +void print_bmap (FILE * fp, reiserfs_filsys_t fs, int silent); +void print_objectid_map (FILE * fp, reiserfs_filsys_t fs); + + + +/* lbalance.c */ +int leaf_move_items (int shift_mode, struct tree_balance * tb, + int mov_num, int mov_bytes, struct buffer_head * Snew); +int leaf_shift_left (struct tree_balance * tb, int shift_num, int shift_bytes); +int leaf_shift_right (struct tree_balance * tb, int shift_num, int shift_bytes); +void leaf_delete_items (reiserfs_filsys_t, struct buffer_info * cur_bi, + int last_first, int first, int del_num, int del_bytes); +void leaf_insert_into_buf (reiserfs_filsys_t, struct buffer_info * bi, + int before, struct item_head * inserted_item_ih, const char * inserted_item_body, + int zeros_number); +void leaf_paste_in_buffer (reiserfs_filsys_t, struct buffer_info * bi, int pasted_item_num, + int pos_in_item, int paste_size, const char * body, int zeros_number); +void leaf_cut_from_buffer (reiserfs_filsys_t, struct buffer_info * bi, int cut_item_num, + int pos_in_item, int cut_size); +void leaf_paste_entries (struct buffer_head * bh, int item_num, int before, int new_entry_count, + struct reiserfs_de_head * new_dehs, const char * records, + int paste_size); +void delete_item (reiserfs_filsys_t fs, struct buffer_head * bh, int item_num); +void cut_entry (reiserfs_filsys_t fs, struct buffer_head * bh, + int item_num, int entry_num, int del_count); + + +/* ibalance.c */ +int balance_internal (struct tree_balance * , int, int, struct item_head * , + struct buffer_head **); + +/* do_balance.c */ +void do_balance (struct tree_balance * tb, + struct item_head * ih, const char * body, int flag, int zeros_num); +void reiserfs_invalidate_buffer (struct tree_balance * tb, struct buffer_head * bh, int); +int get_left_neighbor_position (struct tree_balance * tb, int h); +int get_right_neighbor_position (struct tree_balance * tb, int h); +void replace_key (reiserfs_filsys_t, struct buffer_head *, int, struct buffer_head *, int); +void replace_lkey (struct tree_balance *, int, struct item_head *); +void replace_rkey (struct tree_balance *, int, struct item_head *); +void make_empty_node (struct buffer_info *); +struct buffer_head * get_FEB (struct tree_balance *); + + +__u64 get_bytes_number (struct item_head * ih, int blocksize); + + + + +/* hashes.c */ +__u32 keyed_hash (const char *msg, int len); +__u32 yura_hash (const char *msg, int len); +__u32 r5_hash (const char *msg, int len); + + + +/* node_format.c */ +int get_journal_old_start_must (struct reiserfs_super_block * rs); +int get_journal_start_must (int blocksize); +/*extern hashf_t hashes [];*/ + + diff --git a/include/reiserfs_lib.h b/include/reiserfs_lib.h new file mode 100644 index 0000000..b23893e --- /dev/null +++ b/include/reiserfs_lib.h @@ -0,0 +1,199 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#ifndef REISERFS_LIB_H +#define REISERFS_LIB_H + +typedef struct super_block * reiserfs_filsys_t; + +#include "reiserfs_fs.h" + +struct _bitmap { + unsigned long bm_byte_size; + unsigned long bm_bit_size; + char * bm_map; + unsigned long bm_set_bits; +}; + +typedef struct _bitmap * reiserfs_bitmap_t; + +struct super_block { + int s_version; /* on-disk format version */ + reiserfs_bitmap_t bitmap; /* copy of reiserfs on-disk bitmap */ + + int s_dev; /* descriptor of opened block device file */ + int s_blocksize; + struct buffer_head ** s_ap_bitmap; /* array of buffers containing bitmap + blocks */ + struct buffer_head * s_sbh; /* buffer containing super block */ + struct reiserfs_super_block * s_rs; /* pointer to its b_data */ + int s_dirt; + hashf_t s_hash_function; /* pointer to function which is used to sort + names in directory. It is set by reiserfs_open + if it is set in the super block, otherwise it + is set by first is_properly_hashed */ + char * file_name; /* file name of underlying device */ + int s_flags; + void * s_vp; + int (*block_allocator) (reiserfs_filsys_t fs, + unsigned long * free_blocknrs, + unsigned long start, int amount_needed); + int (*block_deallocator) (reiserfs_filsys_t fs, unsigned long block); +}; + + +/* reiserfslib.c */ + +reiserfs_filsys_t reiserfs_open (char * filename, int flags, int * error, void * vp); +void reiserfs_read_bitmap_blocks (reiserfs_filsys_t); +void reiserfs_free_bitmap_blocks (reiserfs_filsys_t); +int no_reiserfs_found (reiserfs_filsys_t fs); +void reiserfs_reopen (reiserfs_filsys_t fs, int flags); +void reiserfs_flush (reiserfs_filsys_t fs); +void reiserfs_free (reiserfs_filsys_t fs); +void reiserfs_close (reiserfs_filsys_t fs); +int reiserfs_new_blocknrs (reiserfs_filsys_t fs, + unsigned long * free_blocknrs, unsigned long start, + int amount_needed); +int reiserfs_free_block (reiserfs_filsys_t fs, unsigned long block); +int spread_bitmaps (reiserfs_filsys_t fs); +int filesystem_dirty (reiserfs_filsys_t fs); +void mark_filesystem_dirty (reiserfs_filsys_t fs); + +void reiserfs_paste_into_item (reiserfs_filsys_t fs, struct path * path, + const void * body, int size); +void reiserfs_insert_item (reiserfs_filsys_t fs, struct path * path, + struct item_head * ih, const void * body); + +int reiserfs_find_entry (reiserfs_filsys_t fs, struct key * dir, char * name, + int * min_gen_counter); +int reiserfs_add_entry (reiserfs_filsys_t fs, struct key * dir, char * name, + struct key * key, int fsck_need); + +int _search_by_entry_key (reiserfs_filsys_t fs, struct key * key, + struct path * path); +void copy_key (void * to, void * from); +void copy_short_key (void * to, void * from); +void copy_item_head(void * p_v_to, void * p_v_from); +int comp_keys (void * k1, void * k2); +int comp_short_keys (void * p_s_key1, void * p_s_key2); +int comp_items (struct item_head * p_s_ih, struct path * p_s_path); + + +/* bitmap.c */ + +reiserfs_bitmap_t reiserfs_create_bitmap (unsigned int bit_count); +int reiserfs_expand_bitmap (reiserfs_bitmap_t bm, unsigned int bit_count); +void reiserfs_delete_bitmap (reiserfs_bitmap_t bm); +void reiserfs_bitmap_copy (reiserfs_bitmap_t to, reiserfs_bitmap_t from); +int reiserfs_bitmap_compare (reiserfs_bitmap_t bm1, reiserfs_bitmap_t bm2); +void reiserfs_bitmap_set_bit (reiserfs_bitmap_t bm, unsigned int bit_number); +void reiserfs_bitmap_clear_bit (reiserfs_bitmap_t bm, unsigned int bit_number); + +int reiserfs_bitmap_test_bit (reiserfs_bitmap_t bm, unsigned int bit_number); +int reiserfs_bitmap_find_zero_bit (reiserfs_bitmap_t bm, unsigned long * start); +int reiserfs_fetch_disk_bitmap (reiserfs_bitmap_t bm, reiserfs_filsys_t fs); +int reiserfs_flush_bitmap (reiserfs_bitmap_t bm, reiserfs_filsys_t fs); +void reiserfs_bitmap_zero (reiserfs_bitmap_t bm); +void reiserfs_bitmap_fill (reiserfs_bitmap_t bm); +int reiserfs_bitmap_ones (reiserfs_bitmap_t bm); +int reiserfs_bitmap_zeros (reiserfs_bitmap_t bm); + +void reiserfs_bitmap_save (char * filename, reiserfs_bitmap_t bm); +reiserfs_bitmap_t reiserfs_bitmap_load (char * filename); +void reiserfs_bitmap_invert (reiserfs_bitmap_t bm); + + +int reiserfs_remove_entry (reiserfs_filsys_t fs, struct key * key); + + + +/* node_formats.c */ + +#define THE_LEAF 1 +#define THE_INTERNAL 2 +#define THE_SUPER 3 +#define THE_JDESC 4 +#define THE_UNKNOWN 5 + +int is_reiserfs_magic_string (struct reiserfs_super_block * rs); +int is_reiser2fs_magic_string (struct reiserfs_super_block * rs); +int is_prejournaled_reiserfs (struct reiserfs_super_block * rs); +int does_desc_match_commit (struct reiserfs_journal_desc *desc, + struct reiserfs_journal_commit *commit); +int who_is_this (char * buf, int blocksize); +int journal_size (struct super_block * s); +int not_data_block (struct super_block * s, unsigned long block); +int not_journalable (reiserfs_filsys_t fs, unsigned long block); +int block_of_bitmap (reiserfs_filsys_t fs, unsigned long block); +int block_of_journal (reiserfs_filsys_t fs, unsigned long block); +int is_tree_node (struct buffer_head * bh, int level); +int is_properly_hashed (reiserfs_filsys_t fs, + char * name, int namelen, __u32 offset); +int dir_entry_bad_location (struct reiserfs_de_head * deh, + struct item_head * ih, int first); +void make_dir_stat_data (int blocksize, int key_format, + __u32 dirid, __u32 objectid, + struct item_head * ih, void * sd); +void make_empty_dir_item_v1 (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid); +void make_empty_dir_item (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid); + + +typedef void (*item_action_t) (struct buffer_head * bh, struct item_head * ih); +typedef void (*item_head_action_t) (struct item_head * ih); + +void for_every_item (struct buffer_head * bh, item_head_action_t action, + item_action_t * actions); +int key_format (const struct key * key); +loff_t get_offset (const struct key * key); +int uniqueness2type (__u32 uniqueness); +__u32 type2uniqueness (int type); +int get_type (const struct key * key); +char * key_of_what (const struct key * key); +int type_unknown (struct key * key); +void set_type (int format, struct key * key, int type); +void set_offset (int format, struct key * key, loff_t offset); +void set_type_and_offset (int format, struct key * key, loff_t offset, int type); + + +typedef int (*check_unfm_func_t) (reiserfs_filsys_t fs, __u32); +int is_it_bad_item (reiserfs_filsys_t, struct item_head *, char *, + check_unfm_func_t, int bad_dir); + + +#define hash_func_is_unknown(fs) ((fs)->s_hash_function == 0) +#define reiserfs_hash(fs) ((fs)->s_hash_function) + +int known_hashes (void); +char * code2name (int code); +int func2code (hashf_t func); +hashf_t code2func (int code); +int find_hash_in_use (char * name, int namelen, __u32 hash_value_masked, int code_to_try_first); + +int entry_length (struct item_head * ih, struct reiserfs_de_head * deh, + int pos_in_item); +char * name_in_entry (struct reiserfs_de_head * deh, int pos_in_item); +int name_length (struct item_head * ih, + struct reiserfs_de_head * deh, int pos_in_item); + + + + +/* prints.c */ +void print_indirect_item (FILE * fp, struct buffer_head * bh, int item_num); +void print_block (FILE * fp, reiserfs_filsys_t, struct buffer_head * bh, ...);//int print_mode, int first, int last); +void reiserfs_warning (FILE * fp, const char * fmt, ...); +char ftypelet (mode_t mode); + +#define reiserfs_panic(fmt, list...) \ +{\ + fprintf (stderr, "%s %d %s\n", __FILE__, __LINE__, __FUNCTION__);\ + reiserfs_warning (stderr, fmt, ## list);\ + exit(4);\ +} + +#endif /* REISERFS_LIB_H */ + diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..e9de238 --- /dev/null +++ b/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/lib/Makefile.am b/lib/Makefile.am new file mode 100644 index 0000000..270515e --- /dev/null +++ b/lib/Makefile.am @@ -0,0 +1,23 @@ +noinst_LIBRARIES = libmisc.a + +libmisc_a_SOURCES = io.c misc.c +##reiserfs.c + +INCLUDES = -I$(top_srcdir)/include + + + + + + + + + + + + + + + + + diff --git a/lib/Makefile.in b/lib/Makefile.in new file mode 100644 index 0000000..309dda3 --- /dev/null +++ b/lib/Makefile.in @@ -0,0 +1,260 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +noinst_LIBRARIES = libmisc.a + +libmisc_a_SOURCES = io.c misc.c + +INCLUDES = -I$(top_srcdir)/include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libmisc_a_LIBADD = +libmisc_a_OBJECTS = io.o misc.o +AR = ar +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(libmisc_a_SOURCES) +OBJECTS = $(libmisc_a_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps lib/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +libmisc.a: $(libmisc_a_OBJECTS) $(libmisc_a_DEPENDENCIES) + -rm -f libmisc.a + $(AR) cru libmisc.a $(libmisc_a_OBJECTS) $(libmisc_a_LIBADD) + $(RANLIB) libmisc.a + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = lib + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +io.o: io.c ../include/io.h ../include/misc.h +misc.o: misc.c ../include/io.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(LIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLIBRARIES clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLIBRARIES distclean-compile \ + distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/lib/io.c b/lib/io.c new file mode 100644 index 0000000..fe94334 --- /dev/null +++ b/lib/io.c @@ -0,0 +1,508 @@ +/* + * Copyright 1996, 1997 Hans Reiser, see reiserfs/README for licensing and copyright details + */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <asm/types.h> + +#include <sys/types.h> +/*#include <linux/unistd.h>*/ + + +#include "io.h" +#include "misc.h" + + + +/* All buffers are in double linked cycled list. Buffers of tree are + hashed by their block number. If getblk found buffer with wanted + block number in hash queue it moves buffer to the end of list */ + +#define BLOCK_SIZE 1024 +#define MAX_NR_BUFFERS 16384 +static int g_nr_buffers; + +#define NR_HASH_QUEUES 4096 +static struct buffer_head * g_a_hash_queues [NR_HASH_QUEUES]; +static struct buffer_head * g_buffer_list_head; +static struct buffer_head * g_buffer_heads; + + + +static void show_buffers (int dev, int size) +{ + int all = 0; + int dirty = 0; + int in_use = 0; /* count != 0 */ + int free = 0; + struct buffer_head * next = g_buffer_list_head; + + for (;;) { + if (!next) + die ("show_buffers: buffer list is corrupted"); + if (next->b_dev == dev && next->b_size == size) { + all ++; + if (next->b_count != 0) { + in_use ++; + } + if (buffer_dirty (next)) { + dirty ++; + } + if (buffer_clean (next) && next->b_count == 0) { + free ++; + } + } + next = next->b_next; + if (next == g_buffer_list_head) + break; + } + + printf ("show_buffers (dev %d, size %d): free %d, count != 0 %d, dirty %d, all %d\n", dev, size, free, in_use, dirty, all); +} + + +static void insert_into_hash_queue (struct buffer_head * bh) +{ + int index = bh->b_blocknr % NR_HASH_QUEUES; + + if (bh->b_hash_prev || bh->b_hash_next) + die ("insert_into_hash_queue: hash queue corrupted"); + + if (g_a_hash_queues[index]) { + g_a_hash_queues[index]->b_hash_prev = bh; + bh->b_hash_next = g_a_hash_queues[index]; + } + g_a_hash_queues[index] = bh; +} + + +static void remove_from_hash_queue (struct buffer_head * bh) +{ + if (bh->b_hash_next == 0 && bh->b_hash_prev == 0 && bh != g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES]) + /* (b_dev == 0) ? */ + return; + + if (bh == g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES]) { + if (bh->b_hash_prev != 0) + die ("remove_from_hash_queue: hash queue corrupted"); + g_a_hash_queues[bh->b_blocknr % NR_HASH_QUEUES] = bh->b_hash_next; + } + if (bh->b_hash_next) + bh->b_hash_next->b_hash_prev = bh->b_hash_prev; + + if (bh->b_hash_prev) + bh->b_hash_prev->b_hash_next = bh->b_hash_next; + + bh->b_hash_prev = bh->b_hash_next = 0; +} + + +static void put_buffer_list_end (struct buffer_head * bh) +{ + struct buffer_head * last = 0; + + if (bh->b_prev || bh->b_next) + die ("put_buffer_list_end: buffer list corrupted"); + + if (g_buffer_list_head == 0) { + bh->b_next = bh; + bh->b_prev = bh; + g_buffer_list_head = bh; + } else { + last = g_buffer_list_head->b_prev; + + bh->b_next = last->b_next; + bh->b_prev = last; + last->b_next->b_prev = bh; + last->b_next = bh; + } +} + + +static void remove_from_buffer_list (struct buffer_head * bh) +{ + if (bh == bh->b_next) { + g_buffer_list_head = 0; + } else { + bh->b_prev->b_next = bh->b_next; + bh->b_next->b_prev = bh->b_prev; + if (bh == g_buffer_list_head) + g_buffer_list_head = bh->b_next; + } + + bh->b_next = bh->b_prev = 0; +} + + +static void put_buffer_list_head (struct buffer_head * bh) +{ + put_buffer_list_end (bh); + g_buffer_list_head = bh; +} + + +#define GROW_BUFFERS__NEW_BUFERS_PER_CALL 10 +/* creates number of new buffers and insert them into head of buffer list + */ +static int grow_buffers (int size) +{ + int i; + struct buffer_head * bh, * tmp; + + if (g_nr_buffers + GROW_BUFFERS__NEW_BUFERS_PER_CALL > MAX_NR_BUFFERS) + return 0; + + /* get memory for array of buffer heads */ + bh = (struct buffer_head *)getmem (GROW_BUFFERS__NEW_BUFERS_PER_CALL * sizeof (struct buffer_head) + sizeof (struct buffer_head *)); + if (g_buffer_heads == 0) + g_buffer_heads = bh; + else { + /* link new array to the end of array list */ + tmp = g_buffer_heads; + while (*(struct buffer_head **)(tmp + GROW_BUFFERS__NEW_BUFERS_PER_CALL) != 0) + tmp = *(struct buffer_head **)(tmp + GROW_BUFFERS__NEW_BUFERS_PER_CALL); + *(struct buffer_head **)(tmp + GROW_BUFFERS__NEW_BUFERS_PER_CALL) = bh; + } + + for (i = 0; i < GROW_BUFFERS__NEW_BUFERS_PER_CALL; i ++) { + + tmp = bh + i; + memset (tmp, 0, sizeof (struct buffer_head)); + tmp->b_data = getmem (size); + if (tmp->b_data == 0) + die ("grow_buffers: no memory for new buffer data"); + tmp->b_dev = 0; + tmp->b_size = size; + put_buffer_list_head (tmp); + + g_nr_buffers ++; + } + return GROW_BUFFERS__NEW_BUFERS_PER_CALL; +} + + +struct buffer_head * find_buffer (int dev, int block, int size) +{ + struct buffer_head * next; + + next = g_a_hash_queues[block % NR_HASH_QUEUES]; + for (;;) { + struct buffer_head *tmp = next; + if (!next) + break; + next = tmp->b_hash_next; + if (tmp->b_blocknr != block || tmp->b_size != size || tmp->b_dev != dev) + continue; + next = tmp; + break; + } + return next; +} + +void __wait_on_buffer (struct buffer_head * bh) +{ +} + +struct buffer_head * get_hash_table(dev_t dev, int block, int size) +{ + struct buffer_head * bh; + + bh = find_buffer (dev, block, size); + if (bh) { + bh->b_count ++; + } + return bh; +} + + +static struct buffer_head * get_free_buffer (int size) +{ + struct buffer_head * next = g_buffer_list_head; + + if (!next) + return 0; + for (;;) { + if (!next) + die ("get_free_buffer: buffer list is corrupted"); + if (next->b_count == 0 && buffer_clean (next) && next->b_size == size) { + remove_from_hash_queue (next); + remove_from_buffer_list (next); + put_buffer_list_end (next); + return next; + } + next = next->b_next; + if (next == g_buffer_list_head) + break; + } + return 0; +} + + +static void sync_buffers (int size, int to_write) +{ + struct buffer_head * next = g_buffer_list_head; + int written = 0; + + for (;;) { + if (!next) + die ("flush_buffer: buffer list is corrupted"); + + if ((!size || next->b_size == size) && buffer_dirty (next) && buffer_uptodate (next)) { + written ++; + bwrite (next); + if (written == to_write) + return; + } + + next = next->b_next; + if (next == g_buffer_list_head) + break; + } +} + +void flush_buffers (void) +{ + sync_buffers (0, 0); +} + + +struct buffer_head * getblk (int dev, int block, int size) +{ + struct buffer_head * bh; + + bh = find_buffer (dev, block, size); + if (bh) { + if (0 && !buffer_uptodate (bh)) + die ("getblk: buffer must be uptodate"); + // move the buffer to the end of list + + /*checkmem (bh->b_data, bh->b_size);*/ + + remove_from_buffer_list (bh); + put_buffer_list_end (bh); + bh->b_count ++; + return bh; + } + + bh = get_free_buffer (size); + if (bh == 0) { + if (grow_buffers (size) == 0) { + sync_buffers (size, 10); + } + bh = get_free_buffer (size); + if (bh == 0) { + show_buffers (dev, size); + die ("getblk: no free buffers after grow_buffers and refill (%d)", g_nr_buffers); + } + } + + bh->b_count = 1; + bh->b_dev = dev; + bh->b_size = size; + bh->b_blocknr = block; + bh->b_end_io = NULL ; + memset (bh->b_data, 0, size); + clear_bit(BH_Dirty, &bh->b_state); + clear_bit(BH_Uptodate, &bh->b_state); + + insert_into_hash_queue (bh); + /*checkmem (bh->b_data, bh->b_size);*/ + + return bh; +} + +struct buffer_head * reiserfs_getblk (int dev, int block, int size, int *repeat) +{ + return getblk (dev, block, size); +} + + +void brelse (struct buffer_head * bh) +{ + if (bh == 0) + return; + if (bh->b_count == 0) { + die ("brelse: can not free a free buffer %lu", bh->b_blocknr); + } + /*checkmem (bh->b_data, bh->b_size);*/ + bh->b_count --; +} + + +void bforget (struct buffer_head * bh) +{ + if (bh) { + brelse (bh); + remove_from_hash_queue (bh); + remove_from_buffer_list (bh); + put_buffer_list_head (bh); + } +} + +#if 0 +#if ! ( defined __USE_LARGEFILE64 || defined __USE_FILE_OFFSET64 ) +_syscall5 (int, _llseek, uint, fd, ulong, hi, ulong, lo, + loff_t *, res, uint, wh); +#endif + +loff_t reiserfs_llseek (unsigned int fd, loff_t offset, unsigned int origin) +{ +#if defined __USE_FILE_OFFSET64 + return lseek(fd, offset, origin); +#elif defined __USE_LARGEFILE64 + return lseek64(fd, offset, origin); +#else + loff_t retval, result; + retval = _llseek (fd, ((unsigned long long) offset) >> 32, + ((unsigned long long) offset) & 0xffffffff, + &result, origin); + return (retval != 0 ? (loff_t)-1 : result); +#endif +} +#endif + +static int f_read(struct buffer_head * bh) +{ + loff_t offset; + ssize_t bytes; + + offset = (loff_t)bh->b_size * (loff_t)bh->b_blocknr; + /*if (reiserfs_llseek (bh->b_dev, offset, SEEK_SET) == (loff_t)-1)*/ + if (lseek64 (bh->b_dev, offset, SEEK_SET) == (loff_t)-1) + return 0; + + bytes = read (bh->b_dev, bh->b_data, bh->b_size); + if (bytes != (ssize_t)bh->b_size) + return 0; + + return 1; +} + +struct buffer_head * bread (int dev, unsigned long block, size_t size) +{ + struct buffer_head * bh; + + /* + if ((size == 32768 && (block == 16 || 1026)) || + (size == 4096 && (block == 128 || block == 8211))) { + size = 10; + return 0; + } + */ + + bh = getblk (dev, block, size); + if (buffer_uptodate (bh)) + return bh; + + if (f_read(bh) == 0) + { + brelse(bh); + return 0; + } + + mark_buffer_uptodate (bh, 0); + return bh; +} + + +int valid_offset( int fd, loff_t offset) +{ + char ch; + loff_t res; + + /*res = reiserfs_llseek (fd, offset, 0);*/ + res = lseek64 (fd, offset, 0); + if (res < 0) + return 0; + + if (read (fd, &ch, 1) < 1) + return 0; + + return 1; +} + + +struct buffer_head * reiserfs_bread (int dev, int block, int size, int *repeat) +{ + return bread (dev, block, size); +} + + +int bwrite (struct buffer_head * bh) +{ + loff_t offset; + ssize_t bytes; + size_t size; + + if (!buffer_dirty (bh) || !buffer_uptodate (bh)) + return 0; + + size = bh->b_size; + offset = (loff_t)size * (loff_t)bh->b_blocknr; + + if (lseek64 (bh->b_dev, offset, SEEK_SET) == (loff_t)-1){ + fprintf (stderr, "bwrite: lseek to position %Ld (block=%lu, dev=%d): %s\n", + offset, bh->b_blocknr, bh->b_dev, strerror (errno)); + exit (4); /* File system errors left uncorrected */ + } + + bytes = write (bh->b_dev, bh->b_data, size); + if (bytes != (ssize_t)size) { + fprintf (stderr, "bwrite: write %d bytes returned %d (block=%ld, dev=%d): %s\n", + size, bytes, bh->b_blocknr, bh->b_dev, strerror (errno)); + exit (4); + } + + mark_buffer_clean (bh); + if (bh->b_end_io) { + bh->b_end_io(bh, 1) ; + } + return 0; +} + + +void check_and_free_buffer_mem (void) +{ + int i = 0; + struct buffer_head * next = g_buffer_list_head; + + //sync_buffers (0, 0); + for (;;) { + if (!next) + die ("check_and_free_buffer_mem: buffer list is corrupted"); + if (next->b_count != 0) + fprintf (stderr, "check_and_free_buffer_mem: not free buffer (%ld, %ld, %d)", + next->b_blocknr, next->b_size, next->b_count); + + if (buffer_dirty (next) && buffer_uptodate (next)) + fprintf (stderr, "check_and_free_buffer_mem: dirty buffer %lu found\n", + next->b_blocknr); + + freemem (next->b_data); + i ++; + next = next->b_next; + if (next == g_buffer_list_head) + break; + } + if (i != g_nr_buffers) + die ("check_and_free_buffer_mem: found %d buffers, must be %d", i, g_nr_buffers); + + /* free buffer heads */ + while ((next = g_buffer_heads)) { + g_buffer_heads = *(struct buffer_head **)(next + GROW_BUFFERS__NEW_BUFERS_PER_CALL); + freemem (next); + } + + return; +} + + +/* */ +void free_buffers (void) +{ + check_and_free_buffer_mem (); +} diff --git a/lib/misc.c b/lib/misc.c new file mode 100644 index 0000000..f14fa6a --- /dev/null +++ b/lib/misc.c @@ -0,0 +1,613 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser + */ +/*#define _GNU_SOURCE*/ +/*#define _FILE_OFFSET_BITS 64*/ + +#include <stdio.h> +#include <unistd.h> +#include <stdarg.h> +#include <string.h> +#include <errno.h> +#include <asm/types.h> +#include <stdlib.h> +#include <mntent.h> +#include <sys/vfs.h> +#include <time.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include "io.h" + +/* + * These have been stolen somewhere from linux + */ +int set_bit (int nr, void * addr) +{ + __u8 * p, mask; + int retval; + + p = (__u8 *)addr; + p += nr >> 3; + mask = 1 << (nr & 0x7); + /*cli();*/ + retval = (mask & *p) != 0; + *p |= mask; + /*sti();*/ + return retval; +} + + +int clear_bit (int nr, void * addr) +{ + __u8 * p, mask; + int retval; + + p = (__u8 *)addr; + p += nr >> 3; + mask = 1 << (nr & 0x7); + /*cli();*/ + retval = (mask & *p) != 0; + *p &= ~mask; + /*sti();*/ + return retval; +} + +int test_bit(int nr, const void * addr) +{ + __u8 * p, mask; + + p = (__u8 *)addr; + p += nr >> 3; + mask = 1 << (nr & 0x7); + return ((mask & *p) != 0); +} + +int find_first_zero_bit (const void *vaddr, unsigned size) +{ + const __u8 *p = vaddr, *addr = vaddr; + int res; + + if (!size) + return 0; + + size = (size >> 3) + ((size & 0x7) > 0); + while (*p++ == 255) { + if (--size == 0) + return (p - addr) << 3; + } + + --p; + for (res = 0; res < 8; res++) + if (!test_bit (res, p)) + break; + return (p - addr) * 8 + res; +} + + +int find_next_zero_bit (const void *vaddr, unsigned size, unsigned offset) +{ + const __u8 *addr = vaddr; + const __u8 *p = addr + (offset >> 3); + int bit = offset & 7, res; + + if (offset >= size) + return size; + + if (bit) { + /* Look for zero in first char */ + for (res = bit; res < 8; res++) + if (!test_bit (res, p)) + return (p - addr) * 8 + res; + p++; + } + /* No zero yet, search remaining full bytes for a zero */ + res = find_first_zero_bit (p, size - 8 * (p - addr)); + return (p - addr) * 8 + res; +} + + +/*int test_and_set_bit (int nr, void * addr) +{ + int oldbit = test_bit (nr, addr); + set_bit (nr, addr); + return oldbit; +} + + +int test_and_clear_bit (int nr, void * addr) +{ + int oldbit = test_bit (nr, addr); + clear_bit (nr, addr); + return oldbit; +}*/ + + +void die (char * fmt, ...) +{ + static char buf[1024]; + va_list args; + + va_start (args, fmt); + vsprintf (buf, fmt, args); + va_end (args); + + fprintf (stderr, "\n%s\n\n\n", buf); + exit (-1); +} + + + +#define MEM_BEGIN "_mem_begin_" +#define MEM_END "mem_end" +#define MEM_FREED "__free_" +#define CONTROL_SIZE (strlen (MEM_BEGIN) + 1 + sizeof (int) + strlen (MEM_END) + 1) + + +static int get_mem_size (char * p) +{ + char * begin; + + begin = p - strlen (MEM_BEGIN) - 1 - sizeof (int); + return *(int *)(begin + strlen (MEM_BEGIN) + 1); +} + + +void checkmem (char * p, int size) +{ + char * begin; + char * end; + + begin = p - strlen (MEM_BEGIN) - 1 - sizeof (int); + if (strcmp (begin, MEM_BEGIN)) + die ("checkmem: memory corrupted - invalid head sign"); + + if (*(int *)(begin + strlen (MEM_BEGIN) + 1) != size) + die ("checkmem: memory corrupted - invalid size"); + + end = begin + size + CONTROL_SIZE - strlen (MEM_END) - 1; + if (strcmp (end, MEM_END)) + die ("checkmem: memory corrupted - invalid end sign"); +} + + +void * getmem (int size) +{ + char * p; + char * mem; + + p = (char *)malloc (CONTROL_SIZE + size); + if (!p) + die ("getmem: no more memory (%d)", size); + + strcpy (p, MEM_BEGIN); + p += strlen (MEM_BEGIN) + 1; + *(int *)p = size; + p += sizeof (int); + mem = p; + memset (mem, 0, size); + p += size; + strcpy (p, MEM_END); + + checkmem (mem, size); + + return mem; +} + + +void * expandmem (void * vp, int size, int by) +{ + int allocated; + char * mem, * p = vp; + int expand_by = by; + + if (p) { + checkmem (p, size); + allocated = CONTROL_SIZE + size; + p -= (strlen (MEM_BEGIN) + 1 + sizeof (int)); + } else { + allocated = 0; + /* add control bytes to the new allocated area */ + expand_by += CONTROL_SIZE; + } + p = realloc (p, allocated + expand_by); + if (!p) + die ("expandmem: no more memory (%d)", size); + if (!vp) { + strcpy (p, MEM_BEGIN); + } + mem = p + strlen (MEM_BEGIN) + 1 + sizeof (int); + + *(int *)(p + strlen (MEM_BEGIN) + 1) = size + by; + /* fill new allocated area by 0s */ + if(by > 0) + memset (mem + size, 0, by); + strcpy (mem + size + by, MEM_END); + checkmem (mem, size + by); + + return mem; +} + + +void freemem (void * vp) +{ + char * p = vp; + int size; + + if (!p) + return; + size = get_mem_size (vp); + checkmem (p, size); + + p -= (strlen (MEM_BEGIN) + 1 + sizeof (int)); + strcpy (p, MEM_FREED); + strcpy (p + size + CONTROL_SIZE - strlen (MEM_END) - 1, MEM_FREED); + free (p); +} + + + +typedef int (*func_t) (char *); + +static int is_readonly_dir (char * dir) +{ + char * name; + FILE * f; + + name = tempnam (dir, 0); + if (!name) { + fprintf (stderr, "is_readonly: tempnam failed, think fs is not readonly\n"); + return 0; + } + + f = fopen (name, "w"); + if (f) { + unlink (name); + free (name); + return 0; + } + free (name); + return (errno == EROFS) ? 1 : 0; +} + +int user_confirmed (char * q, char * yes) +{ + char * answer = 0; + size_t n = 0; + + fprintf (stderr, "%s", q); + if (getline (&answer, &n, stdin) != strlen (yes) || strcmp (yes, answer)) + return 0; + + return 1; +} + + +#include <unistd.h> +#include <linux/unistd.h> + +#define __NR_stat64 195 +_syscall2(long, stat64, char *, filename, struct stat *, statbuf); + + +static int _is_mounted (char * device_name, func_t f) +{ + int retval; + FILE *fp; + struct mntent *mnt; + struct statfs stfs; + struct stat root_st; + struct stat device_st; + /* struct stat64 device_st64;*/ + int used_stat64 = 1; + + if (stat ("/", &root_st) == -1) + die ("is_mounted: could not stat \"/\": %m\n"); + + if (stat64 (device_name, &device_st) == -1) { + used_stat64 = 0; + if (stat (device_name, &device_st) == -1) + die ("is_mounted: could not stat file \"%s\": %m", + device_name); + } + + if ((used_stat64 && !S_ISBLK (device_st.st_mode)) || !S_ISBLK (device_st.st_mode)) + /* not block device file could not be mounted */ + return 0; + + if ((used_stat64 && root_st.st_dev == device_st.st_rdev) || + root_st.st_dev == device_st.st_rdev) { + /* device is mounted as root */ + return (f ? f ("/") : 1); + } + + /* if proc filesystem is mounted */ + if (statfs ("/proc", &stfs) == -1 || stfs.f_type != 0x9fa0/*procfs magic*/ || + (fp = setmntent ("/proc/mounts", "r")) == NULL) { + /* proc filesystem is not mounted, or /proc/mounts does not + exist */ + if (f) + return (user_confirmed (" (could not figure out) Is filesystem mounted read-only? (Yes)", + "Yes\n")); + else + return (user_confirmed (" (could not figure out) Is filesystem mounted? (Yes)", + "Yes\n")); + } + + retval = 0; + while ((mnt = getmntent (fp)) != NULL) + if (strcmp (device_name, mnt->mnt_fsname) == 0) { + retval = (f ? f (mnt->mnt_dir) : 1); + break; + } + endmntent (fp); + + return retval; +} + + +int is_mounted_read_only (char * device_name) +{ + return _is_mounted (device_name, is_readonly_dir); +} + + +int is_mounted (char * device_name) +{ + return _is_mounted (device_name, 0); +} + + +char buf1 [100]; +char buf2 [100]; + +void print_how_fast (unsigned long passed, unsigned long total, + int cursor_pos, int reset_time) +{ + static time_t t0, t1; + int speed; + int indent; + + if (reset_time) + time (&t0); + + time (&t1); + if (t1 != t0) + speed = passed / (t1 - t0); + else + speed = 0; + + /* what has to be written */ + if (total) + sprintf (buf1, "left %lu, %d /sec", total - passed, speed); + else { + /*(*passed) ++;*/ + sprintf (buf1, "done %lu, %d /sec", passed, speed); + } + + /* make indent */ + indent = 79 - cursor_pos - strlen (buf1); + memset (buf2, ' ', indent); + buf2[indent] = 0; + fprintf (stderr, "%s%s", buf2, buf1); + + memset (buf2, '\b', indent + strlen (buf1)); + buf2 [indent + strlen (buf1)] = 0; + fprintf (stderr, "%s", buf2); + fflush (stderr); +} + + +static char * strs[] = +{"0%",".",".",".",".","20%",".",".",".",".","40%",".",".",".",".","60%",".",".",".",".","80%",".",".",".",".","100%"}; + +static char progress_to_be[1024]; +static char current_progress[1024]; + +static void str_to_be (char * buf, int prosents) +{ + int i; + prosents -= prosents % 4; + buf[0] = 0; + for (i = 0; i <= prosents / 4; i ++) + strcat (buf, strs[i]); +} + + +void print_how_far (unsigned long * passed, unsigned long total, + int inc, int quiet) +{ + int percent; + + if (*passed == 0) + current_progress[0] = 0; + + (*passed) += inc; + if (*passed > total) { + fprintf (stderr, "\nprint_how_far: total %lu has been reached already. cur=%lu\n", + total, *passed); + return; + } + + percent = ((*passed) * 100) / total; + + str_to_be (progress_to_be, percent); + + if (strlen (current_progress) != strlen (progress_to_be)) { + fprintf (stderr, "%s", progress_to_be + strlen (current_progress)); + } + + strcat (current_progress, progress_to_be + strlen (current_progress)); + + if (!quiet) + print_how_fast (*passed/* - inc*/, total, strlen (progress_to_be), + (*passed == inc) ? 1 : 0); + + fflush (stderr); +} + + + +#define ENDIANESS_NOT_DEFINED 0 +#define LITTLE_ENDIAN_ARCH 1 +#define BIG_ENDIAN_ARCH 2 + +static int endianess = ENDIANESS_NOT_DEFINED; + + +static void find_endianess (void) +{ + __u32 x = 0x0f0d0b09; + char * s; + + s = (char *)&x; + + // little-endian is 1234 + if (s[0] == '\11' && s[1] == '\13' && s[2] == '\15' && s[3] == '\17') + endianess = LITTLE_ENDIAN_ARCH; + + // big-endian is 4321 + if (s[0] == '\17' && s[1] == '\15' && s[2] == '\13' && s[3] == '\11') + die ("big-endian archs are not supported"); + + // nuxi/pdp-endian is 3412 + if (s[0] == '\15' && s[1] == '\17' && s[2] == '\11' && s[3] == '\13') + die ("nuxi/pdp-endian archs are not supported"); +} + + +// we used to use such function in the kernel stuff of reiserfs. Lets +// have them in utils as well +inline __u32 cpu_to_le32 (__u32 val) +{ + if (endianess == ENDIANESS_NOT_DEFINED) + find_endianess (); + + if (endianess == LITTLE_ENDIAN_ARCH) + return val; + + die ("neither big- nor any other endian archs are supported yet "); + + return ((val>>24) | ((val>>8)&0xFF00) | + ((val<<8)&0xFF0000) | (val<<24)); +} + + +inline __u32 le32_to_cpu (__u32 val) +{ + return cpu_to_le32 (val); +} + + +inline __u16 cpu_to_le16 (__u16 val) +{ + return val; + + if (endianess == ENDIANESS_NOT_DEFINED) + find_endianess (); + + if (endianess == LITTLE_ENDIAN_ARCH) + return val; + die ("neither big- nor pdp- endian arch are supported yet "); + + return (val >> 8) | (val << 8); +} + + +inline __u16 le16_to_cpu (__u16 val) +{ + /*printf ("%s:%u %p %p %p\n", __FILE__, __LINE__, + __builtin_return_address (0), + __builtin_return_address (1), + __builtin_return_address (2));*/ + return val; + return cpu_to_le16 (val); +} + + +inline __u64 cpu_to_le64 (__u64 val) +{ + if (endianess == ENDIANESS_NOT_DEFINED) + find_endianess (); + + if (endianess == LITTLE_ENDIAN_ARCH) + return val; + die ("neither big- nor pdp- endian arch are supported yet "); + + return 0; +} + + +inline __u64 le64_to_cpu (__u64 val) +{ + return cpu_to_le64 (val); +} + + +/* Given a file descriptor and an offset, check whether the offset is + a valid offset for the file - return 0 if it isn't valid or 1 if it + is */ +loff_t reiserfs_llseek (unsigned int fd, loff_t offset, unsigned int origin); +#if 0 +static int valid_offset( int fd, loff_t offset ) +{ + char ch; + loff_t res; + + /*res = reiserfs_llseek (fd, offset, 0);*/ + res = lseek64 (fd, offset, 0); + if (res < 0) + return 0; + + if (read (fd, &ch, 1) < 1) + return 0; + + return 1; +} +#endif + +/* calculates number of blocks on device */ +unsigned long count_blocks (char * filename, int blocksize, int fd) +{ + loff_t high, low; + int opened_here = 0; + + if (fd < 0) { + fd = open (filename, O_RDONLY); + opened_here = 1; + } + if (fd < 0) + die ("count_blocks: open failed (%s)", strerror (errno)); + +#ifdef BLKGETSIZE + { + long size; + + if (ioctl (fd, BLKGETSIZE, &size) >= 0) { + if (opened_here) + close (fd); + return size / (blocksize / 512); + } + } +#endif + + low = 0; + for( high = 1; valid_offset (fd, high); high *= 2 ) + low = high; + while (low < high - 1) { + const loff_t mid = ( low + high ) / 2; + + if (valid_offset (fd, mid)) + low = mid; + else + high = mid; + } + valid_offset (fd, 0); + if (opened_here) + close (fd); + + return (low + 1) / (blocksize); +} + + + @@ -0,0 +1,190 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997 Free Software Foundation, Inc. +# Franc,ois Pinard <pinard@iro.umontreal.ca>, 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing - GNU libit 0.0" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`configure.in'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`configure.in'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`configure.in'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' configure.in` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`configure.in'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequirements for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff --git a/mkinstalldirs b/mkinstalldirs new file mode 100755 index 0000000..dd68e26 --- /dev/null +++ b/mkinstalldirs @@ -0,0 +1,40 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman <friedman@prep.ai.mit.edu> +# Created: 1993-05-16 +# Public domain + +# $Id: mkinstalldirs,v 1.1.1.1 2000/08/03 10:35:16 vs Exp $ + +errstatus=0 + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case "$pathcomp" in + -* ) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# mkinstalldirs ends here diff --git a/mkreiserfs/Makefile.am b/mkreiserfs/Makefile.am new file mode 100644 index 0000000..54198db --- /dev/null +++ b/mkreiserfs/Makefile.am @@ -0,0 +1,9 @@ +sbin_PROGRAMS = mkreiserfs + +mkreiserfs_SOURCES = mkreiserfs.c +man_MANS = mkreiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include diff --git a/mkreiserfs/Makefile.in b/mkreiserfs/Makefile.in new file mode 100644 index 0000000..f73f030 --- /dev/null +++ b/mkreiserfs/Makefile.in @@ -0,0 +1,325 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +sbin_PROGRAMS = mkreiserfs + +mkreiserfs_SOURCES = mkreiserfs.c +man_MANS = mkreiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +PROGRAMS = $(sbin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +mkreiserfs_OBJECTS = mkreiserfs.o +mkreiserfs_LDADD = $(LDADD) +mkreiserfs_DEPENDENCIES = ../lib/libmisc.a ../reiserfscore/libcore.a +mkreiserfs_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +man8dir = $(mandir)/man8 +MANS = $(man_MANS) + +NROFF = nroff +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(mkreiserfs_SOURCES) +OBJECTS = $(mkreiserfs_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps mkreiserfs/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +mkreiserfs: $(mkreiserfs_OBJECTS) $(mkreiserfs_DEPENDENCIES) + @rm -f mkreiserfs + $(LINK) $(mkreiserfs_LDFLAGS) $(mkreiserfs_OBJECTS) $(mkreiserfs_LDADD) $(LIBS) + +install-man8: + $(mkinstalldirs) $(DESTDIR)$(man8dir) + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \ + done + +uninstall-man8: + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \ + rm -f $(DESTDIR)$(man8dir)/$$inst; \ + done +install-man: $(MANS) + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-man8 +uninstall-man: + @$(NORMAL_UNINSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-man8 + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = mkreiserfs + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +mkreiserfs.o: mkreiserfs.c ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-sbinPROGRAMS +install-exec: install-exec-am + +install-data-am: install-man +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-sbinPROGRAMS uninstall-man +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) $(MANS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man8 + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-sbinPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \ +clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ +install-sbinPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile install-man8 uninstall-man8 \ +install-man uninstall-man tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/mkreiserfs/mkreiserfs.8 b/mkreiserfs/mkreiserfs.8 new file mode 100644 index 0000000..4509f42 --- /dev/null +++ b/mkreiserfs/mkreiserfs.8 @@ -0,0 +1,62 @@ +.\" -*- nroff -*- +.\" Copyright 1996-2001 Hans Reiser. +.\" +.TH MKREISERFS 8 "March 2001" "Reiserfsprogs-3.x.0j" +.SH NAME +mkreiserfs \- create a Linux Reiserfs file system +.SH SYNOPSIS +.B mkreiserfs +[ +.B -h +.I r5 +| +.I tea +| +.I rupasov +] [ +.B \-v +.I 1 +| +.I 2 +] [ +.B -q +] +.I device +[ +.I size-in-blocks +] +.SH DESCRIPTION +It creates a Linux Reiserfs file system on a device +(usually a disk partition). +.TP +.I device +is the special file corresponding to the device (e.g /dev/hdXX for +IDE disk partition or /dev/sdXX for SCSI disk partition). +.TP +.I block-count +is the number of blocks on the device. If omitted, it will be +determined by +.B mkreiserfs +automatically. +.SH OPTIONS +.TP +\fB\-h \fIr5\fR |\fI tea\fR |\fI rupasov +This specifies the name of hash function file names in directories +will be sorted with. Choose one of the above. 'r5' is default. +.TP +\fB\-v \fI1\fR |\fI 2 +This specifies format new filsystem has to be of. +.TP +\fB\-q\fR +This makes the progress bar much less verbose. Useful when +logged in via a slow link (e.g. serial console). +.SH AUTHOR +This version of +.B mkreiserfs +has been written by Hans Reiser <reiser@idiom.com>. +.SH BUGS +No other blocksizes but 4k are available. +Please, report about other bugs to Hans Reiser <reiser@idiom.com>. +.SH SEE ALSO +.BR reiserfsck (8), +.BR debugreiserfs (8) diff --git a/mkreiserfs/mkreiserfs.c b/mkreiserfs/mkreiserfs.c new file mode 100644 index 0000000..a907d19 --- /dev/null +++ b/mkreiserfs/mkreiserfs.c @@ -0,0 +1,504 @@ +/* + * Copyright 1996, 1997, 1998, 1999 Hans Reiser + */ + +/* mkreiserfs is very simple. It supports only 4 and 8K blocks. It skips + first 64k of device, and then writes the super + block, the needed amount of bitmap blocks (this amount is calculated + based on file system size), and root block. Bitmap policy is + primitive: it assumes, that device does not have unreadable blocks, + and it occupies first blocks for super, bitmap and root blocks. + bitmap blocks are interleaved across the disk, mainly to make + resizing faster. */ + +// +// FIXME: not 'not-i386' safe +// +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <asm/types.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/vfs.h> +#include <time.h> +#include <sys/ioctl.h> +#include <sys/mount.h> +#include <linux/major.h> +#include <sys/stat.h> +#include <linux/kdev_t.h> + +#include "io.h" +#include "misc.h" +#include "reiserfs_lib.h" +#include "../version.h" + + +#define print_usage_and_exit() die ("Usage: %s [ -f ] [ -h tea | rupasov | r5 ]"\ + " [ -v 1 | 2] [ -q ] device [block-count]\n\n", argv[0]) + + +#define DEFAULT_BLOCKSIZE 4096 + + + + +struct buffer_head * g_sb_bh; +struct buffer_head * g_bitmap_bh; +struct buffer_head * g_rb_bh; +struct buffer_head * g_journal_bh ; + + +int g_block_size = DEFAULT_BLOCKSIZE; +unsigned long int g_block_number; +int g_hash = DEFAULT_HASH; +int g_3_6_format = 1; /* new format is default */ + +int quiet = 0; + +/* reiserfs needs at least: enough blocks for journal, 64 k at the beginning, + one block for super block, bitmap block and root block */ +static unsigned long min_block_amount (int block_size, unsigned long journal_size) +{ + unsigned long blocks; + + blocks = REISERFS_DISK_OFFSET_IN_BYTES / block_size + + 1 + 1 + 1 + journal_size; + if (blocks > block_size * 8) + die ("mkreiserfs: journal size specified incorrectly"); + + return blocks; +} + + +/* form super block (old one) */ +static void make_super_block (int dev) +{ + struct reiserfs_super_block * rs; + int sb_size = g_3_6_format ? SB_SIZE : SB_SIZE_V1; + __u32 * oids; + + + if (SB_SIZE > g_block_size) + die ("mkreiserfs: blocksize (%d) too small", g_block_size); + + /* get buffer for super block */ + g_sb_bh = getblk (dev, REISERFS_DISK_OFFSET_IN_BYTES / g_block_size, g_block_size); + + rs = (struct reiserfs_super_block *)g_sb_bh->b_data; + set_blocksize (rs, g_block_size); + set_block_count (rs, g_block_number); + set_state (rs, REISERFS_VALID_FS); + set_tree_height (rs, 2); + + set_bmap_nr (rs, (g_block_number + (g_block_size * 8 - 1)) / (g_block_size * 8)); + set_version (rs, g_3_6_format ? REISERFS_VERSION_2 : REISERFS_VERSION_1); + + set_hash (rs, g_hash); + + // journal things + rs->s_v1.s_journal_dev = cpu_to_le32 (0) ; + rs->s_v1.s_orig_journal_size = cpu_to_le32 (JOURNAL_BLOCK_COUNT) ; + rs->s_v1.s_journal_trans_max = cpu_to_le32 (0) ; + rs->s_v1.s_journal_block_count = cpu_to_le32 (0) ; + rs->s_v1.s_journal_max_batch = cpu_to_le32 (0) ; + rs->s_v1.s_journal_max_commit_age = cpu_to_le32 (0) ; + rs->s_v1.s_journal_max_trans_age = cpu_to_le32 (0) ; + + // the differences between sb V1 and sb V2 are: magic string + memcpy (rs->s_v1.s_magic, g_3_6_format ? REISER2FS_SUPER_MAGIC_STRING : REISERFS_SUPER_MAGIC_STRING, + strlen (g_3_6_format ? REISER2FS_SUPER_MAGIC_STRING : REISERFS_SUPER_MAGIC_STRING)); + // start of objectid map + oids = (__u32 *)((char *)rs + sb_size); + + // max size of objectid map + rs->s_v1.s_oid_maxsize = cpu_to_le16 ((g_block_size - sb_size) / sizeof(__u32) / 2 * 2); + + oids[0] = cpu_to_le32 (1); + oids[1] = cpu_to_le32 (REISERFS_ROOT_OBJECTID + 1); + set_objectid_map_size (rs, 2); + + mark_buffer_dirty (g_sb_bh); + mark_buffer_uptodate (g_sb_bh, 1); + return; + +} + + +void zero_journal_blocks(int dev, int start, int len) { + int i ; + struct buffer_head *bh ; + unsigned long done = 0; + + printf ("Initializing journal - "); fflush (stdout); + + for (i = 0 ; i < len ; i++) { + print_how_far (&done, len, 1, quiet); + + bh = getblk (dev, start + i, g_block_size) ; + memset(bh->b_data, 0, g_block_size) ; + mark_buffer_dirty(bh) ; + mark_buffer_uptodate(bh,0) ; + bwrite (bh); + brelse(bh) ; + } + printf ("\n"); fflush (stdout); +} + + +/* this only sets few first bits in bitmap block. Fills not initialized fields + of super block (root block and bitmap block numbers) */ +static void make_bitmap (void) +{ + struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data; + int i, j; + + /* get buffer for bitmap block */ + g_bitmap_bh = getblk (g_sb_bh->b_dev, g_sb_bh->b_blocknr + 1, g_sb_bh->b_size); + + /* mark, that first 8K of device is busy */ + for (i = 0; i < REISERFS_DISK_OFFSET_IN_BYTES / g_block_size; i ++) + set_bit (i, g_bitmap_bh->b_data); + + /* mark that super block is busy */ + set_bit (i++, g_bitmap_bh->b_data); + + /* mark first bitmap block as busy */ + set_bit (i ++, g_bitmap_bh->b_data); + + /* sb->s_journal_block = g_block_number - JOURNAL_BLOCK_COUNT ; */ /* journal goes at end of disk */ + set_journal_start (rs, i); + + /* mark journal blocks as busy BUG! we need to check to make sure journal + will fit in the first bitmap block */ + for (j = 0 ; j < (JOURNAL_BLOCK_COUNT + 1); j++) /* the descriptor block goes after the journal */ + set_bit (i ++, g_bitmap_bh->b_data); + + /* and tree root is busy */ + set_bit (i, g_bitmap_bh->b_data); + + set_root_block (rs, i); + set_free_blocks (rs, rs_block_count (rs) - i - 1); + + /* count bitmap blocks not resides in first s_blocksize blocks - ?? */ + set_free_blocks (rs, rs_free_blocks (rs) - (rs_bmap_nr (rs) - 1)); + + mark_buffer_dirty (g_bitmap_bh); + mark_buffer_uptodate (g_bitmap_bh, 0); + + mark_buffer_dirty (g_sb_bh); + return; +} + + +/* form the root block of the tree (the block head, the item head, the + root directory) */ +static void make_root_block (void) +{ + struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data; + char * rb; + struct item_head * ih; + + /* get memory for root block */ + g_rb_bh = getblk (g_sb_bh->b_dev, rs_root_block (rs), rs_blocksize (rs)); + rb = g_rb_bh->b_data; + + /* block head */ + set_leaf_node_level (g_rb_bh); + set_node_item_number (g_rb_bh, 0); + set_node_free_space (g_rb_bh, rs_blocksize (rs) - BLKH_SIZE); + + /* first item is stat data item of root directory */ + ih = (struct item_head *)(g_rb_bh->b_data + BLKH_SIZE); + + make_dir_stat_data (g_block_size, g_3_6_format ? KEY_FORMAT_2 : KEY_FORMAT_1, + REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID, + ih, g_rb_bh->b_data + g_block_size - (g_3_6_format ? SD_SIZE : SD_V1_SIZE)); + set_ih_location (ih, g_block_size - ih_item_len (ih)); + + // adjust block head + set_node_item_number (g_rb_bh, node_item_number (g_rb_bh) + 1); + set_node_free_space (g_rb_bh, node_free_space (g_rb_bh) - (IH_SIZE + ih_item_len (ih))); + + + /* second item is root directory item, containing "." and ".." */ + ih ++; + ih->ih_key.k_dir_id = cpu_to_le32 (REISERFS_ROOT_PARENT_OBJECTID); + ih->ih_key.k_objectid = cpu_to_le32 (REISERFS_ROOT_OBJECTID); + ih->ih_key.u.k_offset_v1.k_offset = cpu_to_le32 (DOT_OFFSET); + ih->ih_key.u.k_offset_v1.k_uniqueness = cpu_to_le32 (DIRENTRY_UNIQUENESS); + ih->ih_item_len = cpu_to_le16 (g_3_6_format ? EMPTY_DIR_SIZE : EMPTY_DIR_SIZE_V1); + ih->ih_item_location = cpu_to_le16 (ih_location (ih-1) - ih_item_len (ih)); + ih->u.ih_entry_count = cpu_to_le16 (2); + set_key_format (ih, KEY_FORMAT_1); + + if (g_3_6_format) + make_empty_dir_item (g_rb_bh->b_data + ih_location (ih), + REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID, + 0, REISERFS_ROOT_PARENT_OBJECTID); + else + make_empty_dir_item_v1 (g_rb_bh->b_data + ih_location (ih), + REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID, + 0, REISERFS_ROOT_PARENT_OBJECTID); + + // adjust block head + set_node_item_number (g_rb_bh, node_item_number (g_rb_bh) + 1); + set_node_free_space (g_rb_bh, node_free_space (g_rb_bh) - (IH_SIZE + ih_item_len (ih))); + + + print_block (stdout, 0, g_rb_bh, 3, -1, -1); + + mark_buffer_dirty (g_rb_bh); + mark_buffer_uptodate (g_rb_bh, 0); + return; +} + + +/* + * write the super block, the bitmap blocks and the root of the tree + */ +static void write_super_and_root_blocks (void) +{ + struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data; + int i; + + zero_journal_blocks(g_sb_bh->b_dev, rs_journal_start (rs), JOURNAL_BLOCK_COUNT + 1) ; + + /* super block */ + bwrite (g_sb_bh); + + /* bitmap blocks */ + for (i = 0; i < rs_bmap_nr (rs); i ++) { + if (i != 0) { + g_bitmap_bh->b_blocknr = i * rs_blocksize (rs) * 8; + memset (g_bitmap_bh->b_data, 0, g_bitmap_bh->b_size); + set_bit (0, g_bitmap_bh->b_data); + } + if (i == rs_bmap_nr (rs) - 1) { + int j; + + /* fill unused part of last bitmap block with 1s */ + if (rs_block_count (rs) % (rs_blocksize (rs) * 8)) + for (j = rs_block_count (rs) % (rs_blocksize (rs) * 8); j < rs_blocksize (rs) * 8; j ++) { + set_bit (j, g_bitmap_bh->b_data); + } + } + /* write bitmap */ + mark_buffer_dirty (g_bitmap_bh); + bwrite (g_bitmap_bh); + } + + /* root block */ + bwrite (g_rb_bh); + brelse (g_rb_bh); + brelse (g_bitmap_bh); + brelse (g_sb_bh); +} + + +static void report (char * devname) +{ + struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data; + unsigned int i; + + printf ("Creating reiserfs of %s format\n", g_3_6_format ? "3.6" : "3.5"); + printf ("Block size %d bytes\n", rs_blocksize (rs)); + printf ("Block count %d\n", rs_block_count (rs)); + printf ("Used blocks %d\n", rs_block_count (rs) - rs_free_blocks (rs)); + printf ("Free blocks count %d\n", rs_free_blocks (rs)); + printf ("First %ld blocks skipped\n", g_sb_bh->b_blocknr); + printf ("Super block is in %ld\n", g_sb_bh->b_blocknr); + printf ("Bitmap blocks (%d) are : \n\t%ld", rs_bmap_nr (rs), g_bitmap_bh->b_blocknr); + for (i = 1; i < rs_bmap_nr (rs); i ++) { + printf (", %d", i * rs_blocksize (rs) * 8); + } + printf ("\nJournal size %d (blocks %d-%d of file %s)\n", + JOURNAL_BLOCK_COUNT, rs_journal_start (rs), + rs_journal_start (rs) + JOURNAL_BLOCK_COUNT, devname); + printf ("Root block %u\n", rs_root_block (rs)); + printf ("Hash function \"%s\"\n", g_hash == TEA_HASH ? "tea" : + ((g_hash == YURA_HASH) ? "rupasov" : "r5")); + fflush (stdout); +} + + +/* wipe out first 2 k of a device and both possible reiserfs super block */ +static void invalidate_other_formats (int dev) +{ + struct buffer_head * bh; + + bh = getblk (dev, 0, 2048); + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); + brelse (bh); + + bh = getblk(dev, REISERFS_OLD_DISK_OFFSET_IN_BYTES / 1024, 1024) ; + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); + brelse (bh); + + bh = getblk(dev, REISERFS_DISK_OFFSET_IN_BYTES / 1024, 1024) ; + mark_buffer_uptodate (bh, 1); + mark_buffer_dirty (bh); + bwrite (bh); + brelse (bh); +} + + +static void set_hash_function (char * str) +{ + if (!strcmp (str, "tea")) + g_hash = TEA_HASH; + else if (!strcmp (str, "rupasov")) + g_hash = YURA_HASH; + else if (!strcmp (str, "r5")) + g_hash = R5_HASH; + else + printf ("mkreiserfs: wrong hash type specified. Using default\n"); +} + + +static void set_reiserfs_version (char * str) +{ + if (!strcmp (str, "1")) + g_3_6_format = 0; + else if (!strcmp (str, "2")) + g_3_6_format = 1; + else + printf ("mkreiserfs: wrong reiserfs version specified. Using default 3.5 format\n"); +} + + +int main (int argc, char **argv) +{ + char *tmp; + int dev; + int force = 0; + struct stat st; + char * device_name; + char c; + + print_banner ("mkreiserfs"); + + if (argc < 2) + print_usage_and_exit (); + + + while ( ( c = getopt( argc, argv, "fh:v:q" ) ) != EOF ) + switch( c ) + { + case 'f' : /* force if file is not a block device or fs is + mounted. Confirm still required */ + force = 1; + break; + + case 'h': + set_hash_function (optarg); + break; + + case 'v': + set_reiserfs_version (optarg); + break; + + case 'q': + quiet = 1; + break; + + default : + print_usage_and_exit (); + } + device_name = argv [optind]; + + + /* get block number for file system */ + if (optind == argc - 2) { + g_block_number = strtol (argv[optind + 1], &tmp, 0); + if (*tmp == 0) { /* The string is integer */ + if (g_block_number > count_blocks (device_name, g_block_size, -1)) + die ("mkreiserfs: specified block number (%d) is too high", g_block_number); + } else { + die ("mkreiserfs: bad block count : %s\n", argv[optind + 1]); + } + } else + if (optind == argc - 1) { + /* number of blocks is not specified */ + g_block_number = count_blocks (device_name, g_block_size, -1); + tmp = ""; + } else + print_usage_and_exit (); + + + /*g_block_number = g_block_number / 8 * 8;*/ + + if (g_block_number < min_block_amount (g_block_size, JOURNAL_BLOCK_COUNT + 1)) + die ("mkreiserfs: can not create filesystem on that small device (%lu blocks).\n" + "It should have at least %lu blocks", + g_block_number, min_block_amount (g_block_size, JOURNAL_BLOCK_COUNT + 1)); + + if (is_mounted (device_name)) { + printf ("mkreiserfs: '%s' contains a mounted file system\n", device_name); + if (!force) + exit (1); + if (!user_confirmed ("Forced to continue, but please confirm (y/n)", "y")) + exit (1); + } + + dev = open (device_name, O_RDWR); + if (dev == -1) + die ("mkreiserfs: can not open '%s': %s", device_name, strerror (errno)); + + if (fstat (dev, &st) < 0) + die ("mkreiserfs: unable to stat %s", device_name); + + if (!S_ISBLK (st.st_mode)) { + printf ("mkreiserfs: %s is not a block special device.\n", device_name); + if (!force) { + exit (1); + } + if (!user_confirmed ("Forced to continue, but please confirm (y/n)", "y")) + exit (1); + } else { + // from e2progs-1.18/misc/mke2fs.c + if ((MAJOR (st.st_rdev) == HD_MAJOR && MINOR (st.st_rdev)%64 == 0) || + (SCSI_BLK_MAJOR (MAJOR(st.st_rdev)) && MINOR (st.st_rdev) % 16 == 0)) { + printf ("mkreiserfs: %s is entire device, not just one partition! Continue? (y/n) ", + device_name); + if (!user_confirmed ("Continue (y/n)", "y")) + exit (1); + } + } + + /* these fill buffers (super block, first bitmap, root block) with + reiserfs structures */ + make_super_block (dev); + make_bitmap (); + make_root_block (); + + report (device_name); + + printf ("ATTENTION: YOU SHOULD REBOOT AFTER FDISK!\n\t ALL DATA WILL BE LOST ON '%s'! ", device_name); + if (!user_confirmed ("(y/n)", "y\n")) + die ("mkreiserfs: Disk was not formatted"); + + invalidate_other_formats (dev); + write_super_and_root_blocks (); + + check_and_free_buffer_mem (); + + printf ("Syncing.."); fflush (stdout); + + close(dev) ; + sync (); + + printf ("\n\nReiserFS core development sponsored by SuSE Labs (suse.com)\n\n" + "Journaling sponsored by MP3.com.\n\n" + //"Item handlers sponsored by Ecila.com\n\n + "To learn about the programmers and ReiserFS, please go to\n" + "http://www.devlinux.com/namesys\n\nHave fun.\n\n"); + fflush (stdout); + return 0; +} diff --git a/reiserfscore/Makefile.am b/reiserfscore/Makefile.am new file mode 100644 index 0000000..3dc78bc --- /dev/null +++ b/reiserfscore/Makefile.am @@ -0,0 +1,5 @@ +noinst_LIBRARIES = libcore.a + +libcore_a_SOURCES = do_balan.c fix_node.c hashes.c ibalance.c lbalance.c prints.c stree.c node_formats.c reiserfslib.c bitmap.c includes.h + +INCLUDES = -I../include diff --git a/reiserfscore/Makefile.in b/reiserfscore/Makefile.in new file mode 100644 index 0000000..967298f --- /dev/null +++ b/reiserfscore/Makefile.in @@ -0,0 +1,280 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +noinst_LIBRARIES = libcore.a + +libcore_a_SOURCES = do_balan.c fix_node.c hashes.c ibalance.c lbalance.c prints.c stree.c node_formats.c reiserfslib.c bitmap.c includes.h + +INCLUDES = -I../include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +LIBRARIES = $(noinst_LIBRARIES) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +libcore_a_LIBADD = +libcore_a_OBJECTS = do_balan.o fix_node.o hashes.o ibalance.o \ +lbalance.o prints.o stree.o node_formats.o reiserfslib.o bitmap.o +AR = ar +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(libcore_a_SOURCES) +OBJECTS = $(libcore_a_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps reiserfscore/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-noinstLIBRARIES: + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) + +distclean-noinstLIBRARIES: + +maintainer-clean-noinstLIBRARIES: + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +libcore.a: $(libcore_a_OBJECTS) $(libcore_a_DEPENDENCIES) + -rm -f libcore.a + $(AR) cru libcore.a $(libcore_a_OBJECTS) $(libcore_a_LIBADD) + $(RANLIB) libcore.a + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = reiserfscore + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +bitmap.o: bitmap.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +do_balan.o: do_balan.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +fix_node.o: fix_node.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +hashes.o: hashes.c +ibalance.o: ibalance.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +lbalance.o: lbalance.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +node_formats.o: node_formats.c includes.h ../include/io.h \ + ../include/misc.h ../include/reiserfs_lib.h \ + ../include/reiserfs_fs.h +prints.o: prints.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h +reiserfslib.o: reiserfslib.c includes.h ../include/io.h \ + ../include/misc.h ../include/reiserfs_lib.h \ + ../include/reiserfs_fs.h +stree.o: stree.c includes.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: +install-exec: install-exec-am + +install-data-am: +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: +uninstall: uninstall-am +all-am: Makefile $(LIBRARIES) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-noinstLIBRARIES mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-noinstLIBRARIES clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-noinstLIBRARIES distclean-compile \ + distclean-tags distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-noinstLIBRARIES \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-noinstLIBRARIES distclean-noinstLIBRARIES \ +clean-noinstLIBRARIES maintainer-clean-noinstLIBRARIES \ +mostlyclean-compile distclean-compile clean-compile \ +maintainer-clean-compile tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/reiserfscore/bitmap.c b/reiserfscore/bitmap.c new file mode 100644 index 0000000..4318c08 --- /dev/null +++ b/reiserfscore/bitmap.c @@ -0,0 +1,448 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +/* + * 2000/10/26 - Initial version. + */ + +#include <assert.h> +#include "includes.h" + + +/* create clean bitmap */ +reiserfs_bitmap_t reiserfs_create_bitmap (unsigned int bit_count) +{ + reiserfs_bitmap_t bm; + + bm = getmem (sizeof (*bm)); + if (!bm) + return 0; + bm->bm_bit_size = bit_count; + bm->bm_byte_size = (bit_count + 7) / 8; + bm->bm_set_bits = 0; + bm->bm_map = getmem (bm->bm_byte_size); + if (!bm->bm_map) { + freemem (bm); + return 0; + } + + return bm; +} + +/* Expand existing bitmap. Return non-zero if can't. FIXME: it is + assumed that bit_count is new number of blocks to be addressed */ +int reiserfs_expand_bitmap (reiserfs_bitmap_t bm, unsigned int bit_count) +{ + unsigned int byte_count = ((bit_count + 7) / 8); + char * new_map; + + new_map = expandmem (bm->bm_map, bm->bm_byte_size, + byte_count - bm->bm_byte_size); + + if (!new_map) { + return 1; + } + + bm->bm_map = new_map; + bm->bm_byte_size = byte_count; + bm->bm_bit_size = bit_count; + return 0; +} + +/* bitmap destructor */ +void reiserfs_delete_bitmap (reiserfs_bitmap_t bm) +{ + freemem(bm->bm_map); + bm->bm_map = NULL; /* to not reuse bitmap handle */ + bm->bm_bit_size = 0; + bm->bm_byte_size = 0; + freemem(bm); +} + + +void reiserfs_bitmap_copy (reiserfs_bitmap_t to, reiserfs_bitmap_t from) +{ + assert (to->bm_byte_size == from->bm_byte_size); + memcpy (to->bm_map, from->bm_map, from->bm_byte_size); + to->bm_bit_size = from->bm_bit_size; + to->bm_set_bits = from->bm_set_bits; +} + + +int reiserfs_bitmap_compare (reiserfs_bitmap_t bm1, reiserfs_bitmap_t bm2) +{ + int bytes, bits; + int i, diff; + + assert (bm1->bm_byte_size == bm2->bm_byte_size && + bm1->bm_bit_size == bm2->bm_bit_size); + + diff = 0; + + /* compare full bytes */ + bytes = bm1->bm_bit_size / 8; + if (memcmp (bm1->bm_map, bm2->bm_map, bytes)) { + for (i = 0; i < bytes; i ++) + if (bm1->bm_map [i] != bm2->bm_map[i]) { + printf ("byte %d: bm1: %x bm2 %x\n", i, bm1->bm_map[i], bm2->bm_map[i]); + diff ++; + } + } + + /* compare last byte of bitmap which can be used partially */ + bits = bm1->bm_bit_size % 8; + if (bits) { + int mask; + + mask = 255 >> (8 - bits); + if ((bm1->bm_map [bytes] & mask) != (bm2->bm_map [bytes] & mask)) { + printf ("last byte %d: bm1: %x bm2 %x\n", bytes, bm1->bm_map[bytes], bm2->bm_map[bytes]); + diff ++; + } + } + return diff; +} + + +void reiserfs_bitmap_set_bit (reiserfs_bitmap_t bm, unsigned int bit_number) +{ + assert(bit_number < bm->bm_bit_size); + if (test_bit (bit_number, bm->bm_map)) + return; + set_bit(bit_number, bm->bm_map); + bm->bm_set_bits ++; +} + + +void reiserfs_bitmap_clear_bit (reiserfs_bitmap_t bm, unsigned int bit_number) +{ + assert(bit_number < bm->bm_bit_size); + if (!test_bit (bit_number, bm->bm_map)) + return; + clear_bit (bit_number, bm->bm_map); + bm->bm_set_bits --; +} + + +int reiserfs_bitmap_test_bit (reiserfs_bitmap_t bm, unsigned int bit_number) +{ + if (bit_number >= bm->bm_bit_size) + printf ("bit %u, bitsize %lu\n", bit_number, bm->bm_bit_size); + assert(bit_number < bm->bm_bit_size); + return test_bit(bit_number, bm->bm_map); +} + + +int reiserfs_bitmap_zeros (reiserfs_bitmap_t bm) +{ + return bm->bm_bit_size - bm->bm_set_bits; +} + + +int reiserfs_bitmap_ones (reiserfs_bitmap_t bm) +{ + return bm->bm_set_bits; +} + + +int reiserfs_bitmap_find_zero_bit (reiserfs_bitmap_t bm, unsigned long * start) +{ + unsigned int bit_nr = *start; + assert(*start < bm->bm_bit_size); + + bit_nr = find_next_zero_bit(bm->bm_map, bm->bm_bit_size, *start); + + if (bit_nr >= bm->bm_bit_size) { /* search failed */ + return 1; + } + + *start = bit_nr; + return 0; +} + + +/* copy reiserfs filesystem bitmap into memory bitmap */ +int reiserfs_fetch_disk_bitmap (reiserfs_bitmap_t bm, reiserfs_filsys_t fs) +{ + int i; + int bytes; + char * p; + int unused_bits; + + reiserfs_warning (stderr, "Fetching on-disk bitmap.."); + assert (bm->bm_bit_size == SB_BLOCK_COUNT (fs)); + + bytes = fs->s_blocksize; + p = bm->bm_map; + for (i = 0; i < SB_BMAP_NR (fs); i ++) { + if ((i == (SB_BMAP_NR (fs) - 1)) && bm->bm_byte_size % fs->s_blocksize) + bytes = bm->bm_byte_size % fs->s_blocksize; + + memcpy (p, SB_AP_BITMAP (fs)[i]->b_data, bytes); + p += bytes; + } + + /* on disk bitmap has bits out of SB_BLOCK_COUNT set to 1, where as + reiserfs_bitmap_t has those bits set to 0 */ + unused_bits = bm->bm_byte_size * 8 - bm->bm_bit_size; + for (i = 0; i < unused_bits; i ++) + clear_bit (bm->bm_bit_size + i, bm->bm_map); + + bm->bm_set_bits = 0; + /* FIXME: optimize that */ + for (i = 0; i < bm->bm_bit_size; i ++) + if (reiserfs_bitmap_test_bit (bm, i)) + bm->bm_set_bits ++; + + /* unused part of last bitmap block is filled with 0s */ + if (bm->bm_bit_size % (fs->s_blocksize * 8)) + for (i = SB_BLOCK_COUNT (fs) % (fs->s_blocksize * 8); i < fs->s_blocksize * 8; i ++) + if (!test_bit (i, SB_AP_BITMAP (fs)[SB_BMAP_NR (fs) - 1]->b_data)) { + reiserfs_warning (stderr, "fetch_bitmap: on-disk bitmap is not padded properly\n"); + break; + } + + reiserfs_warning (stderr, "done\n"); + return 0; +} + + +/* copy bitmap to buffers which hold on-disk bitmap */ +int reiserfs_flush_bitmap (reiserfs_bitmap_t bm, reiserfs_filsys_t fs) +{ + int i; + int bytes; + char * p; + + bytes = fs->s_blocksize; + p = bm->bm_map; + for (i = 0; i < SB_BMAP_NR (fs); i ++) { + if ((i == (SB_BMAP_NR (fs) - 1)) && (bm->bm_byte_size % fs->s_blocksize)) + bytes = bm->bm_byte_size % fs->s_blocksize; + + memcpy (SB_AP_BITMAP (fs)[i]->b_data, p, bytes); + mark_buffer_dirty (SB_AP_BITMAP (fs)[i]); + + p += bytes; + } + + /* unused part of last bitmap block is filled with 0s */ + if (bm->bm_bit_size % (fs->s_blocksize * 8)) + for (i = bm->bm_bit_size % (fs->s_blocksize * 8); i < fs->s_blocksize * 8; i ++) + set_bit (i, SB_AP_BITMAP (fs)[SB_BMAP_NR (fs) - 1]->b_data); + + return 0; +} + + +void reiserfs_bitmap_zero (reiserfs_bitmap_t bm) +{ + memset (bm->bm_map, 0, bm->bm_byte_size); + bm->bm_set_bits = 0; +} + + +void reiserfs_bitmap_fill (reiserfs_bitmap_t bm) +{ + memset (bm->bm_map, 0xff, bm->bm_byte_size); + bm->bm_set_bits = bm->bm_bit_size; +} + + +/* format of bitmap saved in a file: + magic number (32 bits) + bm_bit_size (32 bits) + number of ranges of used and free blocks (32 bits) + number of contiguously used block, .. of free blocks, used, free, etc + magic number (32 bits) */ + +#define BITMAP_START_MAGIC 374031 +#define BITMAP_END_MAGIC 7786472 + +void reiserfs_bitmap_save (char * filename, reiserfs_bitmap_t bm) +{ + FILE * fp; + __u32 v; + int zeros; + int count; + int i; + int extents; + + fp = fopen (filename, "w+"); + if (!fp) { + reiserfs_warning (stderr, "reiserfs_bitmap_save: could not save bitmap in %s: %m", + filename); + return; + } + + reiserfs_warning (stderr, "Saving bitmap in \"%s\" .. ", filename); fflush (stderr); + + v = BITMAP_START_MAGIC; + fwrite (&v, 4, 1, fp); + + v = bm->bm_bit_size; + fwrite (&v, 4, 1, fp); + + /*printf ("SAVE: bit_size - %d\n", v);*/ + + + if (fseek (fp, 4, SEEK_CUR)) { + reiserfs_warning (stderr, "reiserfs_bitmap_save: fseek failed: %m\n"); + fclose (fp); + return; + } + + zeros = 0; + count = 0; + extents = 0; + for (i = 0; i < v; i ++) { + if (reiserfs_bitmap_test_bit (bm, i)) { + if (zeros) { + /* previous bit was not set, write amount of not set + bits, switch to count set bits */ + fwrite (&count, 4, 1, fp); + /*printf ("SAVE: Free %d\n", count);*/ + extents ++; + count = 1; + zeros = 0; + } else { + /* one more zero bit appeared */ + count ++; + } + } else { + /* zero bit found */ + if (zeros) { + count ++; + } else { + /* previous bit was set, write amount of set bits, + switch to count not set bits */ + fwrite (&count, 4, 1, fp); + /*printf ("SAVE: Used %d\n", count);*/ + extents ++; + count = 1; + zeros = 1; + } + } + } + + fwrite (&count, 4, 1, fp); + extents ++; +/* + if (zeros) + printf ("SAVE: Free %d\n", count); + else + printf ("SAVE: Used %d\n", count); +*/ + + v = BITMAP_END_MAGIC; + fwrite (&v, 4, 1, fp); + + if (fseek (fp, 8, SEEK_SET)) { + reiserfs_warning (stderr, "reiserfs_bitmap_save: fseek failed: %m"); + fclose (fp); + return; + } + + fwrite (&extents, 4, 1, fp); + /*printf ("SAVE: extents %d\n", extents);*/ + + fclose (fp); + + reiserfs_warning (stderr, "done\n"); fflush (stderr); +} + + +reiserfs_bitmap_t reiserfs_bitmap_load (char * filename) +{ + FILE * fp; + __u32 v; + int count; + int i, j; + int extents; + int bit; + reiserfs_bitmap_t bm; + + fp = fopen (filename, "r"); + if (!fp) { + reiserfs_warning (stderr, "reiserfs_bitmap_load: fseek failed: %m\n"); + return 0; + } + + fread (&v, 4, 1, fp); + if (v != BITMAP_START_MAGIC) { + reiserfs_warning (stderr, "reiserfs_bitmap_load: " + "no bitmap start magic found"); + fclose (fp); + return 0; + } + + /* read bit size of bitmap */ + fread (&v, 4, 1, fp); + + bm = reiserfs_create_bitmap (v); + if (!bm) { + reiserfs_warning (stderr, "reiserfs_bitmap_load: creation failed"); + fclose (fp); + return 0; + } + + reiserfs_warning (stderr, "Loading bitmap from %s .. ", filename); fflush (stderr); + + /*printf ("LOAD: bit_size - %d\n", v);*/ + + fread (&extents, 4, 1, fp); + + /*printf ("LOAD: extents - %d\n", extents);*/ + + bit = 0; + for (i = 0; i < extents; i ++) { + fread (&count, 4, 1, fp); +/* + if (i % 2) + printf ("LOAD: Free %d\n", count); + else + printf ("LOAD: Used %d\n", count); +*/ + for (j = 0; j < count; j ++, bit ++) + if (i % 2 == 0) { + reiserfs_bitmap_set_bit (bm, bit); + } + } + + fread (&v, 4, 1, fp); + + /*printf ("LOAD: Endmagic %d\n", v);*/ + + fclose (fp); + if (v != BITMAP_END_MAGIC) { + reiserfs_warning (stderr, "reiserfs_bitmap_load: " + "no bitmap end magic found"); + return 0; + } + + reiserfs_warning (stderr, "%d bits set - done\n", reiserfs_bitmap_ones (bm)); + fflush (stderr); + return bm; +} + + +void reiserfs_bitmap_invert (reiserfs_bitmap_t bm) +{ + int i; + + reiserfs_warning (stderr, "Bitmap inverting..");fflush (stderr); + for (i = 0; i < bm->bm_bit_size; i ++) { + if (reiserfs_bitmap_test_bit (bm, i)) + reiserfs_bitmap_clear_bit (bm, i); + else + reiserfs_bitmap_set_bit (bm, i); + } + + reiserfs_warning (stderr, "done\n"); +} + + + + + diff --git a/reiserfscore/do_balan.c b/reiserfscore/do_balan.c new file mode 100644 index 0000000..f81ff78 --- /dev/null +++ b/reiserfscore/do_balan.c @@ -0,0 +1,1720 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +/* Now we have all buffers that must be used in balancing of the tree */ +/* Further calculations can not cause schedule(), and thus the buffer */ +/* tree will be stable until the balancing will be finished */ +/* balance the tree according to the analysis made before, */ +/* and using buffers obtained after all above. */ + + +/** + ** balance_leaf_when_delete + ** balance_leaf + ** do_balance + ** + **/ + +#include "includes.h" + + +#ifdef CONFIG_REISERFS_CHECK + +struct tree_balance * cur_tb = NULL; /* detects whether more than one copy of tb exists as a means + of checking whether schedule is interrupting do_balance */ +struct tree_balance init_tb; /* Sometimes used to store a snapshot of tb during debugging. */ +int init_item_pos, init_pos_in_item, init_mode; /* Sometimes used to store a snapshot of tb during debugging. */ + + +#endif /* CONFIG_REISERFS_CHECK */ + + + +/* summary: + if deleting something ( tb->insert_size[0] < 0 ) + return(balance_leaf_when_delete()); (flag d handled here) + else + if lnum is larger than 0 we put items into the left node + if rnum is larger than 0 we put items into the right node + if snum1 is larger than 0 we put items into the new node s1 + if snum2 is larger than 0 we put items into the new node s2 +Note that all *num* count new items being created. + +It would be easier to read balance_leaf() if each of these summary +lines was a separate procedure rather than being inlined. I think +that there are many passages here and in balance_leaf_when_delete() in +which two calls to one procedure can replace two passages, and it +might save cache space and improve software maintenance costs to do so. + +Vladimir made the perceptive comment that we should offload most of +the decision making in this function into fix_nodes/check_balance, and +then create some sort of structure in tb that says what actions should +be performed by do_balance. + +-Hans */ + + + +/* Balance leaf node in case of delete or cut: insert_size[0] < 0 + * + * lnum, rnum can have values >= -1 + * -1 means that the neighbor must be joined with S + * 0 means that nothing should be done with the neighbor + * >0 means to shift entirely or partly the specified number of items to the neighbor + */ +static int balance_leaf_when_delete (/*struct reiserfs_transaction_handle *th,*/ + struct tree_balance * tb, + int flag) +{ + struct buffer_head * tbS0 = PATH_PLAST_BUFFER (tb->tb_path); + int item_pos = PATH_LAST_POSITION (tb->tb_path); + int pos_in_item = tb->tb_path->pos_in_item; + struct buffer_info bi; + int n; + struct item_head * ih; + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->FR[0] && B_BLK_HEAD(tb->FR[0])->blk_level <= DISK_LEAF_NODE_LEVEL ) + reiserfs_panic (tb->tb_sb, + "balance_leaf_when_delete: 11999:level == %u\n", B_BLK_HEAD(tb->FR[0])->blk_level); + if ( tb->blknum[0] > 1 ) + reiserfs_panic (tb->tb_sb, + "PAP-12005: balance_leaf_when_delete: tb->blknum == %d, can not be > 1", tb->blknum[0]); + + if ( ! tb->blknum[0] && ! PATH_H_PPARENT(tb->tb_path, 0)) + reiserfs_panic (tb->tb_sb, "PAP-12010: balance_leaf_when_delete: tree can not be empty"); +#endif + + ih = B_N_PITEM_HEAD (tbS0, item_pos); + + /* Delete or truncate the item */ + + switch (flag) { + case M_DELETE: /* delete item in S[0] */ + + bi.bi_bh = tbS0; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + bi.bi_position = PATH_H_POSITION (tb->tb_path, 1); + leaf_delete_items (tb->tb_sb, &bi, 0, item_pos, 1, -1); + +#ifdef CONFIG_REISERFS_CHECK + if (! item_pos && !tb->CFL[0]) + reiserfs_panic (tb->tb_sb, "PAP-12020: balance_leaf_when_delete: " + "tb->CFL[0]==0 when item_pos == 0"); +#endif + + if ( ! item_pos ) { + // we have removed first item in the node - update left delimiting key + if ( B_NR_ITEMS(tbS0) ) { + replace_key(tb->tb_sb, tb->CFL[0],tb->lkey[0],tbS0,0); + } + else { + if ( ! PATH_H_POSITION (tb->tb_path, 1) ) + replace_key(tb->tb_sb, tb->CFL[0],tb->lkey[0],PATH_H_PPARENT(tb->tb_path, 0),0); + } + } + + break; + + case M_CUT: { /* cut item in S[0] */ + bi.bi_bh = tbS0; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + bi.bi_position = PATH_H_POSITION (tb->tb_path, 1); + if (I_IS_DIRECTORY_ITEM (ih)) { + /* UFS unlink semantics are such that you can only delete + one directory entry at a time. */ + /* when we cut a directory tb->insert_size[0] means number + of entries to be cut (always 1) */ + tb->insert_size[0] = -1; + leaf_cut_from_buffer (tb->tb_sb, &bi, item_pos, pos_in_item, -tb->insert_size[0]); + +#ifdef CONFIG_REISERFS_CHECK + if (! item_pos && ! pos_in_item && ! tb->CFL[0]) + reiserfs_panic (tb->tb_sb, "PAP-12030: balance_leaf_when_delete: can not change delimiting key. CFL[0]=%p", tb->CFL[0]); +#endif /* CONFIG_REISERFS_CHECK */ + + if ( ! item_pos && ! pos_in_item ) { + replace_key(tb->tb_sb, tb->CFL[0],tb->lkey[0],tbS0,0); + } + } else { + leaf_cut_from_buffer (tb->tb_sb, &bi, item_pos, pos_in_item, -tb->insert_size[0]); + +#ifdef CONFIG_REISERFS_CHECK + if (! ih->ih_item_len) + reiserfs_panic (tb->tb_sb, "PAP-12035: balance_leaf_when_delete: cut must leave non-zero dynamic length of item"); +#endif /* CONFIG_REISERFS_CHECK */ + } + break; + } + + default: + print_tb(flag, item_pos, pos_in_item, tb,"when_del"); + reiserfs_panic ("PAP-12040: balance_leaf_when_delete: unexpectable mode: %s(%d)", + (flag == M_PASTE) ? "PASTE" : ((flag == M_INSERT) ? "INSERT" : "UNKNOWN"), flag); + } + + /* the rule is that no shifting occurs unless by shifting a node can be freed */ + n = B_NR_ITEMS(tbS0); + if ( tb->lnum[0] ) /* L[0] takes part in balancing */ + { + if ( tb->lnum[0] == -1 ) /* L[0] must be joined with S[0] */ + { + if ( tb->rnum[0] == -1 ) /* R[0] must be also joined with S[0] */ + { + if ( tb->FR[0] == PATH_H_PPARENT(tb->tb_path, 0) ) + { + /* all contents of all the 3 buffers will be in L[0] */ + if ( PATH_H_POSITION (tb->tb_path, 1) == 0 && 1 < B_NR_ITEMS(tb->FR[0]) ) + replace_key(tb->tb_sb, tb->CFL[0],tb->lkey[0],tb->FR[0],1); + + + leaf_move_items (LEAF_FROM_S_TO_L, tb, n, -1, 0); + leaf_move_items (LEAF_FROM_R_TO_L, tb, B_NR_ITEMS(tb->R[0]), -1, 0); + + reiserfs_invalidate_buffer (tb, tbS0, 1/*do_free_block*/); + reiserfs_invalidate_buffer (tb, tb->R[0], 1/*do_free_block*/); + + return 0; + } + /* all contents of all the 3 buffers will be in R[0] */ + leaf_move_items(LEAF_FROM_S_TO_R, tb, n, -1, 0); + leaf_move_items(LEAF_FROM_L_TO_R, tb, B_NR_ITEMS(tb->L[0]), -1, 0); + + /* right_delimiting_key is correct in R[0] */ + replace_key(tb->tb_sb, tb->CFR[0],tb->rkey[0],tb->R[0],0); + + reiserfs_invalidate_buffer (tb, tbS0, 1/*do_free_block*/); + reiserfs_invalidate_buffer (tb, tb->L[0], 1/*do_free_block*/); + + return -1; + } + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rnum[0] != 0 ) + reiserfs_panic (tb->tb_sb, "PAP-12045: balance_leaf_when_delete: rnum must be 0 (%d)", tb->rnum[0]); +#endif /* CONFIG_REISERFS_CHECK */ + + /* all contents of L[0] and S[0] will be in L[0] */ + leaf_shift_left(tb, n, -1); + + reiserfs_invalidate_buffer (tb, tbS0, 1/*do_free_block*/); + + return 0; + } + /* a part of contents of S[0] will be in L[0] and the rest part of S[0] will be in R[0] */ + +#ifdef CONFIG_REISERFS_CHECK + if (( tb->lnum[0] + tb->rnum[0] < n ) || ( tb->lnum[0] + tb->rnum[0] > n+1 )) + reiserfs_panic (tb->tb_sb, "PAP-12050: balance_leaf_when_delete: rnum(%d) and lnum(%d) and item number in S[0] are not consistent", + tb->rnum[0], tb->lnum[0], n); + + if (( tb->lnum[0] + tb->rnum[0] == n ) && (tb->lbytes != -1 || tb->rbytes != -1)) + reiserfs_panic (tb->tb_sb, "PAP-12055: balance_leaf_when_delete: bad rbytes (%d)/lbytes (%d) parameters when items are not split", + tb->rbytes, tb->lbytes); + if (( tb->lnum[0] + tb->rnum[0] == n + 1 ) && (tb->lbytes < 1 || tb->rbytes != -1)) + reiserfs_panic (tb->tb_sb, "PAP-12060: balance_leaf_when_delete: bad rbytes (%d)/lbytes (%d) parameters when items are split", + tb->rbytes, tb->lbytes); +#endif + + leaf_shift_left (tb, tb->lnum[0], tb->lbytes); + leaf_shift_right(tb, tb->rnum[0], tb->rbytes); + + reiserfs_invalidate_buffer (tb, tbS0, 1/*do_free_block*/); + + return 0; + } + + if ( tb->rnum[0] == -1 ) { + /* all contents of R[0] and S[0] will be in R[0] */ + leaf_shift_right(tb, n, -1); + reiserfs_invalidate_buffer (tb, tbS0, 1/*do_free_block*/); + return 0; + } + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rnum[0] ) + reiserfs_panic (tb->tb_sb, "PAP-12065: balance_leaf_when_delete: bad rnum parameter must be 0 (%d)", tb->rnum[0]); +#endif + + return 0; +} + + +static int balance_leaf(/*struct reiserfs_transaction_handle *th, */ + struct tree_balance * tb, /* see reiserfs_fs.h */ + struct item_head * ih, /* item header of inserted item */ + const char * body, /* body of inserted item or bytes to paste */ + int flag, /* i - insert, d - delete, c - cut, p - paste + (see comment to do_balance) */ + int zeros_number, /* will be commented later */ + + struct item_head * insert_key, /* in our processing of one level we sometimes determine what + must be inserted into the next higher level. This insertion + consists of a key or two keys and their corresponding + pointers */ + struct buffer_head ** insert_ptr /* inserted node-ptrs for the next level */ + ) +{ + int pos_in_item = tb->tb_path->pos_in_item; /* position in item, in bytes for direct and + indirect items, in entries for directories (for + which it is an index into the array of directory + entry headers.) */ + struct buffer_head * tbS0 = PATH_PLAST_BUFFER (tb->tb_path); +/* struct buffer_head * tbF0 = PATH_H_PPARENT (tb->tb_path, 0); + int S0_b_item_order = PATH_H_B_ITEM_ORDER (tb->tb_path, 0);*/ + int item_pos = PATH_LAST_POSITION (tb->tb_path); /* index into the array of item headers in S[0] + of the affected item */ + struct buffer_info bi; + struct buffer_head *S_new[2]; /* new nodes allocated to hold what could not fit into S */ + int snum[2]; /* number of items that will be placed into S_new (includes partially shifted items) */ + int sbytes[2]; /* if an item is partially shifted into S_new then + if it is a directory item + it is the number of entries from the item that are shifted into S_new + else + it is the number of bytes from the item that are shifted into S_new + */ + int n, i; + int ret_val; + + /* Make balance in case insert_size[0] < 0 */ + if ( tb->insert_size[0] < 0 ) + return balance_leaf_when_delete (/*th,*/ tb, flag); + + /* for indirect item pos_in_item is measured in unformatted node + pointers. Recalculate to bytes */ + if (flag != M_INSERT && I_IS_INDIRECT_ITEM (B_N_PITEM_HEAD (tbS0, item_pos))) + pos_in_item *= UNFM_P_SIZE; + + if ( tb->lnum[0] > 0 ) { + /* Shift lnum[0] items from S[0] to the left neighbor L[0] */ + if ( item_pos < tb->lnum[0] ) { + /* new item or it part falls to L[0], shift it too */ + n = B_NR_ITEMS(tb->L[0]); + + switch (flag) { + case M_INSERT: /* insert item into L[0] */ + + if ( item_pos == tb->lnum[0] - 1 && tb->lbytes != -1 ) { + /* part of new item falls into L[0] */ + int new_item_len; + +#ifdef CONFIG_REISERFS_CHECK + if (!I_IS_DIRECT_ITEM (ih)) + reiserfs_panic (tb->tb_sb, "PAP-12075: balance_leaf: " + "this item (%h) can not be split on insertion", ih); +#endif + ret_val = leaf_shift_left (/*th,*/ tb, tb->lnum[0]-1, -1); + + /* Calculate item length to insert to S[0] */ + new_item_len = ih->ih_item_len - tb->lbytes; + /* Calculate and check item length to insert to L[0] */ + ih->ih_item_len -= new_item_len; + +#ifdef CONFIG_REISERFS_CHECK + if ( (int)(ih->ih_item_len) <= 0 ) + reiserfs_panic(tb->tb_sb, "PAP-12080: balance_leaf: " + "there is nothing to insert into L[0]: ih_item_len=%d", + (int)ih->ih_item_len); +#endif + + /* Insert new item into L[0] */ + bi.bi_bh = tb->L[0]; + bi.bi_parent = tb->FL[0]; + bi.bi_position = get_left_neighbor_position (tb, 0); + leaf_insert_into_buf (tb->tb_sb, &bi, n + item_pos - ret_val, ih, body, + zeros_number > ih->ih_item_len ? ih->ih_item_len : zeros_number); + + /* Calculate key component, item length and body to insert into S[0] */ + //ih->ih_key.k_offset += tb->lbytes; + set_offset (key_format (&ih->ih_key), &ih->ih_key, get_offset (&ih->ih_key) + tb->lbytes); + ih->ih_item_len = new_item_len; + if ( tb->lbytes > zeros_number ) { + body += (tb->lbytes - zeros_number); + zeros_number = 0; + } + else + zeros_number -= tb->lbytes; + +#ifdef CONFIG_REISERFS_CHECK + if ( (int)(ih->ih_item_len) <= 0 ) + reiserfs_panic(tb->tb_sb, "PAP-12085: balance_leaf: " + "there is nothing to insert into S[0]: ih_item_len=%d", + (int)ih->ih_item_len); +#endif + } else { + /* new item in whole falls into L[0] */ + /* Shift lnum[0]-1 items to L[0] */ + ret_val = leaf_shift_left(tb, tb->lnum[0]-1, tb->lbytes); + + /* Insert new item into L[0] */ + bi.bi_bh = tb->L[0]; + bi.bi_parent = tb->FL[0]; + bi.bi_position = get_left_neighbor_position (tb, 0); + leaf_insert_into_buf (tb->tb_sb, &bi, n + item_pos - ret_val, ih, body, zeros_number); + + tb->insert_size[0] = 0; + zeros_number = 0; + } + break; + + case M_PASTE: /* append item in L[0] */ + + if ( item_pos == tb->lnum[0] - 1 && tb->lbytes != -1 ) { + /* we must shift the part of the appended item */ + if ( I_IS_DIRECTORY_ITEM (B_N_PITEM_HEAD (tbS0, item_pos))) { + +#ifdef CONFIG_REISERFS_CHECK + if ( zeros_number ) + reiserfs_panic(tb->tb_sb, "PAP-12090: balance_leaf: illegal parameter in case of a directory"); +#endif + + /* directory item */ + if ( tb->lbytes > pos_in_item ) { + /* new directory entry falls into L[0] */ + struct item_head * pasted; + int l_pos_in_item = pos_in_item; + + /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 entries from given directory item */ + ret_val = leaf_shift_left(tb, tb->lnum[0], tb->lbytes - 1); + if ( ret_val && ! item_pos ) { + pasted = B_N_PITEM_HEAD(tb->L[0],B_NR_ITEMS(tb->L[0])-1); + l_pos_in_item += ih_entry_count(pasted) - (tb->lbytes-1); + } + + /* Append given directory entry to directory item */ + bi.bi_bh = tb->L[0]; + bi.bi_parent = tb->FL[0]; + bi.bi_position = get_left_neighbor_position (tb, 0); + leaf_paste_in_buffer (tb->tb_sb, &bi, n + item_pos - ret_val, l_pos_in_item, + tb->insert_size[0], body, zeros_number); + + /* previous string prepared space for pasting new entry, following string pastes this entry */ + + /* when we have merge directory item, pos_in_item has been changed too */ + + /* paste new directory entry. 1 is entry number */ + leaf_paste_entries (bi.bi_bh, n + item_pos - ret_val, l_pos_in_item, 1, + (struct reiserfs_de_head *)body, + body + DEH_SIZE, tb->insert_size[0] + ); + tb->insert_size[0] = 0; + } else { + /* new directory item doesn't fall into L[0] */ + /* Shift lnum[0]-1 items in whole. Shift lbytes directory entries from directory item number lnum[0] */ + leaf_shift_left (tb, tb->lnum[0], tb->lbytes); + } + /* Calculate new position to append in item body */ + pos_in_item -= tb->lbytes; + } + else { + /* regular object */ + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->lbytes <= 0 ) + reiserfs_panic(tb->tb_sb, "PAP-12095: balance_leaf: " + "there is nothing to shift to L[0]. lbytes=%d", + tb->lbytes); + if ( pos_in_item != B_N_PITEM_HEAD(tbS0, item_pos)->ih_item_len ) + reiserfs_panic(tb->tb_sb, "PAP-12100: balance_leaf: " + "incorrect position to paste: item_len=%d, pos_in_item=%d", + B_N_PITEM_HEAD(tbS0,item_pos)->ih_item_len, pos_in_item); +#endif + + if ( tb->lbytes >= pos_in_item ) { + /* appended item will be in L[0] in whole */ + int l_n; + struct key * key; + + /* this bytes number must be appended to the last item of L[h] */ + l_n = tb->lbytes - pos_in_item; + + /* Calculate new insert_size[0] */ + tb->insert_size[0] -= l_n; + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->insert_size[0] <= 0 ) + reiserfs_panic(tb->tb_sb, "PAP-12105: balance_leaf: " + "there is nothing to paste into L[0]. insert_size=%d", + tb->insert_size[0]); +#endif + + ret_val = leaf_shift_left(tb, tb->lnum[0], + B_N_PITEM_HEAD(tbS0,item_pos)->ih_item_len); + /* Append to body of item in L[0] */ + bi.bi_bh = tb->L[0]; + bi.bi_parent = tb->FL[0]; + bi.bi_position = get_left_neighbor_position (tb, 0); + leaf_paste_in_buffer(tb->tb_sb, + &bi,n + item_pos - ret_val, + B_N_PITEM_HEAD(tb->L[0],n+item_pos-ret_val)->ih_item_len, + l_n,body, zeros_number > l_n ? l_n : zeros_number + ); + +#ifdef CONFIG_REISERFS_CHECK + if (l_n && I_IS_INDIRECT_ITEM(B_N_PITEM_HEAD(tb->L[0], + n + item_pos - ret_val))) + reiserfs_panic(tb->tb_sb, "PAP-12110: balance_leaf: " + "pasting more than 1 unformatted node pointer into indirect item"); +#endif + + /* 0-th item in S0 can be only of DIRECT type when l_n != 0*/ + //B_N_PKEY (tbS0, 0)->k_offset += l_n; + key = B_N_PKEY (tbS0, 0); + set_offset (key_format (key), key, get_offset (key) + l_n); + + //B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0])->k_offset += l_n; + key = B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]); + set_offset (key_format (key), key, get_offset (key) + l_n); + + /* Calculate new body, position in item and insert_size[0] */ + if ( l_n > zeros_number ) { + body += (l_n - zeros_number); + zeros_number = 0; + } + else + zeros_number -= l_n; + pos_in_item = 0; + +#ifdef CONFIG_REISERFS_CHECK + if (not_of_one_file (B_N_PKEY(tbS0,0), + B_N_PKEY(tb->L[0],B_NR_ITEMS(tb->L[0])-1)) || + !is_left_mergeable (B_N_PITEM_HEAD (tbS0, 0), tbS0->b_size) || + !is_left_mergeable((struct item_head *)B_N_PDELIM_KEY(tb->CFL[0],tb->lkey[0]), tbS0->b_size)) + reiserfs_panic (tb->tb_sb, "PAP-12120: balance_leaf: " + "item must be merge-able with left neighboring item"); +#endif + + } + else { + /* only part of the appended item will be in L[0] */ + + /* Calculate position in item for append in S[0] */ + pos_in_item -= tb->lbytes; + +#ifdef CONFIG_REISERFS_CHECK + if ( pos_in_item <= 0 ) + reiserfs_panic(tb->tb_sb, "PAP-12125: balance_leaf: " + "no place for paste. pos_in_item=%d", pos_in_item); +#endif + + /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 byte from item number lnum[0] */ + leaf_shift_left(tb,tb->lnum[0],tb->lbytes); + } + } + } else { + /* appended item will be in L[0] in whole */ + struct item_head * pasted; + +#ifndef FU//REISERFS_FSCK + // this works + if ( ! item_pos && is_left_mergeable (tb->tb_sb, tb->tb_path) == 1 ) +#else + if ( ! item_pos && is_left_mergeable (B_N_PITEM_HEAD (tbS0, 0), tbS0->b_size) ) +#endif + { /* if we paste into first item of S[0] and it is left mergable */ + /* then increment pos_in_item by the size of the last item in L[0] */ + pasted = B_N_PITEM_HEAD(tb->L[0],n-1); + if ( I_IS_DIRECTORY_ITEM(pasted) ) + pos_in_item += ih_entry_count (pasted); + else + pos_in_item += pasted->ih_item_len; + } + + /* Shift lnum[0] - 1 items in whole. Shift lbytes - 1 byte from item number lnum[0] */ + ret_val = leaf_shift_left(tb, tb->lnum[0], tb->lbytes); + /* Append to body of item in L[0] */ + bi.bi_bh = tb->L[0]; + bi.bi_parent = tb->FL[0]; + bi.bi_position = get_left_neighbor_position (tb, 0); + leaf_paste_in_buffer (tb->tb_sb, &bi, n + item_pos - ret_val, pos_in_item, tb->insert_size[0], + body, zeros_number); + + /* if appended item is directory, paste entry */ + pasted = B_N_PITEM_HEAD (tb->L[0], n + item_pos - ret_val); + if (I_IS_DIRECTORY_ITEM (pasted)) + leaf_paste_entries (bi.bi_bh, n + item_pos - ret_val, pos_in_item, 1, + (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]); + + /* if appended item is indirect item, put unformatted node into un list */ + if (I_IS_INDIRECT_ITEM (pasted)) + set_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + //pasted->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + + tb->insert_size[0] = 0; + zeros_number = 0; + } + break; + default: /* cases d and t */ + reiserfs_panic ("PAP-12130: balance_leaf: lnum > 0: unexpectable mode: %s(%d)", + (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag); + } + } else { + /* new item doesn't fall into L[0] */ + leaf_shift_left (tb, tb->lnum[0], tb->lbytes); + } + } /* tb->lnum[0] > 0 */ + + /* Calculate new item position */ + item_pos -= ( tb->lnum[0] - (( tb->lbytes != -1 ) ? 1 : 0)); + + if ( tb->rnum[0] > 0 ) { + /* shift rnum[0] items from S[0] to the right neighbor R[0] */ + n = B_NR_ITEMS(tbS0); + switch ( flag ) { + + case M_INSERT: /* insert item */ + if ( n - tb->rnum[0] < item_pos ) { + /* new item or its part falls to R[0] */ + if ( item_pos == n - tb->rnum[0] + 1 && tb->rbytes != -1 ) { + /* part of new item falls into R[0] */ + int old_key_comp, old_len, r_zeros_number; + const char * r_body; + +#ifdef CONFIG_REISERFS_CHECK + if ( !I_IS_DIRECT_ITEM(ih) ) + reiserfs_panic(tb->tb_sb, "PAP-12135: balance_leaf: " + "this item (%h) can not be split", ih); +#endif + + leaf_shift_right(tb, tb->rnum[0] - 1, -1); + + /* Remember key component and item length */ + old_key_comp = get_offset (&ih->ih_key); + old_len = ih->ih_item_len; + + /* Calculate key component and item length to insert into R[0] */ + //ih->ih_key.k_offset += (old_len - tb->rbytes); + set_offset (key_format (&ih->ih_key), &ih->ih_key, old_key_comp + old_len - tb->rbytes); + + ih->ih_item_len = tb->rbytes; + /* Insert part of the item into R[0] */ + bi.bi_bh = tb->R[0]; + bi.bi_parent = tb->FR[0]; + bi.bi_position = get_right_neighbor_position (tb, 0); + if ( get_offset (&ih->ih_key) - old_key_comp > zeros_number ) { + r_zeros_number = 0; + r_body = body + get_offset (&ih->ih_key) - old_key_comp - zeros_number; + } + else { + r_body = body; + r_zeros_number = zeros_number - (get_offset (&ih->ih_key) - old_key_comp); + zeros_number -= r_zeros_number; + } + + leaf_insert_into_buf (tb->tb_sb, &bi, 0, ih, r_body, r_zeros_number); + + /* Replace right delimiting key by first key in R[0] */ + replace_key(tb->tb_sb, tb->CFR[0],tb->rkey[0],tb->R[0],0); + + /* Calculate key component and item length to insert into S[0] */ + //ih->ih_key.k_offset = old_key_comp; + set_offset (key_format (&ih->ih_key), &ih->ih_key, old_key_comp); + + ih->ih_item_len = old_len - tb->rbytes; + + tb->insert_size[0] -= tb->rbytes; + + } else { + /* whole new item falls into R[0] */ + + /* Shift rnum[0]-1 items to R[0] */ + ret_val = leaf_shift_right(tb, tb->rnum[0] - 1, tb->rbytes); + + /* Insert new item into R[0] */ + bi.bi_bh = tb->R[0]; + bi.bi_parent = tb->FR[0]; + bi.bi_position = get_right_neighbor_position (tb, 0); + leaf_insert_into_buf (tb->tb_sb, &bi, item_pos - n + tb->rnum[0] - 1, ih, body, zeros_number); + + /* If we insert new item in the begin of R[0] change the right delimiting key */ + if ( item_pos - n + tb->rnum[0] - 1 == 0 ) { + replace_key (tb->tb_sb, tb->CFR[0],tb->rkey[0],tb->R[0],0); + } + zeros_number = tb->insert_size[0] = 0; + } + } else { + /* new item or part of it doesn't fall into R[0] */ + leaf_shift_right (tb, tb->rnum[0], tb->rbytes); + } + break; + + case M_PASTE: /* append item */ + + if ( n - tb->rnum[0] <= item_pos ) { /* pasted item or part of it falls to R[0] */ + if ( item_pos == n - tb->rnum[0] && tb->rbytes != -1 ) { + /* we must shift the part of the appended item */ + if ( I_IS_DIRECTORY_ITEM (B_N_PITEM_HEAD(tbS0, item_pos))) { + /* we append to directory item */ + int entry_count; + +#ifdef CONFIG_REISERFS_CHECK + if ( zeros_number ) + reiserfs_panic(tb->tb_sb, "PAP-12145: balance_leaf: " + "illegal parameter in case of a directory"); +#endif + + entry_count = ih_entry_count (B_N_PITEM_HEAD(tbS0, item_pos)); + if ( entry_count - tb->rbytes < pos_in_item ) { + /* new directory entry falls into R[0] */ + int paste_entry_position; + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rbytes - 1 >= entry_count || ! tb->insert_size[0] ) + reiserfs_panic(tb->tb_sb, "PAP-12150: balance_leaf: " + "no enough of entries to shift to R[0]: rbytes=%d, entry_count=%d", + tb->rbytes, entry_count); +#endif + + /* Shift rnum[0]-1 items in whole. Shift rbytes-1 directory entries from directory item number rnum[0] */ + leaf_shift_right (tb, tb->rnum[0], tb->rbytes - 1); + + /* Paste given directory entry to directory item */ + paste_entry_position = pos_in_item - entry_count + tb->rbytes - 1; + + bi.bi_bh = tb->R[0]; + bi.bi_parent = tb->FR[0]; + bi.bi_position = get_right_neighbor_position (tb, 0); + leaf_paste_in_buffer (tb->tb_sb, &bi, 0, paste_entry_position, + tb->insert_size[0],body,zeros_number); + /* paste entry */ + leaf_paste_entries ( + bi.bi_bh, 0, paste_entry_position, 1, (struct reiserfs_de_head *)body, + body + DEH_SIZE, tb->insert_size[0] + ); + + if ( paste_entry_position == 0 ) { + /* change delimiting keys */ + replace_key(tb->tb_sb, tb->CFR[0],tb->rkey[0],tb->R[0],0); + } + + tb->insert_size[0] = 0; + pos_in_item++; + } else { + /* new directory entry doesn't fall into R[0] */ + leaf_shift_right (tb, tb->rnum[0],tb->rbytes); + } + } + else { + /* regular object */ + + int n_shift, n_rem, r_zeros_number; + const char * r_body; + struct key * key; + + /* Calculate number of bytes which must be shifted from appended item */ + if ( (n_shift = tb->rbytes - tb->insert_size[0]) < 0 ) + n_shift = 0; + +#ifdef CONFIG_REISERFS_CHECK + if (pos_in_item != B_N_PITEM_HEAD (tbS0, item_pos)->ih_item_len) + reiserfs_panic(tb->tb_sb,"PAP-12155: balance_leaf: invalid position %d to paste item %h", + pos_in_item, B_N_PITEM_HEAD(tbS0,item_pos)); +#endif + + leaf_shift_right (tb, tb->rnum[0], n_shift); + + /* Calculate number of bytes which must remain in body after appending to R[0] */ + if ( (n_rem = tb->insert_size[0] - tb->rbytes) < 0 ) + n_rem = 0; + + //B_N_PKEY(tb->R[0],0)->k_offset += n_rem; + key = B_N_PKEY(tb->R[0],0); + set_offset (key_format (key), key, get_offset (key) + n_rem); + + //B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0])->k_offset += n_rem; + key = B_N_PDELIM_KEY(tb->CFR[0],tb->rkey[0]); + set_offset (key_format (key), key, get_offset (key) + n_rem); + + mark_buffer_dirty (tb->CFR[0]); + + /* Append part of body into R[0] */ + bi.bi_bh = tb->R[0]; + bi.bi_parent = tb->FR[0]; + bi.bi_position = get_right_neighbor_position (tb, 0); + if ( n_rem > zeros_number ) { + r_zeros_number = 0; + r_body = body + n_rem - zeros_number; + } + else { + r_body = body; + r_zeros_number = zeros_number - n_rem; + zeros_number -= r_zeros_number; + } + + leaf_paste_in_buffer(tb->tb_sb, &bi, 0, n_shift, tb->insert_size[0] - n_rem, r_body, r_zeros_number); + + if (I_IS_INDIRECT_ITEM(B_N_PITEM_HEAD(tb->R[0],0))) { + +#ifdef CONFIG_REISERFS_CHECK + if (n_rem) + reiserfs_panic(tb->tb_sb, "PAP-12160: balance_leaf: paste more than one unformatted node pointer"); +#endif + + set_free_space (B_N_PITEM_HEAD(tb->R[0],0), ((struct unfm_nodeinfo*)body)->unfm_freespace); + //B_N_PITEM_HEAD(tb->R[0],0)->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + } + + tb->insert_size[0] = n_rem; + if ( ! n_rem ) + pos_in_item ++; + } + } + else { + /* pasted item falls into R[0] entirely */ + + struct item_head * pasted; + + ret_val = leaf_shift_right (tb, tb->rnum[0], tb->rbytes); + + /* append item in R[0] */ + if ( pos_in_item >= 0 ) { + bi.bi_bh = tb->R[0]; + bi.bi_parent = tb->FR[0]; + bi.bi_position = get_right_neighbor_position (tb, 0); + leaf_paste_in_buffer(tb->tb_sb, &bi,item_pos - n + tb->rnum[0], pos_in_item, + tb->insert_size[0],body, zeros_number); + } + + /* paste new entry, if item is directory item */ + pasted = B_N_PITEM_HEAD(tb->R[0], item_pos - n + tb->rnum[0]); + if (I_IS_DIRECTORY_ITEM (pasted) && pos_in_item >= 0 ) { + leaf_paste_entries (bi.bi_bh, item_pos - n + tb->rnum[0], pos_in_item, 1, + (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]); + if ( ! pos_in_item ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( item_pos - n + tb->rnum[0] ) + reiserfs_panic (tb->tb_sb, "PAP-12165: balance_leaf: " + "directory item must be first item of node when pasting is in 0th position"); +#endif + + /* update delimiting keys */ + replace_key (tb->tb_sb, tb->CFR[0],tb->rkey[0],tb->R[0],0); + } + } + + if (I_IS_INDIRECT_ITEM (pasted)) + //pasted->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + set_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + zeros_number = tb->insert_size[0] = 0; + } + } + else { + /* new item doesn't fall into R[0] */ + leaf_shift_right (tb, tb->rnum[0], tb->rbytes); + } + break; + + default: /* cases d and t */ + reiserfs_panic ("PAP-12175: balance_leaf: rnum > 0: unexpectable mode: %s(%d)", + (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag); + } + + } /* tb->rnum[0] > 0 */ + + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->blknum[0] > 3 ) + reiserfs_panic (tb->tb_sb, "PAP-12180: balance_leaf: " + "blknum can not be %d. It must be <= 3", tb->blknum[0]); + + if ( tb->blknum[0] < 0 ) + reiserfs_panic (tb->tb_sb, "PAP-12185: balance_leaf: " + "blknum can not be %d. It must be >= 0", tb->blknum[0]); + + if ( tb->blknum[0] == 0 && (! tb->lnum[0] || ! tb->rnum[0]) ) + reiserfs_panic(tb->tb_sb, "PAP-12190: balance_leaf: lnum and rnum must not be zero"); +#endif + + /* if while adding to a node we discover that it is possible to split + it in two, and merge the left part into the left neighbor and the + right part into the right neighbor, eliminating the node */ + if ( tb->blknum[0] == 0 ) { /* node S[0] is empty now */ + +#ifdef CONFIG_REISERFS_CHECK + if (!tb->CFL[0] || !tb->CFR[0] || !tb->R[0] || !tb->R[0] || + COMP_KEYS (B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]), + B_N_PKEY (tb->R[0], 0))) + reiserfs_panic (tb->tb_sb, "vs-12195: balance_leaf: " + "right delim key (%k) is not set properly (%k)", + B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]), B_N_PKEY (tb->R[0], 0)); +#endif + + reiserfs_invalidate_buffer(tb,tbS0, 1); + return 0; + } + + + /* Fill new nodes that appear in place of S[0] */ + + /* I am told that this copying is because we need an array to enable + the looping code. -Hans */ + snum[0] = tb->s1num, + snum[1] = tb->s2num; + sbytes[0] = tb->s1bytes; + sbytes[1] = tb->s2bytes; + for( i = tb->blknum[0] - 2; i >= 0; i-- ) { + +#ifdef CONFIG_REISERFS_CHECK + if (!snum[i]) + reiserfs_panic(tb->tb_sb,"PAP-12200: balance_leaf: snum[%d] == %d. Must be > 0", i, snum[i]); +#endif /* CONFIG_REISERFS_CHECK */ + + /* here we shift from S to S_new nodes */ + + S_new[i] = get_FEB(tb); + + /* set block level */ + set_leaf_node_level (S_new[i]); + + n = B_NR_ITEMS(tbS0); + + switch (flag) { + case M_INSERT: /* insert item */ + + if ( n - snum[i] < item_pos ) { + /* new item or it's part falls to first new node S_new[i]*/ + if ( item_pos == n - snum[i] + 1 && sbytes[i] != -1 ) { + /* part of new item falls into S_new[i] */ + int old_key_comp, old_len, r_zeros_number; + const char * r_body; + +#ifdef CONFIG_REISERFS_CHECK + if ( !I_IS_DIRECT_ITEM(ih) ) + /* The items which can be inserted are: Stat_data + item, direct item, indirect item and directory item + which consist of only two entries "." and "..". + These items must not be broken except for a direct + one. */ + reiserfs_panic(tb->tb_sb, "PAP-12205: balance_leaf: " + "this item %h can not be broken when inserting", ih); +#endif + + /* Move snum[i]-1 items from S[0] to S_new[i] */ + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i] - 1, -1, S_new[i]); + + /* Remember key component and item length */ + old_key_comp = get_offset (&ih->ih_key); + old_len = ih->ih_item_len; + + /* Calculate key component and item length to insert into S_new[i] */ + //ih->ih_key.k_offset += (old_len - sbytes[i]); + set_offset (key_format (&ih->ih_key), &ih->ih_key, old_key_comp + old_len - sbytes[i]); + + ih->ih_item_len = sbytes[i]; + + /* Insert part of the item into S_new[i] before 0-th item */ + bi.bi_bh = S_new[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + + if ( get_offset (&ih->ih_key) - old_key_comp > zeros_number ) { + r_zeros_number = 0; + r_body = body + (get_offset (&ih->ih_key) - old_key_comp) - zeros_number; + } + else { + r_body = body; + r_zeros_number = zeros_number - (get_offset (&ih->ih_key) - old_key_comp); + zeros_number -= r_zeros_number; + } + + leaf_insert_into_buf (tb->tb_sb, &bi, 0, ih, r_body, r_zeros_number); + + /* Calculate key component and item length to insert into S[i] */ + //ih->ih_key.k_offset = old_key_comp; + set_offset (key_format (&ih->ih_key), &ih->ih_key, old_key_comp); + ih->ih_item_len = old_len - sbytes[i]; + tb->insert_size[0] -= sbytes[i]; + } + else /* whole new item falls into S_new[i] */ + { + /* Shift snum[0] - 1 items to S_new[i] (sbytes[i] of split item) */ + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i] - 1, sbytes[i], S_new[i]); + + /* Insert new item into S_new[i] */ + bi.bi_bh = S_new[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + leaf_insert_into_buf (tb->tb_sb, &bi, item_pos - n + snum[i] - 1, ih, body, zeros_number); + zeros_number = tb->insert_size[0] = 0; + } + } + + else /* new item or it part don't falls into S_new[i] */ + { + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]); + } + break; + + case M_PASTE: /* append item */ + + if ( n - snum[i] <= item_pos ) /* pasted item or part if it falls to S_new[i] */ + { + if ( item_pos == n - snum[i] && sbytes[i] != -1 ) + { /* we must shift part of the appended item */ + struct item_head * aux_ih; + +#ifdef CONFIG_REISERFS_CHECK + if ( ih ) + reiserfs_panic (tb->tb_sb, "PAP-12210: balance_leaf: ih must be 0"); +#endif /* CONFIG_REISERFS_CHECK */ + + if ( I_IS_DIRECTORY_ITEM (aux_ih = B_N_PITEM_HEAD(tbS0,item_pos))) { + /* we append to directory item */ + + int entry_count; + + entry_count = ih_entry_count(aux_ih); + + if ( entry_count - sbytes[i] < pos_in_item && pos_in_item <= entry_count ) { + /* new directory entry falls into S_new[i] */ + +#ifdef CONFIG_REISERFS_CHECK + if ( ! tb->insert_size[0] ) + reiserfs_panic (tb->tb_sb, "PAP-12215: balance_leaif: insert_size is already 0"); + if ( sbytes[i] - 1 >= entry_count ) + reiserfs_panic (tb->tb_sb, "PAP-12220: balance_leaf: " + "there are no so much entries (%d), only %d", + sbytes[i] - 1, entry_count); +#endif + + /* Shift snum[i]-1 items in whole. Shift sbytes[i] directory entries from directory item number snum[i] */ + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i]-1, S_new[i]); + + /* Paste given directory entry to directory item */ + bi.bi_bh = S_new[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + leaf_paste_in_buffer (tb->tb_sb, &bi, 0, pos_in_item - entry_count + sbytes[i] - 1, + tb->insert_size[0], body,zeros_number); + /* paste new directory entry */ + leaf_paste_entries ( + bi.bi_bh, 0, pos_in_item - entry_count + sbytes[i] - 1, + 1, (struct reiserfs_de_head *)body, body + DEH_SIZE, + tb->insert_size[0] + ); + tb->insert_size[0] = 0; + pos_in_item++; + } else { /* new directory entry doesn't fall into S_new[i] */ + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]); + } + } + else /* regular object */ + { + int n_shift, n_rem, r_zeros_number; + const char * r_body; + struct item_head * tmp; + +#ifdef CONFIG_REISERFS_CHECK + if ( pos_in_item != B_N_PITEM_HEAD(tbS0,item_pos)->ih_item_len || + tb->insert_size[0] <= 0 ) + reiserfs_panic (tb->tb_sb, "PAP-12225: balance_leaf: item too short or insert_size <= 0"); +#endif + + /* Calculate number of bytes which must be shifted from appended item */ + n_shift = sbytes[i] - tb->insert_size[0]; + if ( n_shift < 0 ) + n_shift = 0; + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], n_shift, S_new[i]); + + /* Calculate number of bytes which must remain in body after append to S_new[i] */ + n_rem = tb->insert_size[0] - sbytes[i]; + if ( n_rem < 0 ) + n_rem = 0; + /* Append part of body into S_new[0] */ + bi.bi_bh = S_new[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + + if ( n_rem > zeros_number ) { + r_zeros_number = 0; + r_body = body + n_rem - zeros_number; + } + else { + r_body = body; + r_zeros_number = zeros_number - n_rem; + zeros_number -= r_zeros_number; + } + + leaf_paste_in_buffer(tb->tb_sb, &bi, 0, n_shift, tb->insert_size[0]-n_rem, r_body,r_zeros_number); + tmp = B_N_PITEM_HEAD (S_new[i], 0); + if (I_IS_INDIRECT_ITEM(tmp)) { + if (n_rem) + reiserfs_panic ("PAP-12230: balance_leaf: " + "invalid action with indirect item"); + //tmp->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + set_free_space (tmp, ((struct unfm_nodeinfo*)body)->unfm_freespace); + } + + //B_N_PKEY(S_new[i],0)->k_offset += n_rem; + set_offset (key_format (&tmp->ih_key), &tmp->ih_key, get_offset (&tmp->ih_key) + n_rem); + + tb->insert_size[0] = n_rem; + if ( ! n_rem ) + pos_in_item++; + } + } + else + /* item falls wholly into S_new[i] */ + { + int ret_val; + struct item_head * pasted; + +#ifdef CONFIG_REISERFS_CHECK + struct item_head * ih = B_N_PITEM_HEAD(tbS0,item_pos); + + if ( ! I_IS_DIRECTORY_ITEM(ih) && (pos_in_item != ih->ih_item_len || + tb->insert_size[0] <= 0) ) + reiserfs_panic (tb->tb_sb, "PAP-12235: balance_leaf: pos_in_item must be equal to ih_item_len"); +#endif /* CONFIG_REISERFS_CHECK */ + + ret_val = leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]); + +#ifdef CONFIG_REISERFS_CHECK + if ( ret_val ) + reiserfs_panic (tb->tb_sb, "PAP-12240: balance_leaf: " + "unexpected value returned by leaf_move_items (%d)", + ret_val); +#endif /* CONFIG_REISERFS_CHECK */ + + /* paste into item */ + bi.bi_bh = S_new[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + leaf_paste_in_buffer(tb->tb_sb, &bi, item_pos - n + snum[i], pos_in_item, tb->insert_size[0], body, zeros_number); + + pasted = B_N_PITEM_HEAD(S_new[i], item_pos - n + snum[i]); + if (I_IS_DIRECTORY_ITEM (pasted)) { + leaf_paste_entries (bi.bi_bh, item_pos - n + snum[i], pos_in_item, 1, + (struct reiserfs_de_head *)body, body + DEH_SIZE, tb->insert_size[0]); + } + + /* if we paste to indirect item update ih_free_space */ + if (I_IS_INDIRECT_ITEM (pasted)) + //pasted->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + set_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + zeros_number = tb->insert_size[0] = 0; + } + } else { + /* pasted item doesn't fall into S_new[i] */ + leaf_move_items (LEAF_FROM_S_TO_SNEW, tb, snum[i], sbytes[i], S_new[i]); + } + break; + + default: /* cases d and t */ + reiserfs_panic ("PAP-12245: balance_leaf: blknum > 2: unexpectable mode: %s(%d)", + (flag == M_DELETE) ? "DELETE" : ((flag == M_CUT) ? "CUT" : "UNKNOWN"), flag); + } + + memcpy (insert_key + i,B_N_PKEY(S_new[i],0),KEY_SIZE); + insert_ptr[i] = S_new[i]; + +#ifdef CONFIG_REISERFS_CHECK + if (S_new[i]->b_count != 1) { + if (buffer_journaled(S_new[i]) || buffer_journal_dirty(S_new[i])) { + ; + } else { + reiserfs_panic (tb->tb_sb, "PAP-12247: balance_leaf: S_new[%d]->b_count=%u blocknr = %lu\n", i, S_new[i]->b_count, + S_new[i]->b_blocknr); + } + } +#endif + + } + + /* if the affected item was not wholly shifted then we perform all + necessary operations on that part or whole of the affected item which + remains in S */ + if ( 0 <= item_pos && item_pos < tb->s0num ) { + /* if we must insert or append into buffer S[0] */ + + switch (flag) { + case M_INSERT: /* insert item into S[0] */ + bi.bi_bh = tbS0; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + bi.bi_position = PATH_H_POSITION (tb->tb_path, 1); + leaf_insert_into_buf (tb->tb_sb, &bi, item_pos, ih, body, zeros_number); + + /* If we insert the first key change the delimiting key */ + if( item_pos == 0 ) { + if (tb->CFL[0]) /* can be 0 in reiserfsck */ + replace_key (tb->tb_sb, tb->CFL[0], tb->lkey[0],tbS0,0); + } + break; + + case M_PASTE: { /* append item in S[0] */ + struct item_head * pasted; + + pasted = B_N_PITEM_HEAD (tbS0, item_pos); + /* when directory, may be new entry already pasted */ + if (I_IS_DIRECTORY_ITEM (pasted)) { + if ( pos_in_item >= 0 && pos_in_item <= ih_entry_count (pasted) ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! tb->insert_size[0] ) + reiserfs_panic (tb->tb_sb, "PAP-12260: balance_leaf: insert_size is 0 already"); +#endif /* CONFIG_REISERFS_CHECK */ + + /* prepare space */ + bi.bi_bh = tbS0; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + bi.bi_position = PATH_H_POSITION (tb->tb_path, 1); + leaf_paste_in_buffer(tb->tb_sb, &bi, item_pos, pos_in_item, tb->insert_size[0], body, zeros_number); + + /* paste entry */ + leaf_paste_entries (bi.bi_bh, item_pos, pos_in_item, 1, (struct reiserfs_de_head *)body, + body + DEH_SIZE, tb->insert_size[0]); + if ( ! item_pos && ! pos_in_item ) { + +#ifdef CONFIG_REISERFS_CHECK + if (!tb->CFL[0]) + reiserfs_panic (tb->tb_sb, "PAP-12270: balance_leaf: " + "is not able to update left dkey"); +#endif /* CONFIG_REISERFS_CHECK */ + + if (tb->CFL[0]) // can be 0 in reiserfsck + replace_key(tb->tb_sb, tb->CFL[0], tb->lkey[0],tbS0,0); + } + tb->insert_size[0] = 0; + } + } else { /* regular object */ + if ( pos_in_item == pasted->ih_item_len ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->insert_size[0] <= 0 ) + reiserfs_panic (tb->tb_sb, + "PAP-12275: balance_leaf: insert size must not be %d", tb->insert_size[0]); +#endif /* CONFIG_REISERFS_CHECK */ + + bi.bi_bh = tbS0; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + bi.bi_position = PATH_H_POSITION (tb->tb_path, 1); + leaf_paste_in_buffer (tb->tb_sb, &bi, item_pos, pos_in_item, tb->insert_size[0], body, zeros_number); + + if (I_IS_INDIRECT_ITEM (pasted)) { + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->insert_size[0] != UNFM_P_SIZE ) + reiserfs_panic (tb->tb_sb, + "PAP-12280: balance_leaf: insert_size for indirect item must be %d, not %d", + UNFM_P_SIZE, tb->insert_size[0]); +#endif /* CONFIG_REISERFS_CHECK */ + + //pasted->u.ih_free_space = ((struct unfm_nodeinfo*)body)->unfm_freespace; + set_free_space (pasted, ((struct unfm_nodeinfo*)body)->unfm_freespace); + } + tb->insert_size[0] = 0; + } + +#ifdef CONFIG_REISERFS_CHECK + else { + if ( tb->insert_size[0] ) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "12285"); + reiserfs_panic (tb->tb_sb, "PAP-12285: balance_leaf: " + "insert_size must be 0 (%d), pos_in_item %d. (%h)", + tb->insert_size[0], pos_in_item, pasted); + } + } +#endif /* CONFIG_REISERFS_CHECK */ + + } + } /* case M_PASTE: */ + } + } + +#ifdef CONFIG_REISERFS_CHECK + if ( flag == M_PASTE && tb->insert_size[0] ) { + print_tb (M_PASTE, item_pos, pos_in_item, tb, "balance"); + reiserfs_panic (tb->tb_sb, "PAP-12290: balance_leaf: insert_size is still not 0 (%d)", tb->insert_size[0]); + } +#endif /* CONFIG_REISERFS_CHECK */ + + return 0; +} /* Leaf level of the tree is balanced (end of balance_leaf) */ + + + +/* Make empty node */ +void make_empty_node (struct buffer_info * bi) +{ + struct block_head * blkh; + +#ifdef CONFIG_REISERFS_CHECK + if (bi->bi_bh == NULL) + reiserfs_panic (0, "PAP-12295: make_empty_node: pointer to the buffer is NULL"); +#endif + + (blkh = B_BLK_HEAD(bi->bi_bh))->blk_nr_item = 0; + blkh->blk_free_space = MAX_CHILD_SIZE(bi->bi_bh); + + if (bi->bi_parent) + B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size = 0; +} + + +/* Get first empty buffer */ +struct buffer_head * get_FEB (struct tree_balance * tb) +{ + int i; + struct buffer_head * first_b; + struct buffer_info bi; + + for (i = 0; i < MAX_FEB_SIZE; i ++) + if (tb->FEB[i] != 0) + break; + + if (i == MAX_FEB_SIZE) + reiserfs_panic("vs-12300: get_FEB: FEB list is empty"); + + bi.bi_bh = first_b = tb->FEB[i]; + bi.bi_parent = 0; + bi.bi_position = 0; + make_empty_node (&bi); + set_bit(BH_Uptodate, &first_b->b_state); + + tb->FEB[i] = 0; + tb->used[i] = first_b; + + return(first_b); +} + + +/* Replace n_dest'th key in buffer dest by n_src'th key of buffer src.*/ +void replace_key (reiserfs_filsys_t fs, + struct buffer_head * dest, int n_dest, + struct buffer_head * src, int n_src) +{ + +#ifdef CONFIG_REISERFS_CHECK + if (dest == NULL || src == NULL) + reiserfs_panic (0, "vs-12305: replace_key: sourse or destination buffer is 0 (src=%p, dest=%p)", src, dest); + + if ( ! B_IS_KEYS_LEVEL (dest) ) + reiserfs_panic (0, "vs-12310: replace_key: invalid level (%d) for destination buffer. Must be > %d", + B_BLK_HEAD(dest)->blk_level, DISK_LEAF_NODE_LEVEL); + + if (n_dest < 0 || n_src < 0) + reiserfs_panic (0, "vs-12315: replace_key: src(%d) or dest(%d) key number less than 0", n_src, n_dest); + + if (n_dest >= B_NR_ITEMS(dest) || n_src >= B_NR_ITEMS(src)) + reiserfs_panic (0, "vs-12320: replace_key: src(%d(%d)) or dest(%d(%d)) key number is too big", + n_src, B_NR_ITEMS(src), n_dest, B_NR_ITEMS(dest)); +#endif /* CONFIG_REISERFS_CHECK */ + + if (dest) { + if (is_leaf_node (src)) + /* source buffer contains leaf node */ + memcpy (B_N_PDELIM_KEY(dest,n_dest), B_N_PITEM_HEAD(src,n_src), KEY_SIZE); + else + memcpy (B_N_PDELIM_KEY(dest,n_dest), B_N_PDELIM_KEY(src,n_src), KEY_SIZE); + + mark_buffer_dirty(dest); + } +} + + +void reiserfs_invalidate_buffer (struct tree_balance * tb, struct buffer_head * bh, int do_free_block) +{ + + B_BLK_HEAD (bh)->blk_level = FREE_LEVEL; + clear_bit(BH_Dirty, &bh->b_state); + +#ifdef CONFIG_REISERFS_CHECK + B_NR_ITEMS (bh) = 0; +#endif + + if (do_free_block) { + struct buffer_head * to_be_forgotten; + + to_be_forgotten = find_buffer (bh->b_dev, bh->b_blocknr, bh->b_size); + if (to_be_forgotten) { + to_be_forgotten->b_count ++; + bforget (to_be_forgotten); + } + + reiserfs_free_block (tb->tb_sb, bh->b_blocknr); + } +} + + +int get_left_neighbor_position ( + struct tree_balance * tb, + int h + ) +{ + int Sh_position = PATH_H_POSITION (tb->tb_path, h + 1); + +#ifdef CONFIG_REISERFS_CHECK + if (PATH_H_PPARENT (tb->tb_path, h) == 0 || tb->FL[h] == 0) + reiserfs_panic (tb->tb_sb, "vs-12325: get_left_neighbor_position: FL[%d](%p) or F[%d](%p) does not exist", + h, tb->FL[h], h, PATH_H_PPARENT (tb->tb_path, h)); +#endif + + if (Sh_position == 0) + return B_NR_ITEMS (tb->FL[h]); + else + return Sh_position - 1; +} + + +int get_right_neighbor_position (struct tree_balance * tb, int h) +{ + int Sh_position = PATH_H_POSITION (tb->tb_path, h + 1); + +#ifdef CONFIG_REISERFS_CHECK + if (PATH_H_PPARENT (tb->tb_path, h) == 0 || tb->FR[h] == 0) + reiserfs_panic (tb->tb_sb, "vs-12330: get_right_neighbor_position: F[%d](%p) or FR[%d](%p) does not exist", + h, PATH_H_PPARENT (tb->tb_path, h), h, tb->FR[h]); +#endif + + if (Sh_position == B_NR_ITEMS (PATH_H_PPARENT (tb->tb_path, h))) + return 0; + else + return Sh_position + 1; +} + + +#ifdef CONFIG_REISERFS_CHECK + +int is_reusable (struct super_block * s, unsigned long block, int bit_value); +static void check_internal_node (struct super_block * s, struct buffer_head * bh, char * mes) +{ + struct disk_child * dc; + int i; + + if (!bh) + reiserfs_panic (s, "PAP-12336: check_internal_node: bh == 0"); + + if (!bh || !B_IS_IN_TREE (bh)) + return; + + if (!buffer_dirty (bh) && !buffer_journaled(bh) ) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, mes); + reiserfs_panic (s, "PAP-12337: check_internal_node: buffer (%b) must be dirty", bh); + } + + dc = B_N_CHILD (bh, 0); + + for (i = 0; i <= B_NR_ITEMS (bh); i ++, dc ++) { + if (!is_reusable (s, dc->dc_block_number, 1) ) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, mes); + reiserfs_panic (s, "PAP-12338: check_internal_node: invalid child pointer %y in %b", dc, bh); + } + if (dc->dc_size <= BLKH_SIZE) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, mes); + reiserfs_panic (s, "PAP-12338: check_internal_node: empty node in the tree? %y", dc); + } + } +} + + +static int locked_or_not_in_tree (struct buffer_head * bh, char * which) +{ + if ( buffer_locked (bh) || !B_IS_IN_TREE (bh) ) { + reiserfs_warning ("vs-12339: locked_or_not_in_tree: %s (%b)\n", which, bh); + return 1; + } + return 0; +} + + +static int check_before_balancing (struct tree_balance * tb, int mode) +{ + int retval = 0; + int pos_in_item = tb->tb_path->pos_in_item; + + if (mode == M_PASTE) { + // make sure paste can be performed with given parameters + struct item_head * ih; + + ih = get_ih (tb->tb_path); + if (I_IS_INDIRECT_ITEM (ih)) { + // we can paste only to the end for now + if (pos_in_item != I_UNFM_NUM (ih)) + reiserfs_panic (tb->tb_sb, "vs-12333: check_before_balancing: " + "pos_in_item %d set improperly to paste indirect item %h", + pos_in_item, ih); + } + if (I_IS_DIRECT_ITEM (ih)) { + // we can paste only to the end for now + if (pos_in_item != ih_item_len (ih)) + reiserfs_panic (tb->tb_sb, "vs-12334: check_before_balancing: " + "pos_in_item %d set improperly to paste direct item %h", + pos_in_item, ih); + } + } + + if ( cur_tb ) { + reiserfs_panic (tb->tb_sb, "vs-12335: check_before_balancing: " + "suspect that schedule occurred based on cur_tb not being null at this point in code. " + "do_balance cannot properly handle schedule occuring while it runs."); + } + + /* double check that buffers that we will modify are unlocked. (fix_nodes + should already have prepped all of these for us). */ + if ( tb->lnum[0] ) { + retval |= locked_or_not_in_tree (tb->L[0], "L[0]"); + retval |= locked_or_not_in_tree (tb->FL[0], "FL[0]"); + retval |= locked_or_not_in_tree (tb->CFL[0], "CFL[0]"); + check_leaf (tb->L[0]); + } + if ( tb->rnum[0] ) { + retval |= locked_or_not_in_tree (tb->R[0], "R[0]"); + retval |= locked_or_not_in_tree (tb->FR[0], "FR[0]"); + retval |= locked_or_not_in_tree (tb->CFR[0], "CFR[0]"); + check_leaf (tb->R[0]); + } + retval |= locked_or_not_in_tree (PATH_PLAST_BUFFER (tb->tb_path), "S[0]"); + check_leaf (PATH_PLAST_BUFFER (tb->tb_path)); + return retval; +} + + +static void check_after_balance_leaf (struct tree_balance * tb) +{ + if (tb->lnum[0]) { + if (B_BLK_HEAD (tb->L[0])->blk_free_space != + MAX_CHILD_SIZE (tb->L[0]) - B_N_CHILD (tb->FL[0], get_left_neighbor_position (tb, 0))->dc_size) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "12221"); + reiserfs_panic (tb->tb_sb, "PAP-12355: check_after_balance_leaf: shift to left was incorrect"); + } + } + if (tb->rnum[0]) { + if (B_BLK_HEAD (tb->R[0])->blk_free_space != + MAX_CHILD_SIZE (tb->R[0]) - B_N_CHILD (tb->FR[0], get_right_neighbor_position (tb, 0))->dc_size) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "12222"); + reiserfs_panic (tb->tb_sb, "PAP-12360: check_after_balance_leaf: shift to right was incorrect"); + } + } + if (PATH_H_PBUFFER(tb->tb_path,1) && + B_BLK_HEAD (PATH_H_PBUFFER(tb->tb_path,0))->blk_free_space != + MAX_CHILD_SIZE (PATH_H_PBUFFER(tb->tb_path,0)) - + B_N_CHILD (PATH_H_PBUFFER(tb->tb_path,1), + PATH_H_POSITION (tb->tb_path, 1))->dc_size) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "12223"); + reiserfs_panic (tb->tb_sb, "PAP-12365: check_after_balance_leaf: S is incorrect"); + } +} + + +static void compare_pair (struct buffer_head * bh1, struct buffer_head * bh2) +{ + int cur_free, node_size; + + if (!bh1 || !bh2) + return; + + cur_free = node_free_space (bh1); + node_size = bh1->b_size - BLKH_SIZE - ((is_left_mergeable (B_N_PITEM_HEAD (bh2, 0), bh1->b_size)) ? IH_SIZE : 0); + if (cur_free >= node_size) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "12366"); + reiserfs_panic (0, "vs-12366: check_leaf_level: balance condition denied bh1 %z, bh2 %z", + bh1, bh2); + } +} + + +static void check_leaf_level (struct tree_balance * tb) +{ + struct buffer_head * S0 = get_bh (tb->tb_path); + struct buffer_head * bhs[5] = {0, }; + int i; + + // check item types, internal structures of items, etc + check_leaf (tb->L[0]); + check_leaf (tb->R[0]); + check_leaf (S0); + + if (tb->blknum[0] > 2) { + reiserfs_warning ("More than one new node on the leaf level\n"); + return; + } + + // check balance condition for any neighboring buffers + i = 0; + if (tb->L[0] && B_IS_IN_TREE (tb->L[0])) + bhs[i ++] = tb->L[0]; + if (B_IS_IN_TREE (S0)) + bhs[i ++] = S0; + if (tb->blknum[0] == 2) { + if (!B_IS_ITEMS_LEVEL(tb->used[0])) { + reiserfs_warning ("can not find new node\n"); + return; + } + bhs[i ++] = tb->used[0]; + } + if (tb->R[0] && B_IS_IN_TREE (tb->R[0])) { + bhs[i ++] = tb->R[0]; + } + + for (i = 0; i < 4; i ++) { + compare_pair (bhs[i], bhs[i + 1]); + } + +} + + +static void check_after_balancing (struct tree_balance * tb) +{ + int h; + + check_leaf_level (tb); + + /* check all internal nodes */ + for (h = 1; tb->insert_size[h]; h ++) { + check_internal_node (tb->tb_sb, PATH_H_PBUFFER (tb->tb_path, h), "BAD BUFFER ON PATH"); + if (tb->lnum[h]) + check_internal_node (tb->tb_sb, tb->L[h], "BAD L"); + if (tb->rnum[h]) + check_internal_node (tb->tb_sb, tb->R[h], "BAD R"); + } + +} + +#endif + + + + + + +/* Now we have all of the buffers that must be used in balancing of the tree. + We rely on the assumption that schedule() will not occur while do_balance + works. ( Only interrupt handlers are acceptable.) We balance the tree + according to the analysis made before this, using buffers already obtained. + For SMP support it will someday be necessary to add ordered locking of + tb. */ + +/* Some interesting rules of balancing: + + we delete a maximum of two nodes per level per balancing: we never delete R, when we delete two + of three nodes L, S, R then we move them into R. + + we only delete L if we are deleting two nodes, if we delete only one node we delete S + + if we shift leaves then we shift as much as we can: this is a deliberate policy of extremism in + node packing which results in higher average utilization after repeated random balance + operations at the cost of more memory copies and more balancing as a result of small insertions + to full nodes. + + if we shift internal nodes we try to evenly balance the node utilization, with consequent less + balancing at the cost of lower utilization. + + one could argue that the policy for directories in leaves should be that of internal nodes, but + we will wait until another day to evaluate this.... It would be nice to someday measure and + prove these assumptions as to what is optimal.... + +*/ + +void do_balance (struct tree_balance * tb, /* tree_balance structure */ + struct item_head * ih, /* item header of inserted item */ + const char * body, /* body of inserted item or bytes to paste */ + int flag, /* i - insert, d - delete + c - cut, p - paste + + Cut means delete part of an item (includes + removing an entry from a directory). + + Delete means delete whole item. + + Insert means add a new item into the tree. + + Paste means to append to the end of an existing + file or to insert a directory entry. */ + int zeros_num) +{ + //int pos_in_item = tb->tb_path->pos_in_item; + int child_pos, /* position of a child node in its parent */ + h; /* level of the tree being processed */ + struct item_head insert_key[2]; /* in our processing of one level we + sometimes determine what must be + inserted into the next higher level. + This insertion consists of a key or two + keys and their corresponding pointers */ + struct buffer_head *insert_ptr[2]; /* inserted node-ptrs for the next + level */ + + +#ifdef CONFIG_REISERFS_CHECK + memcpy(&init_tb, tb, sizeof(struct tree_balance)); + init_item_pos = PATH_LAST_POSITION (tb->tb_path); + init_pos_in_item = tb->tb_path->pos_in_item;//pos_in_item; + init_mode = flag; + + /* do not delete, just comment it out */ + /*print_tb(flag, PATH_LAST_POSITION(tb->tb_path), pos_in_item, tb, "check");*/ + + if (check_before_balancing (tb/*, pos_in_item*/, flag)) + reiserfs_panic (tb->tb_sb, "PAP-12340: do_balance: " + "balancing can not be performed"); + + cur_tb = tb; +#endif /* CONFIG_REISERFS_CHECK */ + + /* if we have no real work to do */ + if ( ! tb->insert_size[0] ) { +#ifdef CONFIG_REISERFS_CHECK + cur_tb = NULL; + if (flag != M_CUT) + reiserfs_panic (tb->tb_sb, "PAP-12350: do_balance: insert_size == 0, mode == %c", flag); +#endif + unfix_nodes(/*th,*/ tb); + return; + } + +#ifndef FU //REISERFS_FSCK + if (flag == M_INTERNAL) { + insert_ptr[0] = (struct buffer_head *)body; + /* we must prepare insert_key */ + + if (PATH_H_B_ITEM_ORDER (tb->tb_path, 0)/*LAST_POSITION (tb->tb_path)*//*item_pos*/ == -1) { + /* get delimiting key from buffer in tree */ + copy_key (&insert_key[0].ih_key, B_N_PKEY (PATH_PLAST_BUFFER (tb->tb_path), 0)); + /*insert_ptr[0]->b_item_order = 0;*/ + } else { + /* get delimiting key from new buffer */ + copy_key (&insert_key[0].ih_key, B_N_PKEY((struct buffer_head *)body,0)); + /*insert_ptr[0]->b_item_order = item_pos;*/ + } + + /* and insert_ptr instead of balance_leaf */ + child_pos = PATH_H_B_ITEM_ORDER (tb->tb_path, 0)/*item_pos*/; + } else +#endif + + /* balance leaf returns 0 except if combining L R and S into one node. + see balance_internal() for explanation of this line of code.*/ + child_pos = PATH_H_B_ITEM_ORDER (tb->tb_path, 0) + + balance_leaf (/*th,*/ tb/*, pos_in_item*/, ih, body, flag, zeros_num, insert_key, insert_ptr); + +#ifdef CONFIG_REISERFS_CHECK + check_after_balance_leaf (tb); +#endif + + /* Balance internal level of the tree. */ + for ( h = 1; h < MAX_HEIGHT && tb->insert_size[h]; h++ ) + child_pos = balance_internal (/*th,*/ tb, h, child_pos, insert_key, insert_ptr); + +#ifdef CONFIG_REISERFS_CHECK + cur_tb = NULL; + check_after_balancing (tb); +#endif + + /* Release all (except for S[0]) non NULL buffers fixed by fix_nodes() */ + unfix_nodes(/*th,*/ tb); + +#ifdef CONFIG_REISERFS_CHECK + tb->tb_sb->u.reiserfs_sb.s_do_balance ++; +#endif + +} + + + + + + + + + diff --git a/reiserfscore/fix_node.c b/reiserfscore/fix_node.c new file mode 100644 index 0000000..6b18240 --- /dev/null +++ b/reiserfscore/fix_node.c @@ -0,0 +1,2916 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +/** + ** old_item_num + ** old_entry_num + ** set_entry_sizes + ** create_virtual_node + ** check_left + ** check_right + ** directory_part_size + ** get_num_ver + ** item_length + ** set_parameters + ** is_leaf_removable + ** are_leaves_removable + ** get_empty_nodes + ** get_lfree + ** get_rfree + ** is_left_neighbor_in_cache + ** decrement_key + ** get_far_parent + ** get_parents + ** can_node_be_removed + ** ip_check_balance + ** dc_check_balance_internal + ** dc_check_balance_leaf + ** dc_check_balance + ** check_balance + ** get_direct_parent + ** get_neighbors + ** fix_nodes + ** + ** + **/ + + +#include "includes.h" + +__u64 get_bytes_number (struct item_head * ih, int blocksize) +{ + switch (get_type (&ih->ih_key)) { + case TYPE_DIRECT: + return ih_item_len (ih); + case TYPE_INDIRECT: + return I_UNFM_NUM(ih) * blocksize - ih_free_space (ih); + case TYPE_STAT_DATA: + return 0; + } + reiserfs_warning (stderr, "get_bytes_number: called for wrong type of item %h", ih); + return 0; +} + +/* To make any changes in the tree we find a node, that contains item + to be changed/deleted or position in the node we insert a new item + to. We call this node S. To do balancing we need to decide what we + will shift to left/right neighbor, or to a new node, where new item + will be etc. To make this analysis simpler we build virtual + node. Virtual node is an array of items, that will replace items of + node S. (For instance if we are going to delete an item, virtual + node does not contain it). Virtual node keeps information about + item sizes and types, mergeability of first and last items, sizes + of all entries in directory item. We use this array of items when + calculating what we can shift to neighbors and how many nodes we + have to have if we do not any shiftings, if we shift to left/right + neighbor or to both. */ + + +/* taking item number in virtual node, returns number of item, that it has in source buffer */ +static inline int old_item_num (int new_num, int affected_item_num, int mode) +{ + if (mode == M_PASTE || mode == M_CUT || new_num < affected_item_num) + return new_num; + + if (mode == M_INSERT) { + +#ifdef CONFIG_REISERFS_CHECK + if (new_num == 0) + reiserfs_panic (0,"vs-8005: old_item_num: for INSERT mode and item number of inserted item"); +#endif + + return new_num - 1; + } + +#ifdef CONFIG_REISERFS_CHECK + if (mode != M_DELETE) + reiserfs_panic (0, "vs-8010: old_item_num: mode must be M_DELETE (mode = \'%c\'", mode); +#endif + + /* delete mode */ + return new_num + 1; +} + + +/* + * function returns old entry number in directory item in real node + * using new entry number in virtual item in virtual node */ +static inline int old_entry_num (int new_num, int affected_item_num, int new_entry_num, int pos_in_item, int mode) +{ + if ( mode == M_INSERT || mode == M_DELETE) + return new_entry_num; + + if (new_num != affected_item_num) { + /* cut or paste is applied to another item */ + return new_entry_num; + } + + if (new_entry_num < pos_in_item) + return new_entry_num; + + if (mode == M_CUT) + return new_entry_num + 1; + +#ifdef CONFIG_REISERFS_CHECK + if (mode != M_PASTE) + reiserfs_panic (0, "vs-8015: old_entry_num: mode must be M_PASTE (mode = \'%c\'", mode); +#endif + + return new_entry_num - 1; +} + + + +/* + * Create an array of sizes of directory entries for virtual item + */ +static void set_entry_sizes (struct tree_balance * tb, + int old_num, int new_num, + struct buffer_head * bh, + struct item_head * ih + ) +{ + struct virtual_node * vn = tb->tb_vn; + int i; + struct reiserfs_de_head * deh; + struct virtual_item * vi; + + deh = B_I_DEH (bh, ih); + + /* seek to given virtual item in array of virtual items */ + vi = vn->vn_vi + new_num; + + /* virtual directory item have this amount of entry after */ + vi->vi_entry_count = ih_entry_count (ih) + + ((old_num == vn->vn_affected_item_num) ? ((vn->vn_mode == M_CUT) ? -1 : + (vn->vn_mode == M_PASTE ? 1 : 0)) : 0); + +#ifdef CONFIG_REISERFS_CHECK + /* check whether we have enough space for array of entry sizes */ + if (tb->vn_buf + tb->vn_buf_size - vn->vn_free_ptr < vi->vi_entry_count * sizeof (__u16)) + reiserfs_panic (tb->tb_sb, "vs-8020: set_entry_sizes: " + "no enough space for %d entries of virtual item", vi->vi_entry_count); +#endif + + vi->vi_entry_sizes = (__u16 *)vn->vn_free_ptr; + vn->vn_free_ptr += vi->vi_entry_count * sizeof (__u16); + + /* set sizes of old entries */ + for (i = 0; i < vi->vi_entry_count; i ++) { + int j; + + j = old_entry_num (old_num, vn->vn_affected_item_num, i, vn->vn_pos_in_item, vn->vn_mode); + vi->vi_entry_sizes[i] = entry_length (ih, &(deh[j]), j) + DEH_SIZE; + } + + /* set size of pasted entry */ + if (old_num == vn->vn_affected_item_num && vn->vn_mode == M_PASTE) + vi->vi_entry_sizes[vn->vn_pos_in_item] = tb->insert_size[0]; + + +#ifdef CONFIG_REISERFS_CHECK + /* compare total size of entries with item length */ + { + int k, l; + + l = 0; + for (k = 0; k < vi->vi_entry_count; k ++) + l += vi->vi_entry_sizes[k]; + + if (l + IH_SIZE != vi->vi_item_len + + ((old_num == vn->vn_affected_item_num && (vn->vn_mode == M_PASTE || vn->vn_mode == M_CUT)) ? tb->insert_size[0] : 0) ) { + reiserfs_panic (0, "vs-8025: set_entry_sizes: (mode==%c, old_num==%d, aff_num==%d, insert_size==%d), invalid length of directory item", + vn->vn_mode, old_num, vn->vn_affected_item_num, tb->insert_size[0]); + } + } +#endif + +} + + +static void create_virtual_node (struct tree_balance * tb, int h) +{ + struct item_head * ih; + struct virtual_node * vn = tb->tb_vn; + int new_num; + struct buffer_head * Sh; /* this comes from tb->S[h] */ + + struct item_head * temp_ih; + + Sh = PATH_H_PBUFFER (tb->tb_path, h); + + temp_ih = B_N_PITEM_HEAD (PATH_PLAST_BUFFER (tb->tb_path), B_NR_ITEMS (PATH_PLAST_BUFFER (tb->tb_path)) - 1); + + /* size of changed node */ + vn->vn_size = MAX_CHILD_SIZE (Sh) - B_BLK_HEAD (Sh)->blk_free_space + tb->insert_size[h]; + + /* for internal nodes array if virtual items is not created */ + if (h) { + vn->vn_nr_item = (vn->vn_size - DC_SIZE) / (DC_SIZE + KEY_SIZE); + return; + } + + /* number of items in virtual node */ + vn->vn_nr_item = B_NR_ITEMS (Sh) + ((vn->vn_mode == M_INSERT)? 1 : 0) - ((vn->vn_mode == M_DELETE)? 1 : 0); + + /* first virtual item */ + vn->vn_vi = (struct virtual_item *)(tb->tb_vn + 1); + memset (vn->vn_vi, 0, vn->vn_nr_item * sizeof (struct virtual_item)); + vn->vn_free_ptr += vn->vn_nr_item * sizeof (struct virtual_item); + + + /* first item in the node */ + ih = B_N_PITEM_HEAD (Sh, 0); + + /* define the mergeability for 0-th item (if it is not being deleted) */ +#ifndef FU //REISERFS_FSCK + if (is_left_mergeable (tb->tb_sb, tb->tb_path) == 1 && (vn->vn_mode != M_DELETE || vn->vn_affected_item_num)) +#else + if (is_left_mergeable (ih, Sh->b_size) && (vn->vn_mode != M_DELETE || vn->vn_affected_item_num)) +#endif + vn->vn_vi[0].vi_type |= VI_TYPE_LEFT_MERGEABLE; + + /* go through all items those remain in the virtual node (except for the new (inserted) one) */ + for (new_num = 0; new_num < vn->vn_nr_item; new_num ++) { + int j; + + if (vn->vn_affected_item_num == new_num && vn->vn_mode == M_INSERT) + continue; + + /* get item number in source node */ + j = old_item_num (new_num, vn->vn_affected_item_num, vn->vn_mode); + + vn->vn_vi[new_num].vi_item_len += ih[j].ih_item_len + IH_SIZE; + + if (I_IS_STAT_DATA_ITEM (ih + j)) { + vn->vn_vi[new_num].vi_type |= VI_TYPE_STAT_DATA; + +#ifdef CONFIG_REISERFS_CHECK + if (new_num == vn->vn_affected_item_num && (vn->vn_mode == M_CUT || vn->vn_mode == M_PASTE)) + reiserfs_panic (0, "vs-8035: create_virtual_node: stat data cannot be affected item"); +#endif + + continue; + } + + /* set type of item */ + if (I_IS_DIRECT_ITEM (ih + j)) + vn->vn_vi[new_num].vi_type |= VI_TYPE_DIRECT; + + if (I_IS_INDIRECT_ITEM (ih + j)) + vn->vn_vi[new_num].vi_type |= VI_TYPE_INDIRECT; + + if (I_IS_DIRECTORY_ITEM (ih + j)) { + set_entry_sizes (tb, j, new_num, Sh, ih + j); + vn->vn_vi[new_num].vi_type |= VI_TYPE_DIRECTORY; + if (ih[j].ih_key.u.k_offset_v1.k_offset == DOT_OFFSET) + vn->vn_vi[new_num].vi_type |= VI_TYPE_FIRST_DIRECTORY_ITEM; + } + + if (new_num != vn->vn_affected_item_num) + /* this is not being changed */ + continue; + + if (vn->vn_mode == M_PASTE || vn->vn_mode == M_CUT) + vn->vn_vi[new_num].vi_item_len += tb->insert_size[0]; + } + + + /* virtual inserted item is not defined yet */ + if (vn->vn_mode == M_INSERT) { + +#ifdef CONFIG_REISERFS_CHECK + if (vn->vn_ins_ih == 0) + reiserfs_panic (0, "vs-8040: create_virtual_node: item header of inserted item is not specified"); +#endif + + vn->vn_vi[vn->vn_affected_item_num].vi_item_len = tb->insert_size[0]; + + switch (get_type (&vn->vn_ins_ih->ih_key)) { + case TYPE_STAT_DATA: + vn->vn_vi[vn->vn_affected_item_num].vi_type |= VI_TYPE_STAT_DATA; + break; + case TYPE_DIRECT: + vn->vn_vi[vn->vn_affected_item_num].vi_type |= VI_TYPE_DIRECT; + break; + case TYPE_INDIRECT: + vn->vn_vi[vn->vn_affected_item_num].vi_type |= VI_TYPE_INDIRECT; + break; + default: + /* inseted item is directory (it must be item with "." and "..") */ + vn->vn_vi[vn->vn_affected_item_num].vi_type |= + (VI_TYPE_DIRECTORY | VI_TYPE_FIRST_DIRECTORY_ITEM | VI_TYPE_INSERTED_DIRECTORY_ITEM); + + /* this directory item can not be split, so do not set sizes of entries */ + break; + } + } + + /* set right merge flag we take right delimiting key and check whether it is a mergeable item */ + if (tb->CFR[0]) { + ih = (struct item_head *)B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]); +#ifndef FU //REISERFS_FSCK + if (is_right_mergeable (tb->tb_sb, tb->tb_path) == 1 && (vn->vn_mode != M_DELETE || + vn->vn_affected_item_num != B_NR_ITEMS (Sh) - 1)) +#else + if (is_left_mergeable (ih, Sh->b_size) && (vn->vn_mode != M_DELETE || + vn->vn_affected_item_num != B_NR_ITEMS (Sh) - 1)) +#endif + vn->vn_vi[vn->vn_nr_item-1].vi_type |= VI_TYPE_RIGHT_MERGEABLE; + +#ifdef CONFIG_REISERFS_CHECK + if (is_left_mergeable (ih, Sh->b_size) && + !(vn->vn_mode != M_DELETE || vn->vn_affected_item_num != B_NR_ITEMS (Sh) - 1) ) { + /* we delete last item and it could be merged with right neighbor's first item */ + if (!(B_NR_ITEMS (Sh) == 1 && I_IS_DIRECTORY_ITEM (B_N_PITEM_HEAD (Sh, 0)) && + ih_entry_count (B_N_PITEM_HEAD (Sh, 0)) == 1)) { + /* node contains more than 1 item, or item is not directory item, or this item contains more than 1 entry */ + print_block (Sh, 0, -1, -1); + reiserfs_panic (tb->tb_sb, "vs-8045: create_virtual_node: rdkey %k, affected item==%d (mode==%c) Must be %c", + &(ih->ih_key), vn->vn_affected_item_num, vn->vn_mode, M_DELETE); + } else + /* we can delete directory item, that has only one directory entry in it */ + ; + } +#endif + + } +} + + +/* using virtual node check, how many items can be shifted to left + neighbor */ +static int check_left (struct tree_balance * tb, int h, int cur_free) +{ + int i; + struct virtual_node * vn = tb->tb_vn; + int d_size, ih_size, bytes = -1; + +#ifdef CONFIG_REISERFS_CHECK + if (cur_free < 0) + reiserfs_panic (0, "vs-8050: check_left: cur_free (%d) < 0", cur_free); +#endif + + /* internal level */ + if (h > 0) { + if (!cur_free ) { + tb->lnum[h] = 0; + return 0; + } + tb->lnum[h] = cur_free / (DC_SIZE + KEY_SIZE); + return -1; + } + + /* leaf level */ + + if (!cur_free || !vn->vn_nr_item) { + /* no free space */ + tb->lnum[h] = 0; + tb->lbytes = -1; + return 0; + } + +#ifdef CONFIG_REISERFS_CHECK + if (!PATH_H_PPARENT (tb->tb_path, 0)) + reiserfs_panic (0, "vs-8055: check_left: parent does not exist or invalid"); +#endif + + if ((unsigned int)cur_free >= (vn->vn_size - ((vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE) ? IH_SIZE : 0))) { + /* all contents of S[0] fits into L[0] */ + +#ifdef CONFIG_REISERFS_CHECK + if (vn->vn_mode == M_INSERT || vn->vn_mode == M_PASTE) { + reiserfs_panic (0, "vs-8055: check_left: invalid mode or balance condition failed (cur_free %d)vn->vn_size %d", + cur_free, vn->vn_size); + } +#endif + + tb->lnum[0] = vn->vn_nr_item; + tb->lbytes = -1; + return -1; + } + + + d_size = 0, ih_size = IH_SIZE; + + /* first item may be merge with last item in left neighbor */ + if (vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE) + d_size = -((int)IH_SIZE), ih_size = 0; + + tb->lnum[0] = 0; + for (i = 0; i < vn->vn_nr_item; i ++, ih_size = IH_SIZE, d_size = 0) { + d_size += vn->vn_vi[i].vi_item_len; + if (cur_free >= d_size) { + /* the item can be shifted entirely */ + cur_free -= d_size; + tb->lnum[0] ++; + continue; + } + + /* the item cannot be shifted entirely, try to split it */ + /* check whether L[0] can hold ih and at least one byte of the item body */ + if (cur_free <= ih_size) { + /* cannot shift even a part of the current item */ + tb->lbytes = -1; + return -1; + } + cur_free -= ih_size; + + if (vn->vn_vi[i].vi_type & VI_TYPE_STAT_DATA || + vn->vn_vi[i].vi_type & VI_TYPE_INSERTED_DIRECTORY_ITEM) { + /* virtual item is a stat_data or empty directory body ("." and ".."), that is not split able */ + tb->lbytes = -1; + return -1; + } + + if (vn->vn_vi[i].vi_type & VI_TYPE_DIRECT) + /* body of a direct item can be split at any byte */ + tb->lbytes = bytes = cur_free; + + if (vn->vn_vi[i].vi_type & VI_TYPE_INDIRECT) + /* body of a indirect item can be split at unformatted pointer bound */ + tb->lbytes = bytes = cur_free - cur_free % UNFM_P_SIZE; + + /* item is of directory type */ + if (vn->vn_vi[i].vi_type & VI_TYPE_DIRECTORY) { + /* directory entries are the solid granules of the directory + item, they cannot be split in the middle */ + + /* calculate number of dir entries that can be shifted, and + their total size */ + int j; + struct virtual_item * vi; + + tb->lbytes = 0; + bytes = 0; + vi = &vn->vn_vi[i]; + + for (j = 0; j < vi->vi_entry_count; j ++) { + if (vi->vi_entry_sizes[j] > cur_free) + /* j-th entry doesn't fit into L[0] */ + break; + + bytes += vi->vi_entry_sizes[j]; + cur_free -= vi->vi_entry_sizes[j]; + tb->lbytes ++; + } + /* "." can not be cut from first directory item */ + if ((vn->vn_vi[i].vi_type & VI_TYPE_FIRST_DIRECTORY_ITEM) && tb->lbytes < 2) + tb->lbytes = 0; + } + + + if (tb->lbytes <= 0) { + /* nothing can flow from the item */ + tb->lbytes = -1; + return -1; + } + + /* something can flow from the item */ + tb->lnum[0] ++; + +#ifdef CONFIG_REISERFS_CHECK + if (bytes == -1) + reiserfs_panic (tb->tb_sb, "vs-8060: check_left: bytes is not initialized"); +#endif + + return bytes; /* part of split item in bytes */ + } + + + reiserfs_panic (0, "vs: 8065: check_left: all items fit in the left neighbor"); + return 0; +} + + + +/* using virtual node check, how many items can be shifted to right + neighbor */ +static int check_right (struct tree_balance * tb, int h, int cur_free) +{ + int i; + struct virtual_node * vn = tb->tb_vn; + int d_size, ih_size, bytes = -1; + +#ifdef CONFIG_REISERFS_CHECK + if (cur_free < 0) + reiserfs_panic (tb->tb_sb, "vs-8070: check_right: cur_free < 0"); +#endif + + /* internal level */ + if (h > 0) { + if (!cur_free) { + tb->rnum[h] = 0; + return 0; + } + tb->rnum[h] = cur_free / (DC_SIZE + KEY_SIZE); + return -1; + } + + /* leaf level */ + + if (!cur_free || !vn->vn_nr_item) { + /* no free space */ + tb->rnum[h] = 0; + tb->rbytes = -1; + return 0; + } + +#ifdef CONFIG_REISERFS_CHECK + if (!PATH_H_PPARENT (tb->tb_path, 0)) + reiserfs_panic (tb->tb_sb, "vs-8075: check_right: parent does not exist or invalid"); +#endif + + if ((unsigned int)cur_free >= (vn->vn_size - ((vn->vn_vi[vn->vn_nr_item-1].vi_type & VI_TYPE_RIGHT_MERGEABLE) ? IH_SIZE : 0))) + { + /* all contents of S[0] fits into R[0] */ + +#ifdef CONFIG_REISERFS_CHECK + if (vn->vn_mode == M_INSERT || vn->vn_mode == M_PASTE) + reiserfs_panic (tb->tb_sb, "vs-8080: check_right: invalid mode or balance condition failed"); +#endif + + tb->rnum[h] = vn->vn_nr_item; + tb->rbytes = -1; + return -1; + } + + d_size = 0, ih_size = IH_SIZE; + + /* last item may be merge with first item in right neighbor */ + if (vn->vn_vi[vn->vn_nr_item - 1].vi_type & VI_TYPE_RIGHT_MERGEABLE) + d_size = -(int)IH_SIZE, ih_size = 0; + + tb->rnum[0] = 0; + for (i = vn->vn_nr_item - 1; i >= 0; i --, d_size = 0, ih_size = IH_SIZE) + { + d_size += vn->vn_vi[i].vi_item_len; + if (cur_free >= d_size) + { + /* the item can be shifted entirely */ + cur_free -= d_size; + tb->rnum[0] ++; + continue; + } + + /* the item cannot be shifted entirely, try to split it */ + if (vn->vn_vi[i].vi_type & VI_TYPE_STAT_DATA || vn->vn_vi[i].vi_type & VI_TYPE_INSERTED_DIRECTORY_ITEM) + { + /* virtual item is a stat_data or empty directory body ("." and "..), that is not split able */ + tb->rbytes = -1; + return -1; + } + + /* check whether R[0] can hold ih and at least one byte of the item body */ + if ( cur_free <= ih_size ) + /* cannot shift even a part of the current item */ + { + tb->rbytes = -1; + return -1; + } + + /* R[0] can hold the header of the item and at least one byte of its body */ + cur_free -= ih_size; /* cur_free is still > 0 */ + + /* item is of direct type */ + if (vn->vn_vi[i].vi_type & VI_TYPE_DIRECT) + /* body of a direct item can be split at any byte */ + tb->rbytes = bytes = cur_free; + + /* item is of indirect type */ + if (vn->vn_vi[i].vi_type & VI_TYPE_INDIRECT) + /* an unformatted node pointer (having size long) is a solid granule of the item */ + tb->rbytes = bytes = cur_free - cur_free % UNFM_P_SIZE; + + /* item is of directory type */ + if (vn->vn_vi[i].vi_type & VI_TYPE_DIRECTORY) + { + int j; + struct virtual_item * vi; + + tb->rbytes = 0; + bytes = 0; + vi = &vn->vn_vi[i]; + + for (j = vi->vi_entry_count - 1; j >= 0; j --) + { + if (vi->vi_entry_sizes[j] > cur_free) + /* j-th entry doesn't fit into L[0] */ + break; + + bytes += vi->vi_entry_sizes[j]; + cur_free -= vi->vi_entry_sizes[j]; + tb->rbytes ++; + } + + /* ".." can not be cut from first directory item */ + if ((vn->vn_vi[i].vi_type & VI_TYPE_FIRST_DIRECTORY_ITEM) && tb->rbytes > vi->vi_entry_count - 2) { + +#ifdef CONFIG_REISERFS_CHECK + if (tb->rbytes > vi->vi_entry_count - 1) { + reiserfs_panic (tb->tb_sb, "vs-8085: check_right: all entries can be shifted to right neighbor"); + } +#endif + + tb->rbytes = vi->vi_entry_count - 2; + } + } + + if ( tb->rbytes <= 0 ) + { + /* nothing can flow from the item */ + tb->rbytes = -1; + return -1; + } + + + /* something can flow from the item */ + tb->rnum[0] ++; +#ifdef CONFIG_REISERFS_CHECK + if (bytes == -1) + reiserfs_panic (tb->tb_sb, "vs-8090: check_right: bytes is not initialized"); +#endif + return bytes; /* part of split item in bytes */ + } + + reiserfs_panic ("vs-8095: check_right: all items fit in the left neighbor"); + return 0; +} + + +/* sum of entry sizes between from-th and to-th entries including both edges */ +static int directory_part_size (struct virtual_item * vi, int from, int to) +{ + int i, retval; + + retval = 0; + for (i = from; i <= to; i ++) + retval += vi->vi_entry_sizes[i]; + + return retval; +} + + +/* + * from - number of items, which are shifted to left neighbor entirely + * to - number of item, which are shifted to right neighbor entirely + * from_bytes - number of bytes of boundary item (or directory entries) which are shifted to left neighbor + * to_bytes - number of bytes of boundary item (or directory entries) which are shifted to right neighbor */ +static int get_num_ver (int mode, struct tree_balance * tb, int h, + int from, int from_bytes, + int to, int to_bytes, + short * snum012, int flow + ) +{ + int i; + int bytes; + struct virtual_node * vn = tb->tb_vn; + struct virtual_item * vi; + + int total_node_size, max_node_size, current_item_size; + int needed_nodes; + int start_item, /* position of item we start filling node from */ + end_item, /* position of item we finish filling node by */ + start_bytes,/* number of first bytes (entries for directory) of start_item-th item + we do not include into node that is being filled */ + end_bytes; /* number of last bytes (entries for directory) of end_item-th item + we do node include into node that is being filled */ + int splitted_item_positions[2]; /* these are positions in virtual item of items, + that are splitted between S[0] and S1new and S1new and S2new */ + + +#ifdef CONFIG_REISERFS_CHECK + /* We only create additional nodes if we are in insert or paste mode + or we are in replace mode at the internal level. If h is 0 and + the mode is M_REPLACE then in fix_nodes we change the mode to + paste or insert before we get here in the code. */ + if ( tb->insert_size[h] < 0 || (mode != M_INSERT && mode != M_PASTE)) + reiserfs_panic (0, "vs-8100: get_num_ver: insert_size < 0 in overflow"); +#endif + + max_node_size = MAX_CHILD_SIZE (PATH_H_PBUFFER (tb->tb_path, h)); + + /* snum012 [0-2] - number of items, that lay + to S[0], first new node and second new node */ + snum012[3] = -1; /* s1bytes */ + snum012[4] = -1; /* s2bytes */ + + + /* internal level */ + if (h > 0) { + i = ((to - from) * (KEY_SIZE + DC_SIZE) + DC_SIZE); + if (i == max_node_size) + return 1; + return (i / max_node_size + 1); + } + + + /* leaf level */ + needed_nodes = 1; + total_node_size = 0; + + start_item = from; + start_bytes = from_bytes; + end_item = vn->vn_nr_item - to - 1; + end_bytes = to_bytes; + + /* go through all items begining from the start_item-th item and ending by + the end_item-th item. If start_bytes != -1 we skip first start_bytes + item units (entries in case of directory). If end_bytes != -1 we skip + end_bytes units of the end_item-th item. */ + for (i = start_item; i <= end_item; i ++) { + +#ifdef CONFIG_REISERFS_CHECK + if (needed_nodes > 3) + reiserfs_panic (0, "vs-8105: get_num_ver: too many nodes are needed"); +#endif + + /* get size of current item */ + current_item_size = (vi = &vn->vn_vi[i])->vi_item_len; + + /* do not take in calculation head part (from_bytes) of from-th item */ + if (i == start_item && start_bytes != -1) { + if (vi->vi_type & VI_TYPE_DIRECTORY) + current_item_size -= directory_part_size (vi, 0, start_bytes - 1); + else + current_item_size -= start_bytes; + } + + /* do not take in calculation tail part of (to-1)-th item */ + if (i == end_item && end_bytes != -1) { + if (vi->vi_type & VI_TYPE_DIRECTORY) + /* first entry, that is not included */ + current_item_size -= directory_part_size (vi, vi->vi_entry_count - end_bytes, vi->vi_entry_count - 1); + else + current_item_size -= end_bytes; + } + + /* if item fits into current node entirely */ + if (total_node_size + current_item_size <= max_node_size) { + snum012[needed_nodes - 1] ++; + total_node_size += current_item_size; + continue; + } + + if (current_item_size > max_node_size) { + /* virtual item length is longer, than max size of item in a node. It is impossible for direct item */ +#ifdef CONFIG_REISERFS_CHECK + if (vi->vi_type & VI_TYPE_DIRECT) + reiserfs_panic (0, "vs-8110: get_num_ver: direct item length is %d. It can not be longer than %d", + current_item_size, max_node_size); +#endif + /* we will try to split it */ + flow = 1; + } + + if (!flow) { + /* as we do not split items, take new node and continue */ + needed_nodes ++; i --; total_node_size = 0; + continue; + } + + if (total_node_size + (int)IH_SIZE >= max_node_size) { + /* even minimal item does not fit into current node, take new node and continue */ + needed_nodes ++, i--, total_node_size = 0; + continue; + } + if (vi->vi_type & VI_TYPE_STAT_DATA) { + + /* stat data can not be split */ + needed_nodes ++, i--, total_node_size = 0; + continue; + } + + /* body of a direct item can be split at any byte */ + /* bytes is free space in filled node */ + bytes = max_node_size - total_node_size - IH_SIZE; + + /* item is of indirect type */ + if (vi->vi_type & VI_TYPE_INDIRECT) + /* an unformatted node pointer (having size long) is a solid granule of the item */ + /* bytes of unformatted node pointers fits into free space of filled node */ + bytes -= (bytes) % UNFM_P_SIZE; + + /* S1bytes or S2bytes. It depends from needed_nodes */ + snum012[needed_nodes - 1 + 3] = bytes; + + /* item is of directory type */ + if (vi->vi_type & VI_TYPE_DIRECTORY) { + /* calculate, how many entries can be put into current node */ + int j; + int end_entry; + + snum012[needed_nodes - 1 + 3] = 0; + + total_node_size += IH_SIZE; + if (start_bytes == -1 || i != start_item) + start_bytes = 0; + + end_entry = vi->vi_entry_count - ((i == end_item && end_bytes != -1) ? end_bytes : 0); + for (j = start_bytes; j < end_entry; j ++) { + /* j-th entry doesn't fit into current node */ + if (total_node_size + vi->vi_entry_sizes[j] > max_node_size) + break; + snum012[needed_nodes - 1 + 3] ++; + bytes += vi->vi_entry_sizes[j]; + total_node_size += vi->vi_entry_sizes[j]; + } + /* "." can not be cut from first directory item */ + if (start_bytes == 0 && (vn->vn_vi[i].vi_type & VI_TYPE_FIRST_DIRECTORY_ITEM) && + snum012[needed_nodes - 1 + 3] < 2) + snum012[needed_nodes - 1 + 3] = 0; + + +#ifdef CONFIG_REISERFS_CHECK + if (vi->vi_entry_count && + vi->vi_entry_count - ((i == end_item && end_bytes != -1) ? end_bytes : 0) + - (start_bytes) <= snum012[needed_nodes - 1 + 3]) + reiserfs_panic (0, "vs-8115: get_num_ver: required part of directory fits into current node"); +#endif + } + + if (snum012[needed_nodes-1+3] <= 0 ) { + /* nothing fits into current node, take new node and continue */ + needed_nodes ++, i--, total_node_size = 0; + continue; + } + + /* something fits into the current node */ + if (vi->vi_type & VI_TYPE_DIRECTORY) + start_bytes += snum012[needed_nodes - 1 + 3]; + else + start_bytes = bytes; + + snum012[needed_nodes - 1] ++; + splitted_item_positions[needed_nodes - 1] = i; + + needed_nodes ++; + /* continue from the same item with start_bytes != -1 */ + start_item = i; + i --; + total_node_size = 0; + } + + + /* snum012[3] and snum012[4] contain how many bytes (entries) of + split item can be in S[0] and S1new. s1bytes and s2bytes are how + many bytes (entries) can be in S1new and S2new. Recalculate it */ + + if (snum012[4] > 0) { /* s2bytes */ + /* get number of item that is split between S1new and S2new */ + int split_item_num; + int bytes_to_r, bytes_to_l; + + split_item_num = splitted_item_positions[1]; + bytes_to_l = ((from == split_item_num && from_bytes != -1) ? from_bytes : 0); + bytes_to_r = ((end_item == split_item_num && end_bytes != -1) ? end_bytes : 0); + if (vn->vn_vi[split_item_num].vi_type & VI_TYPE_DIRECTORY) { + int entries_to_S2new; + + /* calculate number of entries fit into S2new */ + entries_to_S2new = vn->vn_vi[split_item_num].vi_entry_count - snum012[4] - bytes_to_r - bytes_to_l; + if (snum012[3] != -1 && snum012[1] == 1) { + /* directory split into 3 nodes */ + int entries_to_S1new; + + entries_to_S2new -= snum012[3]; + entries_to_S1new = snum012[4]; + snum012[3] = entries_to_S1new; + snum012[4] = entries_to_S2new; + return needed_nodes; + } + snum012[4] = entries_to_S2new; + } else { + /* item is not of directory type */ + int bytes_to_S2new; + + bytes_to_S2new = vn->vn_vi[split_item_num].vi_item_len - IH_SIZE - snum012[4] - bytes_to_r - bytes_to_l; + snum012[4] = bytes_to_S2new; + } + } + + /* now we know S2bytes, calculate S1bytes */ + if (snum012[3] > 0) { /* s1bytes */ + /* get number of item that is split between S0 and S1new */ + int split_item_num; + int bytes_to_r, bytes_to_l; + + split_item_num = splitted_item_positions[0]; + bytes_to_l = ((from == split_item_num && from_bytes != -1) ? from_bytes : 0); + bytes_to_r = ((end_item == split_item_num && end_bytes != -1) ? end_bytes : 0); + if (vn->vn_vi[split_item_num].vi_type & VI_TYPE_DIRECTORY) { + /* entries, who go to S1new node */ + snum012[3] = vn->vn_vi[split_item_num].vi_entry_count - snum012[3] - bytes_to_r - bytes_to_l; + } else + /* bytes, who go to S1new node (not including HI_SIZE) */ + snum012[3] = vn->vn_vi[split_item_num].vi_item_len - IH_SIZE - snum012[3] - bytes_to_r - bytes_to_l; + } + + return needed_nodes; +} + + +#ifdef CONFIG_REISERFS_CHECK +extern struct tree_balance * cur_tb; +#endif + + +/* size of item_num-th item in bytes when regular and in entries when + item is directory */ +static int item_length (struct tree_balance * tb, int item_num) +{ + struct virtual_node * vn = tb->tb_vn; + +#ifdef CONFIG_REISERFS_CHECK + if (item_num >= vn->vn_nr_item) + reiserfs_panic (tb->tb_sb, "vs-8120: item_length: invalid index of item: index = %d (item number = %d)", item_num, vn->vn_nr_item); +#endif + + if (vn->vn_vi[item_num].vi_type & VI_TYPE_DIRECTORY) + return vn->vn_vi[item_num].vi_entry_count; + + return vn->vn_vi[item_num].vi_item_len - IH_SIZE; +} + + +/* Set parameters for balancing. + * Performs write of results of analysis of balancing into structure tb, + * where it will later be used by the functions that actually do the balancing. + * Parameters: + * tb tree_balance structure; + * h current level of the node; + * lnum number of items from S[h] that must be shifted to L[h]; + * rnum number of items from S[h] that must be shifted to R[h]; + * blk_num number of blocks that S[h] will be splitted into; + * s012 number of items that fall into splitted nodes. + * lbytes number of bytes which flow to the left neighbor from the item that is not + * not shifted entirely + * rbytes number of bytes which flow to the right neighbor from the item that is not + * not shifted entirely + * s1bytes number of bytes which flow to the first new node when S[0] splits (this number is contained in s012 array) + */ + +static void set_parameters (struct tree_balance * tb, int h, int lnum, + int rnum, int blk_num, short * s012, int lb, int rb) +{ + + tb->lnum[h] = lnum; + tb->rnum[h] = rnum; + tb->blknum[h] = blk_num; + + if (h == 0) + { /* only for leaf level */ + if (s012 != NULL) + { + tb->s0num = * s012 ++, + tb->s1num = * s012 ++, + tb->s2num = * s012 ++; + tb->s1bytes = * s012 ++; + tb->s2bytes = * s012; + } + tb->lbytes = lb; + tb->rbytes = rb; + } +} + +static void decrement_key (struct key * p_s_key) +{ + int type; + + type = get_type (p_s_key); + switch (type) { + case TYPE_STAT_DATA: + p_s_key->k_objectid --; + set_type_and_offset (key_format (p_s_key), p_s_key, + (loff_t)MAX_FILE_SIZE_V2, TYPE_INDIRECT); + return; + + case TYPE_INDIRECT: + case TYPE_DIRECT: + case TYPE_DIRENTRY: + set_offset (key_format (p_s_key), p_s_key, get_offset (p_s_key) - 1); + if (get_offset (p_s_key) == 0) + set_type (key_format (p_s_key), p_s_key, TYPE_STAT_DATA); + return; + } + reiserfs_warning (stderr, "vs-8125: decrement_key: item of wrong type found %k", + p_s_key); +#if 0 + + unsigned long * p_n_key_field = (unsigned long *)p_s_key + REISERFS_FULL_KEY_LEN - 1; + int n_counter; + + + for( n_counter = 0; n_counter < REISERFS_FULL_KEY_LEN; n_counter++, p_n_key_field-- ) + if ( *p_n_key_field ) { + (*p_n_key_field)--; + break; + } + +#ifdef CONFIG_REISERFS_CHECK + if ( n_counter == REISERFS_FULL_KEY_LEN ) + reiserfs_panic(NULL, "PAP-8175: decrement_key: zero key"); +#endif + +#endif + +} + + +#ifdef FU //REISERFS_FSCK + +inline int is_left_mergeable (struct item_head * ih, unsigned long bsize) +{ + if (I_IS_DIRECT_ITEM (ih)) + return ((get_offset (&ih->ih_key) & (bsize - 1)) != 1); + + if (I_IS_INDIRECT_ITEM (ih)) + return (get_offset (&ih->ih_key) != 1); + + if (I_IS_DIRECTORY_ITEM (ih)) + return ((ih)->ih_key.u.k_offset_v1.k_offset != DOT_OFFSET); + +#ifdef CONFIG_REISERFS_CHECK + if ( ! I_IS_STAT_DATA_ITEM (ih)) + reiserfs_panic (0, "vs-16060: is_left_mergeable: item [%h] must be a stat data", ih); +#endif + + return 0; +} + +#else + +int are_items_mergeable (struct item_head * left, struct item_head * right, int bsize) +{ + if (comp_keys (&left->ih_key, &right->ih_key) != -1) { + reiserfs_panic (0, "vs-16070: are_items_mergeable: left %k, right %k", &(left->ih_key), &(right->ih_key)); + } + + if (not_of_one_file (&left->ih_key, &right->ih_key)) + return 0; + + if (I_IS_DIRECTORY_ITEM (left)) { + return 1; + } + + if ((I_IS_DIRECT_ITEM (left) && I_IS_DIRECT_ITEM (right)) || + (I_IS_INDIRECT_ITEM (left) && I_IS_INDIRECT_ITEM (right))) + return (get_offset (&left->ih_key) + get_bytes_number (left, bsize) == get_offset (&right->ih_key)) ? 1 : 0; + + return 0; +} + +/* get left neighbor of the leaf node */ +static struct buffer_head * get_left_neighbor (struct super_block * s, struct path * path) +{ + struct key key; + struct path path_to_left_neighbor; + struct buffer_head * bh; + int repeat; + + copy_key (&key, B_N_PKEY (PATH_PLAST_BUFFER (path), 0)); + decrement_key (&key); + + init_path (&path_to_left_neighbor); + search_by_key (s, &key, &path_to_left_neighbor, &repeat, DISK_LEAF_NODE_LEVEL); + if (PATH_LAST_POSITION (&path_to_left_neighbor) == 0) { + pathrelse (&path_to_left_neighbor); + return 0; + } + bh = PATH_PLAST_BUFFER (&path_to_left_neighbor); + bh->b_count ++; + pathrelse (&path_to_left_neighbor); + return bh; +} + +extern struct key MIN_KEY; +static struct buffer_head * get_right_neighbor (struct super_block * s, struct path * path) +{ + struct key key; + struct key * rkey; + int repeat; + struct path path_to_right_neighbor; + struct buffer_head * bh; + + rkey = get_rkey (path, s); + if (comp_keys (rkey, &MIN_KEY) == 0) + reiserfs_panic ("vs-16080: get_right_neighbor: get_rkey returned min key (path has changed)"); + copy_key (&key, rkey); + + + init_path (&path_to_right_neighbor); + search_by_key (s, &key, &path_to_right_neighbor, &repeat, DISK_LEAF_NODE_LEVEL); + if (PATH_PLAST_BUFFER (&path_to_right_neighbor) == PATH_PLAST_BUFFER (path)) { + pathrelse (&path_to_right_neighbor); + return 0; + } + bh = PATH_PLAST_BUFFER (&path_to_right_neighbor); + bh->b_count ++; + pathrelse (&path_to_right_neighbor); + return bh; +} + + +int is_left_mergeable (struct super_block * s, struct path * path) +{ + struct item_head * right; + struct buffer_head * bh; + int retval; + + right = B_N_PITEM_HEAD (PATH_PLAST_BUFFER (path), 0); + + bh = get_left_neighbor (s, path); + if (bh == 0) { + return 0; + } + retval = are_items_mergeable (B_N_PITEM_HEAD (bh, B_NR_ITEMS (bh) - 1), right, bh->b_size); + brelse (bh); + return retval; +} + + +int is_right_mergeable (struct super_block * s, struct path * path) +{ + struct item_head * left; + struct buffer_head * bh; + int retval; + + left = B_N_PITEM_HEAD (PATH_PLAST_BUFFER (path), B_NR_ITEMS (PATH_PLAST_BUFFER (path)) - 1); + + bh = get_right_neighbor (s, path); + if (bh == 0) { + return 0; + } + retval = are_items_mergeable (left, B_N_PITEM_HEAD (bh, 0), bh->b_size); + brelse (bh); + return retval; +} + +#endif /* REISERFS_FSCK */ + + + +/* check, does node disappear if we shift tb->lnum[0] items to left + neighbor and tb->rnum[0] to the right one. */ +static int is_leaf_removable (struct tree_balance * tb) +{ + struct virtual_node * vn = tb->tb_vn; + int to_left, to_right; + int size; + int remain_items; + + /* number of items, that will be shifted to left (right) neighbor + entirely */ + to_left = tb->lnum[0] - ((tb->lbytes != -1) ? 1 : 0); + to_right = tb->rnum[0] - ((tb->rbytes != -1) ? 1 : 0); + remain_items = vn->vn_nr_item; + + /* how many items remain in S[0] after shiftings to neighbors */ + remain_items -= (to_left + to_right); + + if (remain_items < 1) { + /* all content of node can be shifted to neighbors */ + set_parameters (tb, 0, to_left, vn->vn_nr_item - to_left, 0, NULL, -1, -1); + return 1; + } + + if (remain_items > 1 || tb->lbytes == -1 || tb->rbytes == -1) + /* S[0] is not removable */ + return 0; + + /* check, whether we can divide 1 remaining item between neighbors */ + + /* get size of remaining item (in directory entry count if directory) */ + size = item_length (tb, to_left); + + if (tb->lbytes + tb->rbytes >= size) { + set_parameters (tb, 0, to_left + 1, to_right + 1, 0, NULL, tb->lbytes, -1); + return 1; + } + + return 0; +} + + +/* check whether L, S, R can be joined in one node */ +static int are_leaves_removable (struct tree_balance * tb, int lfree, int rfree) +{ + struct virtual_node * vn = tb->tb_vn; + int ih_size; + struct buffer_head *S0; + + S0 = PATH_H_PBUFFER (tb->tb_path, 0); + + ih_size = 0; + if (vn->vn_nr_item) { + if (vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE) + ih_size += IH_SIZE; + + if (vn->vn_vi[vn->vn_nr_item-1].vi_type & VI_TYPE_RIGHT_MERGEABLE) + ih_size += IH_SIZE; + } else { + /* there was only one item and it will be deleted */ + struct item_head * ih; + +#ifdef CONFIG_REISERFS_CHECK + if (B_NR_ITEMS (S0) != 1) + reiserfs_panic (0, "vs-8125: are_leaves_removable: item number must be 1: it is %d", B_NR_ITEMS(S0)); +#endif + + ih = B_N_PITEM_HEAD (S0, 0); + if (tb->CFR[0] && !not_of_one_file (&(ih->ih_key), B_N_PDELIM_KEY (tb->CFR[0], tb->rkey[0]))) + if (I_IS_DIRECTORY_ITEM(ih)) { +#ifdef FU //REISERFS_FSCK + + /* Directory must be in correct state here: that is + somewhere at the left side should exist first + directory item. But the item being deleted can not + be that first one because its right neighbor is + item of the same directory. (But first item always + gets deleted in last turn). So, neighbors of + deleted item can be merged, so we can save ih_size */ + ih_size = IH_SIZE; + +#ifdef CONFIG_REISERFS_CHECK + /* we might check that left neighbor exists and is of + the same directory */ + if (get_offset (&ih->ih_key) == DOT_OFFSET) + reiserfs_panic (tb->tb_sb, "vs-8130: are_leaves_removable: " + "first directory item can not be removed until directory is not empty"); +#endif + + +#else /* REISERFS_FSCK */ + + /* we can delete any directory item in fsck (if it is unreachable) */ + if (get_offset (&ih->ih_key) != DOT_OFFSET) { + /* must get left neighbor here to make sure, that + left neighbor is of the same directory */ + struct buffer_head * left; + + left = get_left_neighbor (tb->tb_sb, tb->tb_path); + if (left) { + struct item_head * last; + + if (B_NR_ITEMS (left) == 0) + reiserfs_panic ("vs-8135: are_leaves_removable: " + "empty node in the tree"); + last = B_N_PITEM_HEAD (left, B_NR_ITEMS (left) - 1); + if (!comp_short_keys (&last->ih_key, &ih->ih_key)) + ih_size = IH_SIZE; + brelse (left); + } + } +#endif + } + + } + + if (MAX_CHILD_SIZE (S0) + vn->vn_size <= rfree + lfree + ih_size) { + set_parameters (tb, 0, -1, -1, -1, NULL, -1, -1); + return 1; + } + return 0; + +} + + + +/* when we do not split item, lnum and rnum are numbers of entire items */ +#define SET_PAR_SHIFT_LEFT \ +if (h)\ +{\ + int to_l;\ + \ + to_l = (MAX_NR_KEY(Sh)+1 - lpar + vn->vn_nr_item + 1) / 2 -\ + (MAX_NR_KEY(Sh) + 1 - lpar);\ + \ + set_parameters (tb, h, to_l, 0, lnver, NULL, -1, -1);\ +}\ +else \ +{\ + if (lset==LEFT_SHIFT_FLOW)\ + set_parameters (tb, h, lpar, 0, lnver, snum012+lset,\ + tb->lbytes, -1);\ + else\ + set_parameters (tb, h, lpar - (tb->lbytes!=-1), 0, lnver, snum012+lset,\ + -1, -1);\ +} + + +#define SET_PAR_SHIFT_RIGHT \ +if (h)\ +{\ + int to_r;\ + \ + to_r = (MAX_NR_KEY(Sh)+1 - rpar + vn->vn_nr_item + 1) / 2 - (MAX_NR_KEY(Sh) + 1 - rpar);\ + \ + set_parameters (tb, h, 0, to_r, rnver, NULL, -1, -1);\ +}\ +else \ +{\ + if (rset==RIGHT_SHIFT_FLOW)\ + set_parameters (tb, h, 0, rpar, rnver, snum012+rset,\ + -1, tb->rbytes);\ + else\ + set_parameters (tb, h, 0, rpar - (tb->rbytes!=-1), rnver, snum012+rset,\ + -1, -1);\ +} + +#if 0 +void free_buffers_in_tb ( + struct tree_balance * p_s_tb + ) { + int n_counter; + + decrement_counters_in_path(p_s_tb->tb_path); + + for ( n_counter = 0; n_counter < MAX_HEIGHT; n_counter++ ) { + decrement_bcount(p_s_tb->L[n_counter]); + p_s_tb->L[n_counter] = NULL; + decrement_bcount(p_s_tb->R[n_counter]); + p_s_tb->R[n_counter] = NULL; + decrement_bcount(p_s_tb->FL[n_counter]); + p_s_tb->FL[n_counter] = NULL; + decrement_bcount(p_s_tb->FR[n_counter]); + p_s_tb->FR[n_counter] = NULL; + decrement_bcount(p_s_tb->CFL[n_counter]); + p_s_tb->CFL[n_counter] = NULL; + decrement_bcount(p_s_tb->CFR[n_counter]); + p_s_tb->CFR[n_counter] = NULL; + } +} +#endif + + +/* Get new buffers for storing new nodes that are created while balancing. + * Returns: SCHEDULE_OCCURED - schedule occured while the function worked; + * CARRY_ON - schedule didn't occur while the function worked; + * NO_DISK_SPACE - no disk space. + */ +static int get_empty_nodes (struct tree_balance * p_s_tb, + int n_h) +{ + struct buffer_head * p_s_new_bh, + * p_s_Sh = PATH_H_PBUFFER (p_s_tb->tb_path, n_h); + unsigned long * p_n_blocknr, + a_n_blocknrs[MAX_AMOUNT_NEEDED] = {0, }; + int n_counter, + n_number_of_freeblk, + n_amount_needed,/* number of needed empty blocks */ + n_repeat1, + n_repeat; + struct super_block * p_s_sb = p_s_tb->tb_sb; + + + //#ifndef FU //REISERFS_FSCK + if (n_h == 0 && p_s_tb->insert_size[n_h] == 0x7fff) + return CARRY_ON; + //#endif + + /* number_of_freeblk is the number of empty blocks which have been + acquired for use by the balancing algorithm minus the number of + empty blocks used in the previous levels of the analysis, + number_of_freeblk = tb->cur_blknum can be non-zero if a + schedule occurs after empty blocks are acquired, and the + balancing analysis is then restarted, amount_needed is the + number needed by this level (n_h) of the balancing analysis. + + Note that for systems with many processes writing, it would be + more layout optimal to calculate the total number needed by all + levels and then to run reiserfs_new_blocks to get all of them + at once. */ + + /* Initiate number_of_freeblk to the amount acquired prior to the restart of + the analysis or 0 if not restarted, then subtract the amount needed + by all of the levels of the tree below n_h. */ + /* blknum includes S[n_h], so we subtract 1 in this calculation */ + for ( n_counter = 0, n_number_of_freeblk = p_s_tb->cur_blknum; n_counter < n_h; n_counter++ ) + n_number_of_freeblk -= ( p_s_tb->blknum[n_counter] ) ? (p_s_tb->blknum[n_counter] - 1) : 0; + + /* Allocate missing empty blocks. */ + /* if p_s_Sh == 0 then we are getting a new root */ + n_amount_needed = ( p_s_Sh ) ? (p_s_tb->blknum[n_h] - 1) : 1; + /* Amount_needed = the amount that we need more than the amount that we have. */ + if ( n_amount_needed > n_number_of_freeblk ) + n_amount_needed -= n_number_of_freeblk; + else /* If we have enough already then there is nothing to do. */ + return CARRY_ON; + + if ( (n_repeat = reiserfs_new_blocknrs (p_s_tb->tb_sb, a_n_blocknrs, + PATH_PLAST_BUFFER(p_s_tb->tb_path)->b_blocknr, n_amount_needed)) != CARRY_ON ) { + return n_repeat; /* Out of disk space or schedule() occured. */ + } + + + /* for each blocknumber we just got, get a buffer and stick it on FEB */ + for ( p_n_blocknr = a_n_blocknrs, n_counter = 0; n_counter < n_amount_needed; + p_n_blocknr++, n_counter++ ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! *p_n_blocknr ) + reiserfs_panic(p_s_sb, "PAP-8135: get_empty_nodes: reiserfs_new_blocknrs failed when got new blocks"); +#endif + + n_repeat1 = CARRY_ON; + p_s_new_bh = reiserfs_getblk(p_s_sb->s_dev, *p_n_blocknr, p_s_sb->s_blocksize, &n_repeat1); + n_repeat |= n_repeat1; + if (p_s_new_bh->b_count > 1) { + die ("get_empty_nodes: not free empty buffer"); + } +#ifdef CONFIG_REISERFS_CHECK_NOCHECK + if ((p_s_new_bh->b_count != 1 && !buffer_journaled(p_s_new_bh)) + || (buffer_dirty (p_s_new_bh) && !buffer_journal_dirty(p_s_new_bh))) { + reiserfs_panic(p_s_sb,"PAP-8140: get_empty_nodes: not free or dirty buffer %b for the new block", + p_s_new_bh); + } +#endif + //mark_buffer_journal_new(p_s_new_bh) ; + + /* Put empty buffers into the array. */ + p_s_tb->FEB[p_s_tb->cur_blknum++] = p_s_new_bh; + } + + return n_repeat; +} + + +/* Get free space of the left neighbor, + * which is stored in the parent node of the left neighbor. + */ +static int get_lfree (struct tree_balance * tb, int h) +{ + struct buffer_head * l, * f; + int order; + + if ((f = PATH_H_PPARENT (tb->tb_path, h)) == 0 || (l = tb->FL[h]) == 0) + return 0; + + if (f == l) + order = PATH_H_B_ITEM_ORDER (tb->tb_path, h) - 1; + else { + order = B_BLK_HEAD(l)->blk_nr_item; + f = l; + } + + if (B_N_CHILD(f,order)->dc_size == 0) { + reiserfs_warning (stderr, "get_lfree: block %u block_head %z has bad child pointer %y, order %d\n", + l->b_blocknr, l, B_N_CHILD(f,order), order); + } + return (MAX_CHILD_SIZE(f) - B_N_CHILD(f,order)->dc_size); +} + + +/* Get free space of the right neighbor, + * which is stored in the parent node of the right neighbor. + */ +static int get_rfree (struct tree_balance * tb, int h) +{ + struct buffer_head * r, * f; + int order; + + if ((f = PATH_H_PPARENT (tb->tb_path, h)) == 0 || (r = tb->FR[h]) == 0) + return 0; + + if (f == r) + order = PATH_H_B_ITEM_ORDER (tb->tb_path, h) + 1; + else { + order = 0; + f = r; + } + + return (MAX_CHILD_SIZE(f) - B_N_CHILD(f,order)->dc_size); + +} + + +/* Check whether left neighbor is in memory. */ +static int is_left_neighbor_in_cache( + struct tree_balance * p_s_tb, + int n_h + ) { + struct buffer_head * p_s_father; + struct super_block * p_s_sb = p_s_tb->tb_sb; + unsigned long n_left_neighbor_blocknr; + int n_left_neighbor_position; + + if ( ! p_s_tb->FL[n_h] ) /* Father of the left neighbor does not exist. */ + return 0; + + /* Calculate father of the node to be balanced. */ + p_s_father = PATH_H_PBUFFER(p_s_tb->tb_path, n_h + 1); + +#ifdef CONFIG_REISERFS_CHECK + if ( ! p_s_father || ! B_IS_IN_TREE (p_s_father) || ! B_IS_IN_TREE (p_s_tb->FL[n_h]) || + ! buffer_uptodate (p_s_father) || ! buffer_uptodate (p_s_tb->FL[n_h]) ) { + reiserfs_panic (p_s_sb, "vs-8165: is_left_neighbor_in_cache: F[h] (%b) or FL[h] (%b) is invalid", + p_s_father, p_s_tb->FL[n_h]); + } +#endif + + + /* Get position of the pointer to the left neighbor into the left father. */ + n_left_neighbor_position = ( p_s_father == p_s_tb->FL[n_h] ) ? + p_s_tb->lkey[n_h] : B_BLK_HEAD(p_s_tb->FL[n_h])->blk_nr_item; + /* Get left neighbor block number. */ + n_left_neighbor_blocknr = B_N_CHILD_NUM(p_s_tb->FL[n_h], n_left_neighbor_position); + /* Look for the left neighbor in the cache. */ + if ( (p_s_father = find_buffer(p_s_sb->s_dev, n_left_neighbor_blocknr, p_s_sb->s_blocksize)) ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( buffer_uptodate (p_s_father) && ! B_IS_IN_TREE(p_s_father) ) { + reiserfs_panic(p_s_sb, "vs-8170: is_left_neighbor_in_cache: left neighbor (%b %z) is not in the tree", + p_s_father, p_s_father); + } +#endif + + return 1; + } + + return 0; +} + + +#define LEFT_PARENTS 'l' +#define RIGHT_PARENTS 'r' + + + + +void init_path (struct path * path) +{ + path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET; +} + + +/* Calculate far left/right parent of the left/right neighbor of the current node, that + * is calculate the left/right (FL[h]/FR[h]) neighbor of the parent F[h]. + * Calculate left/right common parent of the current node and L[h]/R[h]. + * Calculate left/right delimiting key position. + * Returns: PATH_INCORRECT - path in the tree is not correct; + SCHEDULE_OCCURRED - schedule occured while the function worked; + * CARRY_ON - schedule didn't occur while the function worked; + */ +static int get_far_parent( + struct tree_balance * p_s_tb, + int n_h, + struct buffer_head ** pp_s_father, + struct buffer_head ** pp_s_com_father, + char c_lr_par + ) { + struct buffer_head * p_s_parent; + struct path s_path_to_neighbor_father, + * p_s_path = p_s_tb->tb_path; + struct key s_lr_father_key; + int n_counter, + n_position = INT_MAX, + n_repeat, + n_first_last_position = 0, + n_path_offset = PATH_H_PATH_OFFSET(p_s_path, n_h); + + /* Starting from F[n_h] go upwards in the tree, and look for the common + ancestor of F[n_h], and its neighbor l/r, that should be obtained. */ + + n_counter = n_path_offset; + +#ifdef CONFIG_REISERFS_CHECK + if ( n_counter < FIRST_PATH_ELEMENT_OFFSET ) + reiserfs_panic(p_s_tb->tb_sb, "PAP-8180: get_far_parent: invalid path length"); +#endif + + + for ( ; n_counter > FIRST_PATH_ELEMENT_OFFSET; n_counter-- ) { + /* Check whether parent of the current buffer in the path is really parent in the tree. */ + if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_path, n_counter - 1)) ) + return PATH_INCORRECT; + /* Check whether position in the parent is correct. */ + if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_counter - 1)) > B_NR_ITEMS(p_s_parent) ) + return PATH_INCORRECT; + /* Check whether parent at the path really points to the child. */ + if ( B_N_CHILD_NUM(p_s_parent, n_position) != + PATH_OFFSET_PBUFFER(p_s_path, n_counter)->b_blocknr ) + return PATH_INCORRECT; + /* Return delimiting key if position in the parent is not equal to first/last one. */ + if ( c_lr_par == RIGHT_PARENTS ) + n_first_last_position = B_BLK_HEAD(p_s_parent)->blk_nr_item; + if ( n_position != n_first_last_position ) { + (*pp_s_com_father = p_s_parent)->b_count++; + break; + } + } + + /* Hopefully we are in the root of the tree. */ + if ( n_counter == FIRST_PATH_ELEMENT_OFFSET ) { + /* Check whether first buffer in the path is the root of the tree. */ + if ( PATH_OFFSET_PBUFFER(p_s_tb->tb_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr == + SB_ROOT_BLOCK (p_s_tb->tb_sb) ) { + *pp_s_father = *pp_s_com_father = NULL; + return CARRY_ON; + } + return PATH_INCORRECT; + } + +#ifdef CONFIG_REISERFS_CHECK + if ( B_BLK_HEAD(*pp_s_com_father)->blk_level <= DISK_LEAF_NODE_LEVEL ) { + reiserfs_panic(p_s_tb->tb_sb, "PAP-8185: get_far_parent: (%b %z) level too small", *pp_s_com_father, *pp_s_com_father); + } +#endif + + /* Check whether the common parent is locked. */ +#if 0 + if ( test_and_wait_on_buffer(*pp_s_com_father) == SCHEDULE_OCCURRED ) { + decrement_bcount(*pp_s_com_father); + return SCHEDULE_OCCURRED; /* schedule() occured */ + } +#endif + + /* So, we got common parent of the current node and its left/right neighbor. + Now we are geting the parent of the left/right neighbor. */ + + /* Form key to get parent of the left/right neighbor. */ + copy_key(&s_lr_father_key, B_N_PDELIM_KEY(*pp_s_com_father, ( c_lr_par == LEFT_PARENTS ) ? + (p_s_tb->lkey[n_h - 1] = n_position - 1) : (p_s_tb->rkey[n_h - 1] = n_position))); + + if ( c_lr_par == LEFT_PARENTS ) { + //reiserfs_warning ("decrememnting key %k\n", &s_lr_father_key); + decrement_key(&s_lr_father_key); + //reiserfs_warning ("done: %k\n", &s_lr_father_key); + } + + init_path (&s_path_to_neighbor_father); + + if (search_by_key(p_s_tb->tb_sb, &s_lr_father_key, &s_path_to_neighbor_father, &n_repeat, n_h + 1) == IO_ERROR) + return IO_ERROR; + + if ( n_repeat != CARRY_ON ) { + pathrelse (&s_path_to_neighbor_father); + //decrement_counters_in_path(&s_path_to_neighbor_father); + brelse (*pp_s_com_father); + //decrement_bcount(*pp_s_com_father); + return n_repeat; + } + + *pp_s_father = PATH_PLAST_BUFFER(&s_path_to_neighbor_father); + +#ifdef CONFIG_REISERFS_CHECK + if ( B_BLK_HEAD(*pp_s_father)->blk_level != n_h + 1 ) { + reiserfs_panic(p_s_tb->tb_sb, "PAP-8190: get_far_parent: (%b %z) level too small", *pp_s_father, *pp_s_father); + } + + if ( s_path_to_neighbor_father.path_length < FIRST_PATH_ELEMENT_OFFSET ) + reiserfs_panic(0, "PAP-8192: get_far_parent: path length is too small"); + +#endif + + s_path_to_neighbor_father.path_length--; + pathrelse (&s_path_to_neighbor_father); + //decrement_counters_in_path(&s_path_to_neighbor_father); + return CARRY_ON; +} + + +/* Get parents of neighbors of node in the path(S[n_path_offset]) and common parents of + * S[n_path_offset] and L[n_path_offset]/R[n_path_offset]: F[n_path_offset], FL[n_path_offset], + * FR[n_path_offset], CFL[n_path_offset], CFR[n_path_offset]. + * Calculate numbers of left and right delimiting keys position: lkey[n_path_offset], rkey[n_path_offset]. + * Returns: SCHEDULE_OCCURRED - schedule occured while the function worked; + * CARRY_ON - schedule didn't occur while the function worked; + */ +static int get_parents (struct tree_balance * p_s_tb, int n_h) +{ + struct path * p_s_path = p_s_tb->tb_path; + int n_position, + n_ret_value, + n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h); + struct buffer_head * p_s_curf, + * p_s_curcf; + + /* Current node is the root of the tree or will be root of the tree */ + if ( n_path_offset <= FIRST_PATH_ELEMENT_OFFSET ) { + /* The root can not have parents. + Release nodes which previously were obtained as parents of the current node neighbors. */ + brelse(p_s_tb->FL[n_h]); + brelse(p_s_tb->CFL[n_h]); + brelse(p_s_tb->FR[n_h]); + brelse(p_s_tb->CFR[n_h]); + //decrement_bcount(p_s_tb->FL[n_h]); + //decrement_bcount(p_s_tb->CFL[n_h]); + //decrement_bcount(p_s_tb->FR[n_h]); + //decrement_bcount(p_s_tb->CFR[n_h]); + p_s_tb->FL[n_h] = p_s_tb->CFL[n_h] = p_s_tb->FR[n_h] = p_s_tb->CFR[n_h] = NULL; + return CARRY_ON; + } + + /* Get parent FL[n_path_offset] of L[n_path_offset]. */ + if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1)) ) { + /* Current node is not the first child of its parent. */ + (p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1))->b_count += 2; + p_s_tb->lkey[n_h] = n_position - 1; + } + else { + /* Calculate current parent of L[n_path_offset], which is the left neighbor of the current node. + Calculate current common parent of L[n_path_offset] and the current node. Note that + CFL[n_path_offset] not equal FL[n_path_offset] and CFL[n_path_offset] not equal F[n_path_offset]. + Calculate lkey[n_path_offset]. */ + if ( (n_ret_value = get_far_parent(p_s_tb, n_h + 1, &p_s_curf, + &p_s_curcf, LEFT_PARENTS)) != CARRY_ON ) + return n_ret_value; /*schedule() occured or path is not correct*/ +#ifdef CONFIG_REISERFS_CHECK + if (p_s_curf == PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1)) { + reiserfs_panic (p_s_tb->tb_sb, "vs-8194: get_parents: " + "get_far_parent fails"); + } +#endif + + } + + brelse(p_s_tb->FL[n_h]); + p_s_tb->FL[n_h] = p_s_curf; /* New initialization of FL[n_h]. */ + + brelse(p_s_tb->CFL[n_h]); + p_s_tb->CFL[n_h] = p_s_curcf; /* New initialization of CFL[n_h]. */ + +#ifdef CONFIG_REISERFS_CHECK + if ((p_s_curf && !B_IS_IN_TREE (p_s_curf)) || (p_s_curcf && !B_IS_IN_TREE (p_s_curcf))) { + reiserfs_panic (p_s_tb->tb_sb, "PAP-8195: get_parents: FL (%b) or CFL (%b) is invalid", p_s_curf, p_s_curcf); + } +#endif + +/* Get parent FR[n_h] of R[n_h]. */ + +/* Current node is the last child of F[n_h]. FR[n_h] != F[n_h]. */ + if ( n_position == B_BLK_HEAD(PATH_H_PBUFFER(p_s_path, n_h + 1))->blk_nr_item ) { +/* Calculate current parent of R[n_h], which is the right neighbor of F[n_h]. + Calculate current common parent of R[n_h] and current node. Note that CFR[n_h] + not equal FR[n_path_offset] and CFR[n_h] not equal F[n_h]. */ + if ( (n_ret_value = get_far_parent(p_s_tb, n_h + 1, &p_s_curf, &p_s_curcf, RIGHT_PARENTS)) != CARRY_ON ) + return n_ret_value; /*schedule() occured while get_far_parent() worked.*/ + } + else { +/* Current node is not the last child of its parent F[n_h]. */ + (p_s_curf = p_s_curcf = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1))->b_count += 2; + p_s_tb->rkey[n_h] = n_position; + } + + brelse/*decrement_bcount*/(p_s_tb->FR[n_h]); + p_s_tb->FR[n_h] = p_s_curf; /* New initialization of FR[n_path_offset]. */ + + brelse/*decrement_bcount*/(p_s_tb->CFR[n_h]); + p_s_tb->CFR[n_h] = p_s_curcf; /* New initialization of CFR[n_path_offset]. */ + +#ifdef CONFIG_REISERFS_CHECK + if ((p_s_curf && !B_IS_IN_TREE (p_s_curf)) || (p_s_curcf && !B_IS_IN_TREE (p_s_curcf))) { + reiserfs_panic (p_s_tb->tb_sb, "PAP-8205: get_parents: FR (%b) or CFR (%b) is invalid", p_s_curf, p_s_curcf); + } +#endif + + return CARRY_ON; /* schedule not occured while get_parents() worked. */ +} + + +/* it is possible to remove node as result of shiftings to + neighbors even when we insert or paste item. */ +static inline int can_node_be_removed (int mode, int lfree, int sfree, int rfree, struct tree_balance * tb, int h) +{ + struct buffer_head * Sh = PATH_H_PBUFFER (tb->tb_path, h); + int levbytes = tb->insert_size[h]; + struct item_head * ih; + struct item_head * r_ih = NULL; + + ih = B_N_PITEM_HEAD (Sh, 0); + if ( tb->CFR[h] ) + r_ih = (struct item_head *)B_N_PDELIM_KEY(tb->CFR[h],tb->rkey[h]); + + if ( + lfree + rfree + sfree < MAX_CHILD_SIZE(Sh) + levbytes + /* shifting may merge items which might save space */ +#ifndef FU //REISERFS_FSCK + - (( ! h && is_left_mergeable (tb->tb_sb, tb->tb_path) == 1 ) ? IH_SIZE : 0) + - (( ! h && r_ih && is_right_mergeable (tb->tb_sb, tb->tb_path) == 1 ) ? IH_SIZE : 0) +#else + - (( ! h && is_left_mergeable (ih, Sh->b_size) ) ? IH_SIZE : 0) + - (( ! h && r_ih && is_left_mergeable (r_ih, Sh->b_size) ) ? IH_SIZE : 0) +#endif + + (( h ) ? KEY_SIZE : 0)) + { + /* node can not be removed */ + if (sfree >= levbytes ) /* new item fits into node S[h] without any shifting */ + { + if ( ! h ) + tb->s0num = B_NR_ITEMS(Sh) + ((mode == M_INSERT ) ? 1 : 0); + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; + } + } + return !NO_BALANCING_NEEDED; +} + + + +/* Check whether current node S[h] is balanced when increasing its size by + * Inserting or Pasting. + * Calculate parameters for balancing for current level h. + * Parameters: + * tb tree_balance structure; + * h current level of the node; + * inum item number in S[h]; + * mode i - insert, p - paste; + * Returns: 1 - schedule occured; + * 0 - balancing for higher levels needed; + * -1 - no balancing for higher levels needed; + * -2 - no disk space. + */ +/* ip means Inserting or Pasting */ +static int ip_check_balance (/*struct reiserfs_transaction_handle *th,*/ struct tree_balance * tb, int h) +{ + struct virtual_node * vn = tb->tb_vn; + int levbytes, /* Number of bytes that must be inserted into (value + is negative if bytes are deleted) buffer which + contains node being balanced. The mnemonic is + that the attempted change in node space used level + is levbytes bytes. */ + n_ret_value; + + int lfree, sfree, rfree /* free space in L, S and R */; + + /* nver is short for number of vertixes, and lnver is the number if + we shift to the left, rnver is the number if we shift to the + right, and lrnver is the number if we shift in both directions. + The goal is to minimize first the number of vertixes, and second, + the number of vertixes whose contents are changed by shifting, + and third the number of uncached vertixes whose contents are + changed by shifting and must be read from disk. */ + int nver, lnver, rnver, lrnver; + + /* used at leaf level only, S0 = S[0] is the node being balanced, + sInum [ I = 0,1,2 ] is the number of items that will + remain in node SI after balancing. S1 and S2 are new + nodes that might be created. */ + + /* we perform 8 calls to get_num_ver(). For each call we calculate five parameters. + where 4th parameter is s1bytes and 5th - s2bytes + */ + short snum012[40] = {0,}; /* s0num, s1num, s2num for 8 cases + 0,1 - do not shift and do not shift but bottle + 2 - shift only whole item to left + 3 - shift to left and bottle as much as possible + 4,5 - shift to right (whole items and as much as possible + 6,7 - shift to both directions (whole items and as much as possible) + */ + + /* Sh is the node whose balance is currently being checked */ + struct buffer_head * Sh; + +#ifndef FU //REISERFS_FSCK + /* special mode for insert pointer to the most low internal node */ + if (h == 0 && vn->vn_mode == M_INTERNAL) { + /* blk_num == 2 is to get pointer inserted to the next level */ + set_parameters (tb, h, 0, 0, 2, NULL, -1, -1); + return 0; + } +#endif + + Sh = PATH_H_PBUFFER (tb->tb_path, h); + levbytes = tb->insert_size[h]; + + /* Calculate balance parameters for creating new root. */ + if ( ! Sh ) { + if ( ! h ) + reiserfs_panic ("vs-8210: ip_check_balance: S[0] can not be 0"); + switch ( n_ret_value = get_empty_nodes (tb, h) ) { + case CARRY_ON: + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; /* no balancing for higher levels needed */ + + case NO_DISK_SPACE: + case SCHEDULE_OCCURRED: + return n_ret_value; + default: + reiserfs_panic("vs-8215: ip_check_balance: incorrect return value of get_empty_nodes"); + } + } + + if ( (n_ret_value = get_parents (tb, h)) != CARRY_ON ) /* get parents of S[h] neighbors. */ + return n_ret_value; + + sfree = B_BLK_HEAD(Sh)->blk_free_space; + + /* get free space of neighbors */ + rfree = get_rfree (tb, h); + lfree = get_lfree (tb, h); + + if (can_node_be_removed (vn->vn_mode, lfree, sfree, rfree, tb, h) == NO_BALANCING_NEEDED) + /* and new item fits into node S[h] without any shifting */ + return NO_BALANCING_NEEDED; + + create_virtual_node (tb, h); + + /* + determine maximal number of items we can shift to the left neighbor (in tb structure) + and the maximal number of bytes that can flow to the left neighbor + from the left most liquid item that cannot be shifted from S[0] entirely (returned value) + */ + check_left (tb, h, lfree); + + /* + determine maximal number of items we can shift to the right neighbor (in tb structure) + and the maximal number of bytes that can flow to the right neighbor + from the right most liquid item that cannot be shifted from S[0] entirely (returned value) + */ + check_right (tb, h, rfree); + + + /* all contents of internal node S[h] can be moved into its + neighbors, S[h] will be removed after balancing */ + if (h && (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1)) { + int to_r; + + /* Since we are working on internal nodes, and our internal + nodes have fixed size entries, then we can balance by the + number of items rather than the space they consume. In this + routine we set the left node equal to the right node, + allowing a difference of less than or equal to 1 child + pointer. */ + to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 - + (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]); + set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1); + return CARRY_ON; + } + +#ifdef CONFIG_REISERFS_CHECK + /* this checks balance condition, that any two neighboring nodes can not fit in one node */ + if ( h && ( tb->lnum[h] >= vn->vn_nr_item + 1 || tb->rnum[h] >= vn->vn_nr_item + 1) ) + reiserfs_panic (tb->tb_sb, "vs-8220: ip_check_balance: tree is not balanced on internal level"); + + if ( ! h && ((tb->lnum[h] >= vn->vn_nr_item && (tb->lbytes == -1)) || + (tb->rnum[h] >= vn->vn_nr_item && (tb->rbytes == -1)) )) + reiserfs_panic(tb->tb_sb, "vs-8225: ip_check_balance: tree is not balanced on leaf level"); +#endif + + /* all contents of S[0] can be moved into its neighbors + S[0] will be removed after balancing. */ + if (!h && is_leaf_removable (tb)) + return CARRY_ON; + + + /* why do we perform this check here rather than earlier?? + Answer: we can win 1 node in some cases above. Moreover we + checked it above, when we checked, that S[0] is not removable + in principle */ + if (sfree >= levbytes) { /* new item fits into node S[h] without any shifting */ + if ( ! h ) + tb->s0num = vn->vn_nr_item; + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; + } + + + { + int lpar, rpar, nset, lset, rset, lrset; + /* + * regular overflowing of the node + */ + + /* get_num_ver works in 2 modes (FLOW & NO_FLOW) + lpar, rpar - number of items we can shift to left/right neighbor (including splitting item) + nset, lset, rset, lrset - shows, whether flowing items give better packing + */ +#define FLOW 1 +#define NO_FLOW 0 /* do not any splitting */ + + /* we choose one the following */ +#define NOTHING_SHIFT_NO_FLOW 0 +#define NOTHING_SHIFT_FLOW 5 +#define LEFT_SHIFT_NO_FLOW 10 +#define LEFT_SHIFT_FLOW 15 +#define RIGHT_SHIFT_NO_FLOW 20 +#define RIGHT_SHIFT_FLOW 25 +#define LR_SHIFT_NO_FLOW 30 +#define LR_SHIFT_FLOW 35 + + + lpar = tb->lnum[h]; + rpar = tb->rnum[h]; + + + /* calculate number of blocks S[h] must be split into when + nothing is shifted to the neighbors, + as well as number of items in each part of the split node (s012 numbers), + and number of bytes (s1bytes) of the shared drop which flow to S1 if any */ + nset = NOTHING_SHIFT_NO_FLOW; + nver = get_num_ver (vn->vn_mode, tb, h, + 0, -1, h?vn->vn_nr_item:0, -1, + snum012, NO_FLOW); + + if (!h) + { + int nver1; + + /* note, that in this case we try to bottle between S[0] and S1 (S1 - the first new node) */ + nver1 = get_num_ver (vn->vn_mode, tb, h, + 0, -1, 0, -1, + snum012 + NOTHING_SHIFT_FLOW, FLOW); + if (nver > nver1) + nset = NOTHING_SHIFT_FLOW, nver = nver1; + } + + + /* calculate number of blocks S[h] must be split into when + l_shift_num first items and l_shift_bytes of the right most + liquid item to be shifted are shifted to the left neighbor, + as well as number of items in each part of the splitted node (s012 numbers), + and number of bytes (s1bytes) of the shared drop which flow to S1 if any + */ + lset = LEFT_SHIFT_NO_FLOW; + lnver = get_num_ver (vn->vn_mode, tb, h, + lpar - (( h || tb->lbytes == -1 ) ? 0 : 1), -1, h ? vn->vn_nr_item:0, -1, + snum012 + LEFT_SHIFT_NO_FLOW, NO_FLOW); + if (!h) + { + int lnver1; + + lnver1 = get_num_ver (vn->vn_mode, tb, h, + lpar - ((tb->lbytes != -1) ? 1 : 0), tb->lbytes, 0, -1, + snum012 + LEFT_SHIFT_FLOW, FLOW); + if (lnver > lnver1) + lset = LEFT_SHIFT_FLOW, lnver = lnver1; + } + + + /* calculate number of blocks S[h] must be split into when + r_shift_num first items and r_shift_bytes of the left most + liquid item to be shifted are shifted to the right neighbor, + as well as number of items in each part of the splitted node (s012 numbers), + and number of bytes (s1bytes) of the shared drop which flow to S1 if any + */ + rset = RIGHT_SHIFT_NO_FLOW; + rnver = get_num_ver (vn->vn_mode, tb, h, + 0, -1, h ? (vn->vn_nr_item-rpar) : (rpar - (( tb->rbytes != -1 ) ? 1 : 0)), -1, + snum012 + RIGHT_SHIFT_NO_FLOW, NO_FLOW); + if (!h) + { + int rnver1; + + rnver1 = get_num_ver (vn->vn_mode, tb, h, + 0, -1, (rpar - ((tb->rbytes != -1) ? 1 : 0)), tb->rbytes, + snum012 + RIGHT_SHIFT_FLOW, FLOW); + + if (rnver > rnver1) + rset = RIGHT_SHIFT_FLOW, rnver = rnver1; + } + + + /* calculate number of blocks S[h] must be split into when + items are shifted in both directions, + as well as number of items in each part of the splitted node (s012 numbers), + and number of bytes (s1bytes) of the shared drop which flow to S1 if any + */ + lrset = LR_SHIFT_NO_FLOW; + lrnver = get_num_ver (vn->vn_mode, tb, h, + lpar - ((h || tb->lbytes == -1) ? 0 : 1), -1, h ? (vn->vn_nr_item-rpar):(rpar - ((tb->rbytes != -1) ? 1 : 0)), -1, + snum012 + LR_SHIFT_NO_FLOW, NO_FLOW); + if (!h) + { + int lrnver1; + + lrnver1 = get_num_ver (vn->vn_mode, tb, h, + lpar - ((tb->lbytes != -1) ? 1 : 0), tb->lbytes, (rpar - ((tb->rbytes != -1) ? 1 : 0)), tb->rbytes, + snum012 + LR_SHIFT_FLOW, FLOW); + if (lrnver > lrnver1) + lrset = LR_SHIFT_FLOW, lrnver = lrnver1; + } + + + + /* Our general shifting strategy is: + 1) to minimized number of new nodes; + 2) to minimized number of neighbors involved in shifting; + 3) to minimized number of disk reads; */ + + /* we can win TWO or ONE nodes by shifting in both directions */ + if (lrnver < lnver && lrnver < rnver) + { +#ifdef CONFIG_REISERFS_CHECK + if (h && (tb->lnum[h] != 1 || tb->rnum[h] != 1 || lrnver != 1 || rnver != 2 || lnver != 2 || h != 1)) + reiserfs_panic (0, "vs-8230: check_balance: bad h"); +#endif + if (lrset == LR_SHIFT_FLOW) + set_parameters (tb, h, tb->lnum[h], tb->rnum[h], lrnver, snum012 + lrset, + tb->lbytes, tb->rbytes); + else + set_parameters (tb, h, tb->lnum[h] - ((tb->lbytes == -1) ? 0 : 1), + tb->rnum[h] - ((tb->rbytes == -1) ? 0 : 1), lrnver, snum012 + lrset, -1, -1); + + return CARRY_ON; + } + + /* if shifting doesn't lead to better packing then don't shift */ + if (nver == lrnver) + { + set_parameters (tb, h, 0, 0, nver, snum012 + nset, -1, -1); + return CARRY_ON; + } + + + /* now we know that for better packing shifting in only one + direction either to the left or to the right is required */ + + /* if shifting to the left is better than shifting to the right */ + if (lnver < rnver) + { + SET_PAR_SHIFT_LEFT; + return CARRY_ON; + } + + /* if shifting to the right is better than shifting to the left */ + if (lnver > rnver) + { + SET_PAR_SHIFT_RIGHT; + return CARRY_ON; + } + + + /* now shifting in either direction gives the same number + of nodes and we can make use of the cached neighbors */ + if (is_left_neighbor_in_cache (tb,h)) + { + SET_PAR_SHIFT_LEFT; + return CARRY_ON; + } + + /* shift to the right independently on whether the right neighbor in cache or not */ + SET_PAR_SHIFT_RIGHT; + return CARRY_ON; + } +} + + +/* Check whether current node S[h] is balanced when Decreasing its size by + * Deleting or Cutting for INTERNAL node of S+tree. + * Calculate parameters for balancing for current level h. + * Parameters: + * tb tree_balance structure; + * h current level of the node; + * inum item number in S[h]; + * mode i - insert, p - paste; + * Returns: 1 - schedule occured; + * 0 - balancing for higher levels needed; + * -1 - no balancing for higher levels needed; + * -2 - no disk space. + * + * Note: Items of internal nodes have fixed size, so the balance condition for + * the internal part of S+tree is as for the B-trees. + */ +static int dc_check_balance_internal (struct tree_balance * tb, int h) +{ + struct virtual_node * vn = tb->tb_vn; + + /* Sh is the node whose balance is currently being checked, + and Fh is its father. */ + struct buffer_head * Sh, * Fh; + int maxsize, + n_ret_value; + int lfree, rfree /* free space in L and R */; + + Sh = PATH_H_PBUFFER (tb->tb_path, h); + Fh = PATH_H_PPARENT (tb->tb_path, h); + + maxsize = MAX_CHILD_SIZE(Sh); + +/* using tb->insert_size[h], which is negative in this case, create_virtual_node calculates: */ +/* new_nr_item = number of items node would have if operation is */ +/* performed without balancing (new_nr_item); */ + create_virtual_node (tb, h); + + if ( ! Fh ) + { /* S[h] is the root. */ + if ( vn->vn_nr_item > 0 ) + { + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; /* no balancing for higher levels needed */ + } + /* new_nr_item == 0. + * Current root will be deleted resulting in + * decrementing the tree height. */ + set_parameters (tb, h, 0, 0, 0, NULL, -1, -1); + return CARRY_ON; + } + + if ( (n_ret_value = get_parents(tb,h)) != CARRY_ON ) + return n_ret_value; + + + /* get free space of neighbors */ + rfree = get_rfree (tb, h); + lfree = get_lfree (tb, h); + + /* determine maximal number of items we can fit into neighbors */ + check_left (tb, h, lfree); + check_right (tb, h, rfree); + + + if ( vn->vn_nr_item >= MIN_NR_KEY(Sh) ) + { /* Balance condition for the internal node is valid. + * In this case we balance only if it leads to better packing. */ + if ( vn->vn_nr_item == MIN_NR_KEY(Sh) ) + { /* Here we join S[h] with one of its neighbors, + * which is impossible with greater values of new_nr_item. */ + if ( tb->lnum[h] >= vn->vn_nr_item + 1 ) + { + /* All contents of S[h] can be moved to L[h]. */ + int n; + int order_L; + + order_L = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==0) ? B_NR_ITEMS(tb->FL[h]) : n - 1; + n = B_N_CHILD(tb->FL[h],order_L)->dc_size / (DC_SIZE + KEY_SIZE); + set_parameters (tb, h, -n-1, 0, 0, NULL, -1, -1); + return CARRY_ON; + } + + if ( tb->rnum[h] >= vn->vn_nr_item + 1 ) + { + /* All contents of S[h] can be moved to R[h]. */ + int n; + int order_R; + + order_R = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==B_NR_ITEMS(Fh)) ? 0 : n + 1; + n = B_N_CHILD(tb->FR[h],order_R)->dc_size / (DC_SIZE + KEY_SIZE); + set_parameters (tb, h, 0, -n-1, 0, NULL, -1, -1); + return CARRY_ON; + } + } + + if (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1) + { + /* All contents of S[h] can be moved to the neighbors (L[h] & R[h]). */ + int to_r; + + to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 - + (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]); + set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* Balancing does not lead to better packing. */ + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; + } + + /* Current node contain insufficient number of items. Balancing is required. */ + /* Check whether we can merge S[h] with left neighbor. */ + if (tb->lnum[h] >= vn->vn_nr_item + 1) + if (is_left_neighbor_in_cache (tb,h) || tb->rnum[h] < vn->vn_nr_item + 1 || !tb->FR[h]) + { + int n; + int order_L; + + order_L = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==0) ? B_NR_ITEMS(tb->FL[h]) : n - 1; + n = B_N_CHILD(tb->FL[h],order_L)->dc_size / (DC_SIZE + KEY_SIZE); + set_parameters (tb, h, -n-1, 0, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* Check whether we can merge S[h] with right neighbor. */ + if (tb->rnum[h] >= vn->vn_nr_item + 1) + { + int n; + int order_R; + + order_R = ((n=PATH_H_B_ITEM_ORDER(tb->tb_path, h))==B_NR_ITEMS(Fh)) ? 0 : (n + 1); + n = B_N_CHILD(tb->FR[h],order_R)->dc_size / (DC_SIZE + KEY_SIZE); + set_parameters (tb, h, 0, -n-1, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* All contents of S[h] can be moved to the neighbors (L[h] & R[h]). */ + if (tb->rnum[h] + tb->lnum[h] >= vn->vn_nr_item + 1) + { + int to_r; + + to_r = ((MAX_NR_KEY(Sh)<<1)+2-tb->lnum[h]-tb->rnum[h]+vn->vn_nr_item+1)/2 - + (MAX_NR_KEY(Sh) + 1 - tb->rnum[h]); + set_parameters (tb, h, vn->vn_nr_item + 1 - to_r, to_r, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* For internal nodes try to borrow item from a neighbor */ +#ifdef CONFIG_REISERFS_CHECK + if (!tb->FL[h] && !tb->FR[h]) + reiserfs_panic (0, "vs-8235: dc_check_balance_internal: trying to borrow for root"); +#endif + + /* Borrow one or two items from caching neighbor */ + if (is_left_neighbor_in_cache (tb,h) || !tb->FR[h]) + { + int from_l; + + from_l = (MAX_NR_KEY(Sh) + 1 - tb->lnum[h] + vn->vn_nr_item + 1) / 2 - (vn->vn_nr_item + 1); + set_parameters (tb, h, -from_l, 0, 1, NULL, -1, -1); + return CARRY_ON; + } + + set_parameters (tb, h, 0, -((MAX_NR_KEY(Sh)+1-tb->rnum[h]+vn->vn_nr_item+1)/2-(vn->vn_nr_item+1)), 1, + NULL, -1, -1); + return CARRY_ON; +} + + +/* Check whether current node S[h] is balanced when Decreasing its size by + * Deleting or Truncating for LEAF node of S+tree. + * Calculate parameters for balancing for current level h. + * Parameters: + * tb tree_balance structure; + * h current level of the node; + * inum item number in S[h]; + * mode i - insert, p - paste; + * Returns: 1 - schedule occured; + * 0 - balancing for higher levels needed; + * -1 - no balancing for higher levels needed; + * -2 - no disk space. + */ +static int dc_check_balance_leaf (struct tree_balance * tb, int h) +{ + struct virtual_node * vn = tb->tb_vn; + + /* Number of bytes that must be deleted from + (value is negative if bytes are deleted) buffer which + contains node being balanced. The mnemonic is that the + attempted change in node space used level is levbytes bytes. */ + int levbytes; + /* the maximal item size */ + int maxsize, + n_ret_value; + /* S0 is the node whose balance is currently being checked, + and F0 is its father. */ + struct buffer_head * S0, * F0; + int lfree, rfree /* free space in L and R */; + + S0 = PATH_H_PBUFFER (tb->tb_path, 0); + F0 = PATH_H_PPARENT (tb->tb_path, 0); + + levbytes = tb->insert_size[h]; + + maxsize = MAX_CHILD_SIZE(S0); /* maximal possible size of an item */ + + if ( ! F0 ) + { /* S[0] is the root now. */ + +#ifdef CONFIG_REISERFS_CHECK + if ( -levbytes >= maxsize - B_BLK_HEAD(S0)->blk_free_space ) + reiserfs_panic (tb->tb_sb, "vs-8240: dc_check_balance_leaf: attempt to create empty buffer tree"); +#endif + + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; + } + + if ( (n_ret_value = get_parents(tb,h)) != CARRY_ON ) + return n_ret_value; + + /* get free space of neighbors */ + rfree = get_rfree (tb, h); + lfree = get_lfree (tb, h); + + create_virtual_node (tb, h); + + /* if 3 leaves can be merge to one, set parameters and return */ + if (are_leaves_removable (tb, lfree, rfree)) + return CARRY_ON; + + /* determine maximal number of items we can shift to the left/right neighbor + and the maximal number of bytes that can flow to the left/right neighbor + from the left/right most liquid item that cannot be shifted from S[0] entirely + */ + check_left (tb, h, lfree); + check_right (tb, h, rfree); + + /* check whether we can merge S with left neighbor. */ + if (tb->lnum[0] >= vn->vn_nr_item && tb->lbytes == -1) + if (is_left_neighbor_in_cache (tb,h) || + ((tb->rnum[0] - ((tb->rbytes == -1) ? 0 : 1)) < vn->vn_nr_item) || /* S can not be merged with R */ + !tb->FR[h]) { + +#ifdef CONFIG_REISERFS_CHECK + if (!tb->FL[h]) + reiserfs_panic (0, "vs-8245: dc_check_balance_leaf: FL[h] must exist"); +#endif + + /* set parameter to merge S[0] with its left neighbor */ + set_parameters (tb, h, -1, 0, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* check whether we can merge S[0] with right neighbor. */ + if (tb->rnum[0] >= vn->vn_nr_item && tb->rbytes == -1) { + set_parameters (tb, h, 0, -1, 0, NULL, -1, -1); + return CARRY_ON; + } + + /* All contents of S[0] can be moved to the neighbors (L[0] & R[0]). Set parameters and return */ + if (is_leaf_removable (tb)) + return CARRY_ON; + + /* Balancing is not required. */ + tb->s0num = vn->vn_nr_item; + set_parameters (tb, h, 0, 0, 1, NULL, -1, -1); + return NO_BALANCING_NEEDED; +} + + + +/* Check whether current node S[h] is balanced when Decreasing its size by + * Deleting or Cutting. + * Calculate parameters for balancing for current level h. + * Parameters: + * tb tree_balance structure; + * h current level of the node; + * inum item number in S[h]; + * mode d - delete, c - cut. + * Returns: 1 - schedule occured; + * 0 - balancing for higher levels needed; + * -1 - no balancing for higher levels needed; + * -2 - no disk space. + */ +static int dc_check_balance (struct tree_balance * tb, int h) +{ + +#ifdef CONFIG_REISERFS_CHECK + if ( ! (PATH_H_PBUFFER (tb->tb_path, h)) ) + reiserfs_panic(tb->tb_sb, "vs-8250: dc_check_balance: S is not initialized"); +#endif + + if ( h ) + return dc_check_balance_internal (tb, h); + else + return dc_check_balance_leaf (tb, h); +} + + + +/* Check whether current node S[h] is balanced. + * Calculate parameters for balancing for current level h. + * Parameters: + * + * tb tree_balance structure: + * + * tb is a large structure that must be read about in the header file + * at the same time as this procedure if the reader is to successfully + * understand this procedure + * + * h current level of the node; + * inum item number in S[h]; + * mode i - insert, p - paste, d - delete, c - cut. + * Returns: 1 - schedule occured; + * 0 - balancing for higher levels needed; + * -1 - no balancing for higher levels needed; + * -2 - no disk space. + */ +static int check_balance (int mode, struct tree_balance * tb, + int h, int inum, int pos_in_item, + struct item_head * ins_ih) +{ + struct virtual_node * vn; + + vn = tb->tb_vn = (struct virtual_node *)(tb->vn_buf);// + ROUND_UP(SB_BMAP_NR (tb->tb_sb) * 2 / 8 + 1, 4)); + vn->vn_free_ptr = (char *)(tb->tb_vn + 1); + vn->vn_mode = mode; + vn->vn_affected_item_num = inum; + vn->vn_pos_in_item = pos_in_item; + vn->vn_ins_ih = ins_ih; + +#ifdef CONFIG_REISERFS_CHECK + if (mode == M_INSERT && !vn->vn_ins_ih) + reiserfs_panic (0, "vs-8255: check_balance: ins_ih can not be 0 in insert mode"); +#endif + + if ( tb->insert_size[h] > 0 ) + /* Calculate balance parameters when size of node is increasing. */ + return ip_check_balance (tb, h); + + /* Calculate balance parameters when size of node is decreasing. */ + return dc_check_balance (tb, h); +} + + + +/* Check whether parent at the path is the really parent of the current node.*/ +static int get_direct_parent( + struct tree_balance * p_s_tb, + int n_h + ) { + struct buffer_head * p_s_bh; + struct path * p_s_path = p_s_tb->tb_path; + int n_position, + n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h); + + /* We are in the root or in the new root. */ + if ( n_path_offset <= FIRST_PATH_ELEMENT_OFFSET ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( n_path_offset < FIRST_PATH_ELEMENT_OFFSET - 1 ) + reiserfs_panic(p_s_tb->tb_sb, "PAP-8260: get_direct_parent: illegal offset in the path"); +#endif + + if ( PATH_OFFSET_PBUFFER(p_s_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr == + SB_ROOT_BLOCK (p_s_tb->tb_sb) ) { + /* Root is not changed. */ + PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1) = NULL; + PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1) = 0; + return CARRY_ON; + } + return PATH_INCORRECT; /* Root is changed and we must recalculate the path. */ + } + + if ( ! B_IS_IN_TREE(p_s_bh = PATH_OFFSET_PBUFFER(p_s_path, n_path_offset - 1)) ) + return PATH_INCORRECT; /* Parent in the path is not in the tree. */ + + if ( (n_position = PATH_OFFSET_POSITION(p_s_path, n_path_offset - 1)) > B_NR_ITEMS(p_s_bh) ) + return PATH_INCORRECT; + + if ( B_N_CHILD_NUM(p_s_bh, n_position) != PATH_OFFSET_PBUFFER(p_s_path, n_path_offset)->b_blocknr ) + /* Parent in the path is not parent of the current node in the tree. */ + return PATH_INCORRECT; + +#if 0 + if ( test_and_wait_on_buffer(p_s_bh) == SCHEDULE_OCCURRED ) /* Buffer was locked. */ + return SCHEDULE_OCCURRED; +#endif + return CARRY_ON; /* Parent in the path is unlocked and really parent of the current node. */ +} + + +/* Using lnum[n_h] and rnum[n_h] we should determine what neighbors + * of S[n_h] we + * need in order to balance S[n_h], and get them if necessary. + * Returns: SCHEDULE_OCCURRED - schedule occured while the function worked; + * CARRY_ON - schedule didn't occur while the function worked; + */ +static int get_neighbors(struct tree_balance * p_s_tb, int n_h) +{ + int n_child_position, + n_repeat, + n_path_offset = PATH_H_PATH_OFFSET(p_s_tb->tb_path, n_h + 1); + unsigned long n_son_number; + struct super_block * p_s_sb = p_s_tb->tb_sb; + struct buffer_head * p_s_bh; + /*struct virtual_node * vn = p_s_tb->tb_vn;*/ + + if ( p_s_tb->lnum[n_h] ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! p_s_tb->lnum[n_h] && vn->vn_mode == M_CUT && + ! (vn->vn_vi[0].vi_type & VI_TYPE_DIRECTORY) ) + reiserfs_panic (p_s_tb->tb_sb, "PAP-8265: get_neighbors: item must be directory item"); +#endif + + /* We need left neighbor to balance S[n_h]. */ + p_s_bh = PATH_OFFSET_PBUFFER(p_s_tb->tb_path, n_path_offset); + +#ifdef CONFIG_REISERFS_CHECK + if ( p_s_bh == p_s_tb->FL[n_h] && ! PATH_OFFSET_POSITION(p_s_tb->tb_path, n_path_offset) ) + reiserfs_panic (p_s_tb->tb_sb, "PAP-8270: get_neighbors: invalid position in the parent"); +#endif + + n_child_position = ( p_s_bh == p_s_tb->FL[n_h] ) ? p_s_tb->lkey[n_h] : B_BLK_HEAD(p_s_tb->FL[n_h])->blk_nr_item; + n_son_number = B_N_CHILD_NUM(p_s_tb->FL[n_h], n_child_position); + n_repeat = CARRY_ON; + p_s_bh = reiserfs_bread(p_s_sb->s_dev, n_son_number, p_s_sb->s_blocksize, &n_repeat); + if (!p_s_bh) + return IO_ERROR; + if ( n_repeat != CARRY_ON ) { + brelse /*decrement_bcount*/(p_s_bh); + return SCHEDULE_OCCURRED; + } + +#ifdef CONFIG_REISERFS_CHECK + if ( ! B_IS_IN_TREE(p_s_tb->FL[n_h]) || n_child_position > B_NR_ITEMS(p_s_tb->FL[n_h]) || + B_N_CHILD_NUM(p_s_tb->FL[n_h], n_child_position) != p_s_bh->b_blocknr ) + reiserfs_panic (p_s_tb->tb_sb, "PAP-8275: get_neighbors: invalid parent"); + if ( ! B_IS_IN_TREE(p_s_bh) ) + reiserfs_panic (p_s_tb->tb_sb, "PAP-8280: get_neighbors: invalid child"); + + if (! n_h && node_free_space (p_s_bh) != MAX_CHILD_SIZE (p_s_bh) - B_N_CHILD (p_s_tb->FL[0],n_child_position)->dc_size) { + reiserfs_panic (p_s_tb->tb_sb, "PAP-8290: get_neighbors: invalid child size of left neighbor"); + } +#endif + + brelse /*decrement_bcount*/(p_s_tb->L[n_h]); + p_s_tb->L[n_h] = p_s_bh; + } + + if ( p_s_tb->rnum[n_h] ) { /* We need right neighbor to balance S[n_path_offset]. */ + p_s_bh = PATH_OFFSET_PBUFFER(p_s_tb->tb_path, n_path_offset); + +#ifdef CONFIG_REISERFS_CHECK + if ( p_s_bh == p_s_tb->FR[n_h] && PATH_OFFSET_POSITION(p_s_tb->tb_path, n_path_offset) >= B_NR_ITEMS(p_s_bh) ) + reiserfs_panic (p_s_tb->tb_sb, "PAP-8295: get_neighbors: invalid position in the parent"); +#endif + + n_child_position = ( p_s_bh == p_s_tb->FR[n_h] ) ? p_s_tb->rkey[n_h] + 1 : 0; + n_son_number = B_N_CHILD_NUM(p_s_tb->FR[n_h], n_child_position); + n_repeat = CARRY_ON; + p_s_bh = reiserfs_bread(p_s_sb->s_dev, n_son_number, p_s_sb->s_blocksize, &n_repeat); + if (!p_s_bh) + return IO_ERROR; + if ( n_repeat != CARRY_ON ) { + brelse/*decrement_bcount*/(p_s_bh); + return SCHEDULE_OCCURRED; + } + brelse/*decrement_bcount*/(p_s_tb->R[n_h]); + p_s_tb->R[n_h] = p_s_bh; + +#ifdef CONFIG_REISERFS_CHECK + if (! n_h && node_free_space (p_s_bh) != MAX_CHILD_SIZE (p_s_bh) - B_N_CHILD (p_s_tb->FR[0],n_child_position)->dc_size) { + reiserfs_panic (p_s_tb->tb_sb, "PAP-8300: get_neighbors: invalid child size of right neighbor (%d != %d - %d)", + node_free_space (p_s_bh), MAX_CHILD_SIZE (p_s_bh), B_N_CHILD (p_s_tb->FR[0],n_child_position)->dc_size); + } +#endif + + } + return CARRY_ON; +} + + +void * reiserfs_kmalloc (size_t size, int flags, struct super_block * s) +{ + void * vp; + + vp = getmem (size); + return vp; +} + +void reiserfs_kfree (/*const */void * vp, size_t size, struct super_block * s) +{ + freemem (vp); +#if 0 + + kfree (vp); + + s->u.reiserfs_sb.s_kmallocs -= size; + if (s->u.reiserfs_sb.s_kmallocs < 0) + reiserfs_warning ("vs-8302: reiserfs_kfree: allocated memory %d\n", s->u.reiserfs_sb.s_kmallocs); +#endif +} + + +static int get_virtual_node_size (struct super_block * sb, struct buffer_head * bh) +{ + //int size = sizeof (struct virtual_item); /* for new item in case of insert */ + //int i, nr_items; + //struct item_head * ih; + + return sb->s_blocksize; + +#if 0 + size = sizeof (struct virtual_node) + sizeof (struct virtual_item); + ih = B_N_PITEM_HEAD (bh, 0); + nr_items = B_NR_ITEMS (bh); + for (i = 0; i < nr_items; i ++, ih ++) { + /* each item occupies some space in virtual node */ + size += sizeof (struct virtual_item); + if (I_IS_DIRECTORY_ITEM (ih)) + /* each entry and new one occupeis 2 byte in the virtual node */ + size += (ih_entry_count (ih) + 1) * sizeof (__u16); + } + + /* 1 bit for each bitmap block to note whether bitmap block was + dirtied in the operation */ + size += (SB_BMAP_NR (sb) * 2 / 8 + 4); + return size; +#endif +} + + +static int get_mem_for_virtual_node (struct tree_balance * tb) +{ + int size; + + + size = get_virtual_node_size (tb->tb_sb, PATH_PLAST_BUFFER(tb->tb_path)); + tb->vn_buf = getmem (size); + return CARRY_ON; +} + + +/* Prepare for balancing, that is + * get all necessary parents, and neighbors; + * analyze what and where should be moved; + * get sufficient number of new nodes; + * Balancing will start only after all resources will be collected at a time. + * + * When ported to SMP kernels, only at the last moment after all needed nodes + * are collected in cache, will the resources be locked using the usual + * textbook ordered lock acquisition algorithms. Note that ensuring that + * this code neither write locks what it does not need to write lock nor locks out of order + * will be a pain in the butt that could have been avoided. Grumble grumble. -Hans + * + * fix is meant in the sense of render unchanging + * + * Latency might be improved by first gathering a list of what buffers are needed + * and then getting as many of them in parallel as possible? -Hans + * + * Parameters: + * op_mode i - insert, d - delete, c - cut (truncate), p - paste (append) + * tb tree_balance structure; + * inum item number in S[h]; + * pos_in_item - comment this if you can + * ins_ih & ins_sd are used when inserting + * Returns: 1 - schedule occurred while the function worked; + * 0 - schedule didn't occur while the function worked; + * -1 - if no_disk_space + */ + + +int fix_nodes (/*struct reiserfs_transaction_handle *th,*/ + int n_op_mode, struct tree_balance * p_s_tb, + struct item_head * p_s_ins_ih) +{ + int n_pos_in_item = p_s_tb->tb_path->pos_in_item; + int n_ret_value, + n_h, + n_item_num = get_item_pos (p_s_tb->tb_path); + struct buffer_head * p_s_tbS0 = get_bh (p_s_tb->tb_path); +// struct item_head * ih = get_ih (p_s_tb->tb_path); + + + /* if it possible in indirect_to_direct conversion */ + if (buffer_locked (p_s_tbS0)) { + return SCHEDULE_OCCURRED; + } + + +#ifdef CONFIG_REISERFS_CHECK + if ( cur_tb ) { + print_tb (n_op_mode, n_item_num, n_pos_in_item, cur_tb,"fix_nodes"); + reiserfs_panic(p_s_tb->tb_sb,"PAP-8305: fix_nodes: there is pending do_balance"); + } + + if (!buffer_uptodate (p_s_tbS0) || !B_IS_IN_TREE (p_s_tbS0)) { + reiserfs_panic (p_s_tb->tb_sb, "PAP-8320: fix_nodes: S[0] (%b %z) is not uptodate " + "at the beginning of fix_nodes or not in tree (mode %c)", p_s_tbS0, p_s_tbS0, n_op_mode); + } + + /* Check parameters. */ + switch (n_op_mode) { +#ifndef FU //REISERFS_FSCK + // FIXME: REISERFS_CHECK can not be turned on for utils + case M_INTERNAL: + break; + case M_INSERT: + if ( n_item_num < 0 || n_item_num > B_NR_ITEMS(p_s_tbS0) ) + reiserfs_panic(p_s_tb->tb_sb,"PAP-8325: fix_nodes: Incorrect item number %d (in S0 - %d) in case of insert", + n_item_num, B_NR_ITEMS(p_s_tbS0)); +#else + case M_INSERT: + if ( n_item_num <= 0 || n_item_num > B_NR_ITEMS(p_s_tbS0) ) + reiserfs_panic(p_s_tb->tb_sb,"PAP-8330: fix_nodes: Incorrect item number %d (in S0 - %d) in case of insert", + n_item_num, B_NR_ITEMS(p_s_tbS0)); +#endif + break; + case M_PASTE: + if (I_IS_DIRECT_ITEM (get_ih (p_s_tb->tb_path))) { + // we can paste only to the end for now + if (n_pos_in_item != ih_item_len (get_ih (p_s_tb->tb_path))) + reiserfs_panic (th->t_super, "vs-8332: fix_nodes: " + "pos_in_item %d set improperly to paste direct item %h", + n_pos_in_item, get_ih (p_s_tb->tb_path)); + } + // fall through + case M_DELETE: + case M_CUT: + if ( n_item_num < 0 || n_item_num >= B_NR_ITEMS(p_s_tbS0) ) { + print_block (p_s_tbS0, 0, -1, -1); + printk("mode = %c insert_size = %d\n", n_op_mode, p_s_tb->insert_size[0]); + reiserfs_panic(p_s_tb->tb_sb,"PAP-8335: fix_nodes: Incorrect item number(%d)", n_item_num); + } + break; + default: + reiserfs_panic(p_s_tb->tb_sb,"PAP-8340: fix_nodes: Incorrect mode of operation"); + } +#endif + + + if (get_mem_for_virtual_node (p_s_tb) == SCHEDULE_OCCURRED) { + return SCHEDULE_OCCURRED; + } + + /* Starting from the leaf level; for all levels n_h of the tree. */ + for ( n_h = 0; n_h < MAX_HEIGHT && p_s_tb->insert_size[n_h]; n_h++ ) { + if ( (n_ret_value = get_direct_parent(p_s_tb, n_h)) != CARRY_ON ) { + return n_ret_value; + } + + if ( (n_ret_value = check_balance (/*th,*/ n_op_mode, p_s_tb, n_h, n_item_num, + n_pos_in_item, p_s_ins_ih)) != CARRY_ON ) { + if ( n_ret_value == NO_BALANCING_NEEDED ) { + /* No balancing for higher levels needed. */ + if ( (n_ret_value = get_neighbors(p_s_tb, n_h)) != CARRY_ON ) { + return n_ret_value; + } + if ( n_h != MAX_HEIGHT - 1 ) + p_s_tb->insert_size[n_h + 1] = 0; + /* ok, analysis and resource gathering are complete */ + break; + } + + return n_ret_value; + } + + if ( (n_ret_value = get_neighbors(p_s_tb, n_h)) != CARRY_ON ) { + return n_ret_value; + } + + if ( (n_ret_value = get_empty_nodes(/*th,*/ p_s_tb, n_h)) != CARRY_ON ) { + return n_ret_value; /* No disk space, or schedule occurred and + analysis may be invalid and needs to be redone. */ + } + + if ( ! PATH_H_PBUFFER(p_s_tb->tb_path, n_h) ) { + /* We have a positive insert size but no nodes exist on this + level, this means that we are creating a new root. */ + +#ifdef CONFIG_REISERFS_CHECK + if ( p_s_tb->blknum[n_h] != 1 ) + reiserfs_panic(p_s_tb->tb_sb,"PAP-8350: fix_nodes: creating new empty root"); +#endif /* CONFIG_REISERFS_CHECK */ + + if ( n_h < MAX_HEIGHT - 1 ) + p_s_tb->insert_size[n_h + 1] = 0; + } + else + if ( ! PATH_H_PBUFFER(p_s_tb->tb_path, n_h + 1) ) { + if ( p_s_tb->blknum[n_h] > 1 ) { + /* The tree needs to be grown, so this node S[n_h] + which is the root node is split into two nodes, and + a new node (S[n_h+1]) will be created to become the root node. */ + +#ifdef CONFIG_REISERFS_CHECK + if ( n_h == MAX_HEIGHT - 1 ) + reiserfs_panic(p_s_tb->tb_sb, "PAP-8355: fix_nodes: attempt to create too high of a tree"); +#endif /* CONFIG_REISERFS_CHECK */ + + p_s_tb->insert_size[n_h + 1] = (DC_SIZE + KEY_SIZE) * (p_s_tb->blknum[n_h] - 1) + DC_SIZE; + } + else + if ( n_h < MAX_HEIGHT - 1 ) + p_s_tb->insert_size[n_h + 1] = 0; + } + else + p_s_tb->insert_size[n_h + 1] = (DC_SIZE + KEY_SIZE) * (p_s_tb->blknum[n_h] - 1); + } + + return CARRY_ON; /* schedule did not occur */ +} + + +void unfix_nodes(/* struct reiserfs_transaction_handle *th,*/struct tree_balance * p_s_tb) +{ + struct path * p_s_path = p_s_tb->tb_path; + int n_counter; + // int i, j; + //struct buffer_head * bh; + +#ifdef CONFIG_REISERFS_CHECK + if ( ! p_s_tb->vn_buf ) + reiserfs_panic (p_s_tb->tb_sb, + "PAP-16050: unfix_nodes: pointer to the virtual node is NULL"); +#endif + + + /* Release path buffers. */ + pathrelse(p_s_path); + + + for ( n_counter = 0; n_counter < MAX_HEIGHT; n_counter++ ) { + /* Release fathers and neighbors. */ + brelse(p_s_tb->L[n_counter]); + brelse(p_s_tb->R[n_counter]); + brelse(p_s_tb->FL[n_counter]); + brelse(p_s_tb->FR[n_counter]); + brelse(p_s_tb->CFL[n_counter]); + brelse(p_s_tb->CFR[n_counter]); + } + + /* Could be optimized. Will be done by PAP someday */ + for ( n_counter = 0; n_counter < MAX_FEB_SIZE; n_counter++ ) { + if ( p_s_tb->FEB[n_counter] ) { + /* release what was not used */ + reiserfs_free_block(p_s_tb->tb_sb, p_s_tb->FEB[n_counter]->b_blocknr); + + bforget(p_s_tb->FEB[n_counter]); + /* tree balance bitmap of bitmaps has bit set already */ + } + /* release used as new nodes including a new root */ + brelse (p_s_tb->used[n_counter]); + } + + reiserfs_kfree (p_s_tb->vn_buf, p_s_tb->vn_buf_size, p_s_tb->tb_sb); + +} + + + + + + diff --git a/reiserfscore/hashes.c b/reiserfscore/hashes.c new file mode 100644 index 0000000..32628ce --- /dev/null +++ b/reiserfscore/hashes.c @@ -0,0 +1,253 @@ +/* + * Keyed 32-bit hash function using TEA in a Davis-Meyer function + * H0 = Key + * Hi = E Mi(Hi-1) + Hi-1 + * + * (see Applied Cryptography, 2nd edition, p448). + * + * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998 + * + * Jeremy has agreed to the contents of reiserfs/README. -Hans + * Yura's function is added (04/07/2000) + */ + +// +// keyed_hash +// yura_hash +// r5 +// + +#include <asm/types.h> + + + +#define DELTA 0x9E3779B9 +#define FULLROUNDS 10 /* 32 is overkill, 16 is strong crypto */ +#define PARTROUNDS 6 /* 6 gets complete mixing */ + +#ifndef __KERNEL__ +typedef __u32 u32; +#endif + +/* a, b, c, d - data; h0, h1 - accumulated hash */ +#define TEACORE(rounds) \ + do { \ + u32 sum = 0; \ + int n = rounds; \ + u32 b0, b1; \ + \ + b0 = h0; \ + b1 = h1; \ + \ + do \ + { \ + sum += DELTA; \ + b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); \ + b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); \ + } while(--n); \ + \ + h0 += b0; \ + h1 += b1; \ + } while(0) + + +u32 keyed_hash(const char *msg, int len) +{ + u32 k[] = { 0x9464a485, 0x542e1a94, 0x3e846bff, 0xb75bcfc3}; + + u32 h0 = k[0], h1 = k[1]; + u32 a, b, c, d; + u32 pad; + int i; + + + // assert(len >= 0 && len < 256); + + pad = (u32)len | ((u32)len << 8); + pad |= pad << 16; + + while(len >= 16) + { + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + c = (u32)msg[ 8] | + (u32)msg[ 9] << 8 | + (u32)msg[10] << 16| + (u32)msg[11] << 24; + d = (u32)msg[12] | + (u32)msg[13] << 8 | + (u32)msg[14] << 16| + (u32)msg[15] << 24; + + TEACORE(PARTROUNDS); + + len -= 16; + msg += 16; + } + + if (len >= 12) + { + //assert(len < 16); + if (len >= 16) + *(int *)0 = 0; + + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + c = (u32)msg[ 8] | + (u32)msg[ 9] << 8 | + (u32)msg[10] << 16| + (u32)msg[11] << 24; + + d = pad; + for(i = 12; i < len; i++) + { + d <<= 8; + d |= msg[i]; + } + } + else if (len >= 8) + { + //assert(len < 12); + if (len >= 12) + *(int *)0 = 0; + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + b = (u32)msg[ 4] | + (u32)msg[ 5] << 8 | + (u32)msg[ 6] << 16| + (u32)msg[ 7] << 24; + + c = d = pad; + for(i = 8; i < len; i++) + { + c <<= 8; + c |= msg[i]; + } + } + else if (len >= 4) + { + //assert(len < 8); + if (len >= 8) + *(int *)0 = 0; + a = (u32)msg[ 0] | + (u32)msg[ 1] << 8 | + (u32)msg[ 2] << 16| + (u32)msg[ 3] << 24; + + b = c = d = pad; + for(i = 4; i < len; i++) + { + b <<= 8; + b |= msg[i]; + } + } + else + { + //assert(len < 4); + if (len >= 4) + *(int *)0 = 0; + a = b = c = d = pad; + for(i = 0; i < len; i++) + { + a <<= 8; + a |= msg[i]; + } + } + + TEACORE(FULLROUNDS); + +/* return 0;*/ + return h0^h1; +} + + +u32 yura_hash (const char *msg, int len) +{ + int j, pow; + u32 a, c; + int i; + + for (pow=1,i=1; i < len; i++) pow = pow * 10; + + if (len == 1) + a = msg[0]-48; + else + a = (msg[0] - 48) * pow; + + for (i=1; i < len; i++) { + c = msg[i] - 48; + for (pow=1,j=i; j < len-1; j++) pow = pow * 10; + a = a + c * pow; + } + + for (; i < 40; i++) { + c = '0' - 48; + for (pow=1,j=i; j < len-1; j++) pow = pow * 10; + a = a + c * pow; + } + + for (; i < 256; i++) { + c = i; + for (pow=1,j=i; j < len-1; j++) pow = pow * 10; + a = a + c * pow; + } + + a = a << 7; + return a; +} + + +u32 r5_hash (const char *msg, int len) +{ + u32 a=0; + int i; + + for (i = 0; i < len; i ++) { + a += msg[i] << 4; + a += msg[i] >> 4; + a *= 11; + } + return a; +} + +#if 0 + +#include <stdio.h> +//#include <stddef.h> + +int main (void) +{ + char * name = 0; + size_t n = 0; + + while (1) { + getline (&name, &n, stdin); + if (!strcmp (name, "\n")) + break; + name [strlen (name) - 1] = 0; + printf ("tea %lu\n, r5 %lu\nyura %lu\n", + keyed_hash (name, strlen (name)) & 0x7fffff80, + r5_hash (name, strlen (name)) & 0x7fffff80, + yura_hash (name, strlen (name)) & 0x7fffff80); + free (name); + name = 0; + n = 0; + } +} + +#endif + diff --git a/reiserfscore/ibalance.c b/reiserfscore/ibalance.c new file mode 100644 index 0000000..782f099 --- /dev/null +++ b/reiserfscore/ibalance.c @@ -0,0 +1,1052 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +#include "includes.h" + + +/* this is one and only function that is used outside (do_balance.c) */ +int balance_internal ( + /*struct reiserfs_transaction_handle *th,*/ + struct tree_balance * , + int, + int, + struct item_head * , + struct buffer_head ** + ); + +/* modes of internal_shift_left, internal_shift_right and internal_insert_childs */ +#define INTERNAL_SHIFT_FROM_S_TO_L 0 +#define INTERNAL_SHIFT_FROM_R_TO_S 1 +#define INTERNAL_SHIFT_FROM_L_TO_S 2 +#define INTERNAL_SHIFT_FROM_S_TO_R 3 +#define INTERNAL_INSERT_TO_S 4 +#define INTERNAL_INSERT_TO_L 5 +#define INTERNAL_INSERT_TO_R 6 + +static void internal_define_dest_src_infos ( + int shift_mode, + struct tree_balance * tb, + int h, + struct buffer_info * dest_bi, + struct buffer_info * src_bi, + int * d_key, + struct buffer_head ** cf + ) +{ +#ifdef CONFIG_REISERFS_CHECK + memset (dest_bi, 0, sizeof (struct buffer_info)); + memset (src_bi, 0, sizeof (struct buffer_info)); +#endif + /* define dest, src, dest parent, dest position */ + switch (shift_mode) { + case INTERNAL_SHIFT_FROM_S_TO_L: /* used in internal_shift_left */ + src_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h); + src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h); + src_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + dest_bi->bi_bh = tb->L[h]; + dest_bi->bi_parent = tb->FL[h]; + dest_bi->bi_position = get_left_neighbor_position (tb, h); + *d_key = tb->lkey[h]; + *cf = tb->CFL[h]; + break; + case INTERNAL_SHIFT_FROM_L_TO_S: + src_bi->bi_bh = tb->L[h]; + src_bi->bi_parent = tb->FL[h]; + src_bi->bi_position = get_left_neighbor_position (tb, h); + dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h); + dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h); + dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); /* dest position is analog of dest->b_item_order */ + *d_key = tb->lkey[h]; + *cf = tb->CFL[h]; + break; + + case INTERNAL_SHIFT_FROM_R_TO_S: /* used in internal_shift_left */ + src_bi->bi_bh = tb->R[h]; + src_bi->bi_parent = tb->FR[h]; + src_bi->bi_position = get_right_neighbor_position (tb, h); + dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h); + dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h); + dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + *d_key = tb->rkey[h]; + *cf = tb->CFR[h]; + break; + case INTERNAL_SHIFT_FROM_S_TO_R: + src_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h); + src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h); + src_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + dest_bi->bi_bh = tb->R[h]; + dest_bi->bi_parent = tb->FR[h]; + dest_bi->bi_position = get_right_neighbor_position (tb, h); + *d_key = tb->rkey[h]; + *cf = tb->CFR[h]; + break; + + case INTERNAL_INSERT_TO_L: + dest_bi->bi_bh = tb->L[h]; + dest_bi->bi_parent = tb->FL[h]; + dest_bi->bi_position = get_left_neighbor_position (tb, h); + break; + + case INTERNAL_INSERT_TO_S: + dest_bi->bi_bh = PATH_H_PBUFFER (tb->tb_path, h); + dest_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, h); + dest_bi->bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + break; + + case INTERNAL_INSERT_TO_R: + dest_bi->bi_bh = tb->R[h]; + dest_bi->bi_parent = tb->FR[h]; + dest_bi->bi_position = get_right_neighbor_position (tb, h); + break; + + default: + reiserfs_panic ("internal_define_dest_src_infos", "shift type is unknown (%d)", shift_mode); + } +} + + + +/* Insert 'count' node pointers into buffer cur before position 'to' + 1. + * Insert count items into buffer cur before position to. + * Items and node pointers are specified by inserted and bh respectively. + */ +static void internal_insert_childs (reiserfs_filsys_t fs, + struct buffer_info * cur_bi, + int to, int count, + struct item_head * inserted, + struct buffer_head ** bh) +{ + struct buffer_head * cur = cur_bi->bi_bh; + int nr; + struct key * key; + struct disk_child new_dc[2]; + struct disk_child * dc; + int i; + int from; + + if (count <= 0) + return; + + nr = node_item_number (cur); + +#ifdef CONFIG_REISERFS_CHECK + if (count > 2) + reiserfs_panic (0, "internal_insert_childs", "too many children (%d) are to be inserted", count); + if (node_free_space (cur) < count * (KEY_SIZE + DC_SIZE)) + reiserfs_panic (0, "internal_insert_childs", "no enough free space (%d), needed %d bytes", + node_free_space (cur), count * (KEY_SIZE + DC_SIZE)); +#endif /* CONFIG_REISERFS_CHECK */ + + /* prepare space for count disk_child */ + dc = B_N_CHILD (cur,to+1); + + memmove (dc + count, dc, (nr+1-(to+1)) * DC_SIZE); + + /* make disk child array for insertion */ + for (i = 0; i < count; i ++) { + new_dc[i].dc_size = cpu_to_le16 (MAX_CHILD_SIZE(bh[i]) - + node_free_space (bh[i])); + new_dc[i].dc_block_number = cpu_to_le32 (bh[i]->b_blocknr); + } + memcpy (dc, new_dc, DC_SIZE * count); + + /* prepare space for 'count' items */ + from = ((to == -1) ? 0 : to); + key = B_N_PDELIM_KEY (cur, from); + + memmove (key + count, key, (nr - from/*to*/) * KEY_SIZE + (nr + 1 + count) * DC_SIZE); + + /* copy keys */ + memcpy (key, inserted, KEY_SIZE); + if ( count > 1 ) + memcpy (key + 1, inserted + 1, KEY_SIZE); + + /* sizes, item number */ + set_node_item_number (cur, nr + count); + set_node_free_space (cur, node_free_space (cur) - + count * (DC_SIZE + KEY_SIZE)); + mark_buffer_dirty (cur); + + if (cur_bi->bi_parent) { + B_N_CHILD (cur_bi->bi_parent,cur_bi->bi_position)->dc_size += count * (DC_SIZE + KEY_SIZE); + mark_buffer_dirty (cur_bi->bi_parent); + } + +} + + +/* Delete del_num items and node pointers from buffer cur starting from * + * the first_i'th item and first_p'th pointers respectively. */ +static void internal_delete_pointers_items (reiserfs_filsys_t fs, + struct buffer_info * cur_bi, + int first_p, int first_i, + int del_num) +{ + struct buffer_head * cur = cur_bi->bi_bh; + int nr; + struct block_head * blkh; + struct key * key; + struct disk_child * dc; + + +#ifdef CONFIG_REISERFS_CHECK + if (cur == NULL) + reiserfs_panic (0, "internal_delete_pointers_items1: buffer is 0"); + + if (del_num < 0) + reiserfs_panic (0, "internal_delete_pointers_items2", + "negative number of items (%d) can not be deleted", del_num); + + if (first_p < 0 || first_p + del_num > B_NR_ITEMS (cur) + 1 || first_i < 0) + reiserfs_panic (0, "internal_delete_pointers_items3", + "first pointer order (%d) < 0 or " + "no so many pointers (%d), only (%d) or " + "first key order %d < 0", first_p, + first_p + del_num, B_NR_ITEMS (cur) + 1, first_i); +#endif /* CONFIG_REISERFS_CHECK */ + if ( del_num == 0 ) + return; + + nr = (blkh = B_BLK_HEAD(cur))->blk_nr_item; + + if ( first_p == 0 && del_num == nr + 1 ) { +#ifdef CONFIG_REISERFS_CHECK + if ( first_i != 0 ) + reiserfs_panic (0, "internal_delete_pointers_items5", + "first deleted key must have order 0, not %d", first_i); +#endif /* CONFIG_REISERFS_CHECK */ + make_empty_node (cur_bi); + return; + } + +#ifdef CONFIG_REISERFS_CHECK + if (first_i + del_num > B_NR_ITEMS (cur)) { + printk("first_i = %d del_num = %d\n",first_i,del_num); + reiserfs_panic (0, "internal_delete_pointers_items4: :" + "no so many keys (%d) in the node (%b)(%z)", first_i + del_num, cur, cur); + } +#endif /* CONFIG_REISERFS_CHECK */ + + + /* deleting */ + dc = B_N_CHILD (cur, first_p); + + memmove (dc, dc + del_num, (nr + 1 - first_p - del_num) * DC_SIZE); + key = B_N_PDELIM_KEY (cur, first_i); + memmove (key, key + del_num, (nr - first_i - del_num) * KEY_SIZE + (nr + 1 - del_num) * DC_SIZE); + + + /* sizes, item number */ + blkh->blk_nr_item -= del_num; + blkh->blk_free_space += del_num * (KEY_SIZE + DC_SIZE); + + mark_buffer_dirty (cur); + + if (cur_bi->bi_parent) { + B_N_CHILD (cur_bi->bi_parent, cur_bi->bi_position)->dc_size -= del_num * (KEY_SIZE + DC_SIZE); + mark_buffer_dirty (cur_bi->bi_parent); + } +} + + +/* delete n node pointers and items starting from given position */ +static void internal_delete_childs (reiserfs_filsys_t fs, + struct buffer_info * cur_bi, + int from, int n) +{ + int i_from; + + i_from = (from == 0) ? from : from - 1; + + /* delete n pointers starting from `from' position in CUR; + delete n keys starting from 'i_from' position in CUR; + */ + internal_delete_pointers_items (fs, cur_bi, from, i_from, n); +} + + +/* copy cpy_num node pointers and cpy_num - 1 items from buffer src to buffer dest +* last_first == FIRST_TO_LAST means, that we copy first items from src to tail of dest + * last_first == LAST_TO_FIRST means, that we copy last items from src to head of dest + */ +static void internal_copy_pointers_items (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, + struct buffer_head * src, + int last_first, int cpy_num) +{ + /* ATTENTION! Number of node pointers in DEST is equal to number of items in DEST * + * as delimiting key have already inserted to buffer dest.*/ + struct buffer_head * dest = dest_bi->bi_bh; + int nr_dest, nr_src; + int dest_order, src_order; + struct block_head * blkh; + struct key * key; + struct disk_child * dc; + + nr_src = B_NR_ITEMS (src); + +#ifdef CONFIG_REISERFS_CHECK + if ( dest == NULL || src == NULL ) + reiserfs_panic (0, "internal_copy_pointers_items", "src (%p) or dest (%p) buffer is 0", src, dest); + + if (last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST) + reiserfs_panic (0, "internal_copy_pointers_items", + "invalid last_first parameter (%d)", last_first); + + if ( nr_src < cpy_num - 1 ) + reiserfs_panic (0, "internal_copy_pointers_items", "no so many items (%d) in src (%d)", cpy_num, nr_src); + + if ( cpy_num < 0 ) + reiserfs_panic (0, "internal_copy_pointers_items", "cpy_num less than 0 (%d)", cpy_num); + + if (cpy_num - 1 + B_NR_ITEMS(dest) > (int)MAX_NR_KEY(dest)) + reiserfs_panic (0, "internal_copy_pointers_items", + "cpy_num (%d) + item number in dest (%d) can not be more than MAX_NR_KEY(%d)", + cpy_num, B_NR_ITEMS(dest), MAX_NR_KEY(dest)); +#endif + + if ( cpy_num == 0 ) + return; + + /* coping */ + nr_dest = (blkh = B_BLK_HEAD(dest))->blk_nr_item; + + /*dest_order = (last_first == LAST_TO_FIRST) ? 0 : nr_dest;*/ + /*src_order = (last_first == LAST_TO_FIRST) ? (nr_src - cpy_num + 1) : 0;*/ + (last_first == LAST_TO_FIRST) ? (dest_order = 0, src_order = nr_src - cpy_num + 1) : + (dest_order = nr_dest, src_order = 0); + + /* prepare space for cpy_num pointers */ + dc = B_N_CHILD (dest, dest_order); + + memmove (dc + cpy_num, dc, (nr_dest - dest_order) * DC_SIZE); + + /* insert pointers */ + memcpy (dc, B_N_CHILD (src, src_order), DC_SIZE * cpy_num); + + + /* prepare space for cpy_num - 1 item headers */ + key = B_N_PDELIM_KEY(dest, dest_order); + memmove (key + cpy_num - 1, key, + KEY_SIZE * (nr_dest - dest_order) + DC_SIZE * (nr_dest + cpy_num)); + + + /* insert headers */ + memcpy (key, B_N_PDELIM_KEY (src, src_order), KEY_SIZE * (cpy_num - 1)); + + /* sizes, item number */ + blkh->blk_nr_item += cpy_num - 1; + blkh->blk_free_space -= KEY_SIZE * (cpy_num - 1) + DC_SIZE * cpy_num; + + mark_buffer_dirty (dest); + if (dest_bi->bi_parent) { + B_N_CHILD(dest_bi->bi_parent,dest_bi->bi_position)->dc_size += + KEY_SIZE * (cpy_num - 1) + DC_SIZE * cpy_num; + mark_buffer_dirty (dest_bi->bi_parent); + } + +} + + +/* Copy cpy_num node pointers and cpy_num - 1 items from buffer src to buffer dest. + * Delete cpy_num - del_par items and node pointers from buffer src. + * last_first == FIRST_TO_LAST means, that we copy/delete first items from src. + * last_first == LAST_TO_FIRST means, that we copy/delete last items from src. + */ +static void internal_move_pointers_items (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, + struct buffer_info * src_bi, + int last_first, int cpy_num, int del_par) +{ + int first_pointer; + int first_item; + + internal_copy_pointers_items (fs, dest_bi, src_bi->bi_bh, last_first, cpy_num); + + if (last_first == FIRST_TO_LAST) { /* shift_left occurs */ + first_pointer = 0; + first_item = 0; + /* delete cpy_num - del_par pointers and keys starting for pointers with first_pointer, + for key - with first_item */ + internal_delete_pointers_items (fs, src_bi, first_pointer, first_item, cpy_num - del_par); + } else { /* shift_right occurs */ + int i, j; + + i = ( cpy_num - del_par == ( j = B_NR_ITEMS(src_bi->bi_bh)) + 1 ) ? 0 : j - cpy_num + del_par; + + internal_delete_pointers_items (fs, src_bi, j + 1 - cpy_num + del_par, i, cpy_num - del_par); + } +} + +/* Insert n_src'th key of buffer src before n_dest'th key of buffer dest. */ +static void internal_insert_key (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, + int dest_position_before, /* insert key before key with n_dest number */ + struct buffer_head * src, + int src_position ) +{ + struct buffer_head * dest = dest_bi->bi_bh; + int nr; + struct block_head * blkh; + struct key * key; + + +#ifdef CONFIG_REISERFS_CHECK + if (dest == NULL || src == NULL) + reiserfs_panic (0, "internal_insert_key", "sourse(%p) or dest(%p) buffer is 0", src, dest); + + if (dest_position_before < 0 || src_position < 0) + reiserfs_panic (0, "internal_insert_key", "source(%d) or dest(%d) key number less than 0", + src_position, dest_position_before); + + if (dest_position_before > B_NR_ITEMS (dest) || src_position >= B_NR_ITEMS(src)) + reiserfs_panic (0, "internal_insert_key", + "invalid position in dest (%d (key number %d)) or in src (%d (key number %d))", + dest_position_before, B_NR_ITEMS (dest), src_position, B_NR_ITEMS(src)); + + if (B_BLK_HEAD(dest)->blk_free_space < KEY_SIZE) + reiserfs_panic (0, "internal_insert_key", + "no enough free space (%d) in dest buffer", B_BLK_HEAD(dest)->blk_free_space); +#endif + + nr = (blkh=B_BLK_HEAD(dest))->blk_nr_item; + + /* prepare space for inserting key */ + key = B_N_PDELIM_KEY (dest, dest_position_before); + memmove (key + 1, key, (nr - dest_position_before) * KEY_SIZE + (nr + 1) * DC_SIZE); + + /* insert key */ + memcpy (key, B_N_PDELIM_KEY(src, src_position), KEY_SIZE); + + /* Change dirt, free space, item number fields. */ + blkh->blk_nr_item ++; + blkh->blk_free_space -= KEY_SIZE; + + mark_buffer_dirty (dest); + + if (dest_bi->bi_parent) { + B_N_CHILD(dest_bi->bi_parent,dest_bi->bi_position)->dc_size += KEY_SIZE; + mark_buffer_dirty (dest_bi->bi_parent); + } +} + + + +/* Insert d_key'th (delimiting) key from buffer cfl to tail of dest. + * Copy pointer_amount node pointers and pointer_amount - 1 items from buffer src to buffer dest. + * Replace d_key'th key in buffer cfl. + * Delete pointer_amount items and node pointers from buffer src. + */ +/* this can be invoked both to shift from S to L and from R to S */ +static void internal_shift_left (int mode, /* INTERNAL_FROM_S_TO_L | INTERNAL_FROM_R_TO_S */ + struct tree_balance * tb, int h, + int pointer_amount) +{ + struct buffer_info dest_bi, src_bi; + struct buffer_head * cf; + int d_key_position; + + internal_define_dest_src_infos (mode, tb, h, &dest_bi, &src_bi, &d_key_position, &cf); + + /*printk("pointer_amount = %d\n",pointer_amount);*/ + + if (pointer_amount) { + /* insert delimiting key from common father of dest and src to node dest into position B_NR_ITEM(dest) */ + internal_insert_key (tb->tb_sb, &dest_bi, B_NR_ITEMS(dest_bi.bi_bh), cf, d_key_position); + + if (B_NR_ITEMS(src_bi.bi_bh) == pointer_amount - 1) { + if (src_bi.bi_position/*src->b_item_order*/ == 0) + replace_key (tb->tb_sb, cf, d_key_position, src_bi.bi_parent/*src->b_parent*/, 0); + } else + replace_key (tb->tb_sb, cf, d_key_position, src_bi.bi_bh, pointer_amount - 1); + } + /* last parameter is del_parameter */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, FIRST_TO_LAST, pointer_amount, 0); + +} + +/* Insert delimiting key to L[h]. + * Copy n node pointers and n - 1 items from buffer S[h] to L[h]. + * Delete n - 1 items and node pointers from buffer S[h]. + */ +/* it always shifts from S[h] to L[h] */ +static void internal_shift1_left (struct tree_balance * tb, + int h, int pointer_amount) +{ + struct buffer_info dest_bi, src_bi; + struct buffer_head * cf; + int d_key_position; + + internal_define_dest_src_infos (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, &dest_bi, &src_bi, &d_key_position, &cf); + + if ( pointer_amount > 0 ) /* insert lkey[h]-th key from CFL[h] to left neighbor L[h] */ + internal_insert_key (tb->tb_sb, &dest_bi, B_NR_ITEMS(dest_bi.bi_bh), cf, d_key_position); + + /* last parameter is del_parameter */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, FIRST_TO_LAST, pointer_amount, 1); +} + + +/* Insert d_key'th (delimiting) key from buffer cfr to head of dest. + * Copy n node pointers and n - 1 items from buffer src to buffer dest. + * Replace d_key'th key in buffer cfr. + * Delete n items and node pointers from buffer src. + */ +static void internal_shift_right (int mode, /* INTERNAL_FROM_S_TO_R | INTERNAL_FROM_L_TO_S */ + struct tree_balance * tb, int h, + int pointer_amount) +{ + struct buffer_info dest_bi, src_bi; + struct buffer_head * cf; + int d_key_position; + int nr; + + + internal_define_dest_src_infos (mode, tb, h, &dest_bi, &src_bi, &d_key_position, &cf); + + nr = B_NR_ITEMS (src_bi.bi_bh); + + if (pointer_amount > 0) { + /* insert delimiting key from common father of dest and src to dest node into position 0 */ + internal_insert_key (tb->tb_sb, &dest_bi, 0, cf, d_key_position); + if (nr == pointer_amount - 1) { +#ifdef CONFIG_REISERFS_CHECK + if ( src_bi.bi_bh != PATH_H_PBUFFER (tb->tb_path, h)/*tb->S[h]*/ || dest_bi.bi_bh != tb->R[h]) + reiserfs_panic (tb->tb_sb, "internal_shift_right", "src (%p) must be == tb->S[h](%p) when it disappears", + src_bi.bi_bh, PATH_H_PBUFFER (tb->tb_path, h)); +#endif + /* when S[h] disappers replace left delemiting key as well */ + if (tb->CFL[h]) + replace_key(tb->tb_sb, cf, d_key_position, tb->CFL[h], tb->lkey[h]); + } else + replace_key(tb->tb_sb, cf, d_key_position, src_bi.bi_bh, nr - pointer_amount); + } + + /* last parameter is del_parameter */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, LAST_TO_FIRST, pointer_amount, 0); +} + +/* Insert delimiting key to R[h]. + * Copy n node pointers and n - 1 items from buffer S[h] to R[h]. + * Delete n - 1 items and node pointers from buffer S[h]. + */ +/* it always shift from S[h] to R[h] */ +static void internal_shift1_right (struct tree_balance * tb, + int h, int pointer_amount) +{ + struct buffer_info dest_bi, src_bi; + struct buffer_head * cf; + int d_key_position; + + internal_define_dest_src_infos (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, &dest_bi, &src_bi, &d_key_position, &cf); + + if (pointer_amount > 0) /* insert rkey from CFR[h] to right neighbor R[h] */ + internal_insert_key (tb->tb_sb, &dest_bi, 0, cf, d_key_position); + + /* last parameter is del_parameter */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, LAST_TO_FIRST, pointer_amount, 1); +} + + +/* Delete insert_num node pointers together with their left items + * and balance current node.*/ +static void balance_internal_when_delete (struct tree_balance * tb, + int h, int child_pos) +{ + int insert_num; + int n; + struct buffer_head * tbSh = PATH_H_PBUFFER (tb->tb_path, h); + struct buffer_info bi; + + insert_num = tb->insert_size[h] / ((int)(DC_SIZE + KEY_SIZE)); + + /* delete child-node-pointer(s) together with their left item(s) */ + bi.bi_bh = tbSh; + + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h); + + bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + + internal_delete_childs (tb->tb_sb, &bi, child_pos, -insert_num); + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->blknum[h] > 1 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "tb->blknum[%d]=%d when insert_size < 0", + h, tb->blknum[h]); +#endif /* CONFIG_REISERFS_CHECK */ + + n = B_NR_ITEMS(tbSh); + + if ( tb->lnum[h] == 0 && tb->rnum[h] == 0 ) { + if ( tb->blknum[h] == 0 ) { + /* node S[h] (root of the tree) is empty now */ + struct buffer_head *new_root; + +#ifdef CONFIG_REISERFS_CHECK + if (n || B_BLK_HEAD (tbSh)->blk_free_space != MAX_CHILD_SIZE(tbSh) - DC_SIZE) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "buffer must have only 0 keys (%d)", + n); + + if (bi.bi_parent) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "root has parent (%p)", bi.bi_parent); +#endif /* CONFIG_REISERFS_CHECK */ + + /* choose a new root */ + if ( ! tb->L[h-1] || ! B_NR_ITEMS(tb->L[h-1]) ) + new_root = tb->R[h-1]; + else + new_root = tb->L[h-1]; + + /* update super block's tree height and pointer to a root block */ + tb->tb_sb->s_rs->s_v1.s_root_block = cpu_to_le32 (new_root->b_blocknr); + tb->tb_sb->s_rs->s_v1.s_tree_height = SB_TREE_HEIGHT (tb->tb_sb) - 1; + + mark_buffer_dirty (tb->tb_sb->s_sbh); + tb->tb_sb->s_dirt = 1; + + /* mark buffer S[h] not uptodate and put it in free list */ + reiserfs_invalidate_buffer(tb, tbSh, 1); + return; + } + return; + } + + if ( tb->L[h] && tb->lnum[h] == -B_NR_ITEMS(tb->L[h]) - 1 ) { /* join S[h] with L[h] */ + +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rnum[h] != 0 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "invalid tb->rnum[%d]==%d when joining S[h] with L[h]", + h, tb->rnum[h]); +#endif /* CONFIG_REISERFS_CHECK */ + + internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, n + 1);/*tb->L[h], tb->CFL[h], tb->lkey[h], tb->S[h], n+1);*/ + reiserfs_invalidate_buffer(tb, tbSh, 1); /* preserve not needed, internal, 1 mean free block */ + + return; + } + + if ( tb->R[h] && tb->rnum[h] == -B_NR_ITEMS(tb->R[h]) - 1 ) { /* join S[h] with R[h] */ +#ifdef CONFIG_REISERFS_CHECK + if ( tb->lnum[h] != 0 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "invalid tb->lnum[%d]==%d when joining S[h] with R[h]", + h, tb->lnum[h]); +#endif /* CONFIG_REISERFS_CHECK */ + + internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, n + 1); + reiserfs_invalidate_buffer (tb, tbSh, 1); + return; + } + + if ( tb->lnum[h] < 0 ) { /* borrow from left neighbor L[h] */ +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rnum[h] != 0 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "invalid tb->rnum[%d]==%d when borrow from L[h]", + h, tb->rnum[h]); +#endif /* CONFIG_REISERFS_CHECK */ + + internal_shift_right (INTERNAL_SHIFT_FROM_L_TO_S, tb, h, -tb->lnum[h]); + return; + } + + if ( tb->rnum[h] < 0 ) { /* borrow from right neighbor R[h] */ +#ifdef CONFIG_REISERFS_CHECK + if ( tb->lnum[h] != 0 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", "invalid tb->lnum[%d]==%d when borrow from R[h]", + h, tb->lnum[h]); +#endif /* CONFIG_REISERFS_CHECK */ + internal_shift_left (INTERNAL_SHIFT_FROM_R_TO_S, tb, h, -tb->rnum[h]);/*tb->S[h], tb->CFR[h], tb->rkey[h], tb->R[h], -tb->rnum[h]);*/ + return; + } + + if ( tb->lnum[h] > 0 ) { /* split S[h] into two parts and put them into neighbors */ +#ifdef CONFIG_REISERFS_CHECK + if ( tb->rnum[h] == 0 || tb->lnum[h] + tb->rnum[h] != n + 1 ) + reiserfs_panic (tb->tb_sb, "balance_internal_when_delete", + "invalid tb->lnum[%d]==%d or tb->rnum[%d]==%d when S[h](item number == %d) is split between them", + h, tb->lnum[h], h, tb->rnum[h], n); +#endif /* CONFIG_REISERFS_CHECK */ + + internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h]);/*tb->L[h], tb->CFL[h], tb->lkey[h], tb->S[h], tb->lnum[h]);*/ + internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h]); + reiserfs_invalidate_buffer (tb, tbSh, 1); + + return; + } + reiserfs_panic ("balance_internal_when_delete", "unexpected tb->lnum[%d]==%d or tb->rnum[%d]==%d", + h, tb->lnum[h], h, tb->rnum[h]); +} + + +/* Replace delimiting key of buffers L[h] and S[h] by the given key.*/ +void replace_lkey (struct tree_balance * tb, + int h, struct item_head * key) +{ +#ifdef CONFIG_REISERFS_CHECK + if (tb->L[h] == NULL || tb->CFL[h] == NULL) + reiserfs_panic (tb->tb_sb, "replace_lkey: 12255: " + "L[h](%p) and CFL[h](%p) must exist in replace_lkey", tb->L[h], tb->CFL[h]); +#endif + + if (B_NR_ITEMS(PATH_H_PBUFFER(tb->tb_path, h)) == 0) + return; + + memcpy (B_N_PDELIM_KEY(tb->CFL[h],tb->lkey[h]), key, KEY_SIZE); + + mark_buffer_dirty (tb->CFL[h]); +} + + +/* Replace delimiting key of buffers S[h] and R[h] by the given key.*/ +void replace_rkey (struct tree_balance * tb, + int h, struct item_head * key) +{ +#ifdef CONFIG_REISERFS_CHECK + if (tb->R[h] == NULL || tb->CFR[h] == NULL) + reiserfs_panic (tb->tb_sb, "replace_rkey: 12260: " + "R[h](%p) and CFR[h](%p) must exist in replace_rkey", tb->R[h], tb->CFR[h]); + + if (B_NR_ITEMS(tb->R[h]) == 0) + reiserfs_panic (tb->tb_sb, "replace_rkey: 12265: " + "R[h] can not be empty if it exists (item number=%d)", B_NR_ITEMS(tb->R[h])); +#endif + + memcpy (B_N_PDELIM_KEY(tb->CFR[h],tb->rkey[h]), key, KEY_SIZE); + + mark_buffer_dirty (tb->CFR[h]); +} + + + +int balance_internal (struct tree_balance * tb, /* tree_balance structure */ + int h, /* level of the tree */ + int child_pos, + struct item_head * insert_key, /* key for insertion on higher level */ + struct buffer_head ** insert_ptr) /* node for insertion on higher level*/ + /* if inserting/pasting + { + child_pos is the position of the node-pointer in S[h] that * + pointed to S[h-1] before balancing of the h-1 level; * + this means that new pointers and items must be inserted AFTER * + child_pos + } + else + { + it is the position of the leftmost pointer that must be deleted (together with + its corresponding key to the left of the pointer) + as a result of the previous level's balancing. + } +*/ +{ + struct buffer_head * tbSh = PATH_H_PBUFFER (tb->tb_path, h); + struct buffer_info bi; + int order; /* we return this: it is 0 if there is no S[h], else it is tb->S[h]->b_item_order */ + int insert_num, n, k; + struct buffer_head * S_new; + struct item_head new_insert_key; + struct buffer_head * new_insert_ptr = NULL; + struct item_head * new_insert_key_addr = insert_key; + +#ifdef CONFIG_REISERFS_CHECK + if ( h < 1 ) + reiserfs_panic (tb->tb_sb, "balance_internal", "h (%d) can not be < 1 on internal level", h); +#endif /* CONFIG_REISERFS_CHECK */ + + order = ( tbSh ) ? PATH_H_POSITION (tb->tb_path, h + 1)/*tb->S[h]->b_item_order*/ : 0; + + /* Using insert_size[h] calculate the number insert_num of items + that must be inserted to or deleted from S[h]. */ + insert_num = tb->insert_size[h]/((int)(KEY_SIZE + DC_SIZE)); + + /* Check whether insert_num is proper **/ +#ifdef CONFIG_REISERFS_CHECK + if ( insert_num < -2 || insert_num > 2 ) + reiserfs_panic (tb->tb_sb, "balance_internal", + "incorrect number of items inserted to the internal node (%d)", insert_num); + + if ( h > 1 && (insert_num > 1 || insert_num < -1) ) + reiserfs_panic (tb->tb_sb, "balance_internal", + "incorrect number of items (%d) inserted to the internal node on a level (h=%d) higher than last internal level", + insert_num, h); +#endif /* CONFIG_REISERFS_CHECK */ + + /* Make balance in case insert_num < 0 */ + if ( insert_num < 0 ) { + balance_internal_when_delete (tb, h, child_pos); + return order; + } + + k = 0; + if ( tb->lnum[h] > 0 ) { + /* shift lnum[h] items from S[h] to the left neighbor L[h]. + check how many of new items fall into L[h] or CFL[h] after shifting */ + n = B_BLK_HEAD(tb->L[h])->blk_nr_item; /* number of items in L[h] */ + if ( tb->lnum[h] <= child_pos ) { + /* new items don't fall into L[h] or CFL[h] */ + internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h]); + child_pos -= tb->lnum[h]; + } else if ( tb->lnum[h] > child_pos + insert_num ) { + /* all new items fall into L[h] */ + internal_shift_left (INTERNAL_SHIFT_FROM_S_TO_L, tb, h, tb->lnum[h] - insert_num); + + /* insert insert_num keys and node-pointers into L[h] */ + bi.bi_bh = tb->L[h]; + bi.bi_parent = tb->FL[h]; + bi.bi_position = get_left_neighbor_position (tb, h); + internal_insert_childs (tb->tb_sb, &bi,/*tb->L[h], tb->S[h-1]->b_next*/ n + child_pos + 1, + insert_num,insert_key,insert_ptr); + + insert_num = 0; + } else { + struct disk_child * dc; + + /* some items fall into L[h] or CFL[h], but some don't fall */ + internal_shift1_left (tb, h, child_pos + 1); + /* calculate number of new items that fall into L[h] */ + k = tb->lnum[h] - child_pos - 1; + + bi.bi_bh = tb->L[h]; + bi.bi_parent = tb->FL[h]; + bi.bi_position = get_left_neighbor_position (tb, h); + internal_insert_childs (tb->tb_sb, &bi,/*tb->L[h], tb->S[h-1]->b_next,*/ n + child_pos + 1,k, + insert_key,insert_ptr); + + replace_lkey(tb, h, insert_key + k); + + /* replace the first node-ptr in S[h] by node-ptr to insert_ptr[k] */ + (dc = B_N_CHILD(tbSh, 0))->dc_size = + MAX_CHILD_SIZE(insert_ptr[k]) - + B_BLK_HEAD(insert_ptr[k])->blk_free_space; + dc->dc_block_number = insert_ptr[k]->b_blocknr; + + mark_buffer_dirty (tbSh); + + k++; + insert_key += k; + insert_ptr += k; + insert_num -= k; + child_pos = 0; + } + } /* tb->lnum[h] > 0 */ + + if ( tb->rnum[h] > 0 ) { + /*shift rnum[h] items from S[h] to the right neighbor R[h]*/ + /* check how many of new items fall into R or CFR after shifting */ + n = B_BLK_HEAD (tbSh)->blk_nr_item; /* number of items in S[h] */ + if ( n - tb->rnum[h] >= child_pos ) + /* new items fall into S[h] */ + /*internal_shift_right(tb,h,tbSh,tb->CFR[h],tb->rkey[h],tb->R[h],tb->rnum[h]);*/ + internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h]); + else + if ( n + insert_num - tb->rnum[h] < child_pos ) + { + /* all new items fall into R[h] */ + internal_shift_right (INTERNAL_SHIFT_FROM_S_TO_R, tb, h, tb->rnum[h] - insert_num); + + /* insert insert_num keys and node-pointers into R[h] */ + bi.bi_bh = tb->R[h]; + bi.bi_parent = tb->FR[h]; + bi.bi_position = get_right_neighbor_position (tb, h); + internal_insert_childs (tb->tb_sb, &bi, /*tb->R[h],tb->S[h-1]->b_next*/ child_pos - n - insert_num + tb->rnum[h] - 1, + insert_num,insert_key,insert_ptr); + insert_num = 0; + } + else + { + struct disk_child * dc; + + /* one of the items falls into CFR[h] */ + internal_shift1_right(tb, h, n - child_pos + 1); + /* calculate number of new items that fall into R[h] */ + k = tb->rnum[h] - n + child_pos - 1; + + bi.bi_bh = tb->R[h]; + bi.bi_parent = tb->FR[h]; + bi.bi_position = get_right_neighbor_position (tb, h); + internal_insert_childs (tb->tb_sb, &bi, /*tb->R[h], tb->R[h]->b_child,*/ 0, k, insert_key + 1, insert_ptr + 1); + + replace_rkey(tb, h, insert_key + insert_num - k - 1); + + /* replace the first node-ptr in R[h] by node-ptr insert_ptr[insert_num-k-1]*/ + (dc = B_N_CHILD(tb->R[h], 0))->dc_size = + MAX_CHILD_SIZE(insert_ptr[insert_num-k-1]) - + B_BLK_HEAD(insert_ptr[insert_num-k-1])->blk_free_space; + dc->dc_block_number = insert_ptr[insert_num-k-1]->b_blocknr; + + mark_buffer_dirty (tb->R[h]); + + insert_num -= (k + 1); + } + } + + /** Fill new node that appears instead of S[h] **/ +#ifdef CONFIG_REISERFS_CHECK + if ( tb->blknum[h] > 2 ) + reiserfs_panic(0, "balance_internal", "blknum can not be > 2 for internal level"); + if ( tb->blknum[h] < 0 ) + reiserfs_panic(0, "balance_internal", "blknum can not be < 0"); +#endif /* CONFIG_REISERFS_CHECK */ + + if ( ! tb->blknum[h] ) + { /* node S[h] is empty now */ +#ifdef CONFIG_REISERFS_CHECK + if ( ! tbSh ) + reiserfs_panic(0,"balance_internal", "S[h] is equal NULL"); +#endif /* CONFIG_REISERFS_CHECK */ + + /* Mark buffer as invalid and put it to head of free list. */ + reiserfs_invalidate_buffer(tb, tbSh, 1);/* do not preserve, internal node*/ + return order; + } + + if ( ! tbSh ) { + /* create new root */ + struct disk_child * dc; + struct buffer_head * tbSh_1 = PATH_H_PBUFFER (tb->tb_path, h - 1); + + + if ( tb->blknum[h] != 1 ) + reiserfs_panic(0, "balance_internal", "One new node required for creating the new root"); + /* S[h] = empty buffer from the list FEB. */ + tbSh = get_FEB (tb); + B_BLK_HEAD(tbSh)->blk_level = h + 1; + + /* Put the unique node-pointer to S[h] that points to S[h-1]. */ + + (dc = B_N_CHILD(tbSh, 0))->dc_block_number = tbSh_1->b_blocknr; + dc->dc_size = MAX_CHILD_SIZE (tbSh_1) - B_BLK_HEAD(tbSh_1)->blk_free_space; + + tb->insert_size[h] -= DC_SIZE; + B_BLK_HEAD(tbSh)->blk_free_space -= DC_SIZE; + + mark_buffer_dirty (tbSh); + + /* put new root into path structure */ + PATH_OFFSET_PBUFFER(tb->tb_path, ILLEGAL_PATH_ELEMENT_OFFSET) = tbSh; + + /* Change root in structure super block. */ + tb->tb_sb->s_rs->s_v1.s_root_block = cpu_to_le32 (tbSh->b_blocknr); + tb->tb_sb->s_rs->s_v1.s_tree_height = cpu_to_le16 (SB_TREE_HEIGHT (tb->tb_sb) + 1); + + mark_buffer_dirty (tb->tb_sb->s_sbh); + tb->tb_sb->s_dirt = 1; + } + + if ( tb->blknum[h] == 2 ) { + int snum; + struct buffer_info dest_bi, src_bi; + + + /* S_new = free buffer from list FEB */ + S_new = get_FEB(tb); + + B_BLK_HEAD(S_new)->blk_level = h + 1; + + + dest_bi.bi_bh = S_new; + dest_bi.bi_parent = 0; + dest_bi.bi_position = 0; + src_bi.bi_bh = tbSh; + src_bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h); + src_bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1); + + n = B_BLK_HEAD(tbSh)->blk_nr_item; /* number of items in S[h] */ + snum = (insert_num + n + 1)/2; + if ( n - snum >= child_pos ) { + /* new items don't fall into S_new */ + /* store the delimiting key for the next level */ + /* new_insert_key = (n - snum)'th key in S[h] */ + memcpy (&new_insert_key,B_N_PDELIM_KEY(tbSh,n - snum), + KEY_SIZE); + /* last parameter is del_par */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, LAST_TO_FIRST, snum, 0); + } else if ( n + insert_num - snum < child_pos ) { + /* all new items fall into S_new */ + /* store the delimiting key for the next level */ + /* new_insert_key = (n + insert_item - snum)'th key in S[h] */ + memcpy(&new_insert_key,B_N_PDELIM_KEY(tbSh,n + insert_num - snum), + KEY_SIZE); + /* last parameter is del_par */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, LAST_TO_FIRST, snum - insert_num, 0); + /* internal_move_pointers_items(S_new,tbSh,1,snum - insert_num,0);*/ + + /* insert insert_num keys and node-pointers into S_new */ + internal_insert_childs (tb->tb_sb, &dest_bi, /*S_new,tb->S[h-1]->b_next,*/child_pos - n - insert_num + snum - 1, + insert_num,insert_key,insert_ptr); + + insert_num = 0; + } else { + struct disk_child * dc; + + /* some items fall into S_new, but some don't fall */ + /* last parameter is del_par */ + internal_move_pointers_items (tb->tb_sb, &dest_bi, &src_bi, LAST_TO_FIRST, n - child_pos + 1, 1); + /* internal_move_pointers_items(S_new,tbSh,1,n - child_pos + 1,1);*/ + /* calculate number of new items that fall into S_new */ + k = snum - n + child_pos - 1; + + internal_insert_childs (tb->tb_sb, &dest_bi, /*S_new,*/ 0, k, insert_key + 1, insert_ptr+1); + + /* new_insert_key = insert_key[insert_num - k - 1] */ + memcpy(&new_insert_key,insert_key + insert_num - k - 1, + KEY_SIZE); + /* replace first node-ptr in S_new by node-ptr to insert_ptr[insert_num-k-1] */ + + (dc = B_N_CHILD(S_new,0))->dc_size = + MAX_CHILD_SIZE(insert_ptr[insert_num-k-1]) - + B_BLK_HEAD(insert_ptr[insert_num-k-1])->blk_free_space; + dc->dc_block_number = insert_ptr[insert_num-k-1]->b_blocknr; + + mark_buffer_dirty (S_new); + + insert_num -= (k + 1); + } + /* new_insert_ptr = node_pointer to S_new */ + new_insert_ptr = S_new; + +#ifdef CONFIG_REISERFS_CHECK + if ( buffer_locked(S_new) ) + reiserfs_panic (tb->tb_sb, "balance_internal", "locked buffer S_new[]"); + if (S_new->b_count != 1) + if (!(buffer_journaled(S_new) && S_new->b_count == 2)) { + printk ("REISERFS: balance_internal: S_new->b_count != 1 (%u)\n", S_new->b_count); + } +#endif /* CONFIG_REISERFS_CHECK */ + + /* + S_new->b_count --; + */ + /*brelse(S_new);*/ + } + + n = B_BLK_HEAD(tbSh)->blk_nr_item; /*number of items in S[h] */ + +#ifndef FU //REISERFS_FSCK + if ( -1 <= child_pos && child_pos <= n && insert_num > 0 ) { +#else + if ( 0 <= child_pos && child_pos <= n && insert_num > 0 ) { +#endif + bi.bi_bh = tbSh; + bi.bi_parent = PATH_H_PPARENT (tb->tb_path, h); + bi.bi_position = PATH_H_POSITION (tb->tb_path, h + 1); +#ifndef FU //REISERFS_FSCK + if (child_pos == -1) { + /* this is a little different from original do_balance: + here we insert the minimal keys in the tree, that has never happened when file system works */ + if (tb->CFL[h-1] || insert_num != 1 || h != 1) + die ("balance_internal: invalid child_pos"); +/* insert_child (tb->S[h], tb->S[h-1], child_pos, insert_num, B_N_ITEM_HEAD(tb->S[0],0), insert_ptr);*/ + internal_insert_childs (tb->tb_sb, &bi, child_pos, insert_num, B_N_PITEM_HEAD (PATH_PLAST_BUFFER (tb->tb_path), 0), insert_ptr); + } else +#endif + internal_insert_childs (tb->tb_sb, + &bi, child_pos,insert_num,insert_key,insert_ptr); + } + + + memcpy (new_insert_key_addr,&new_insert_key,KEY_SIZE); + insert_ptr[0] = new_insert_ptr; + + return order; + } + diff --git a/reiserfscore/includes.h b/reiserfscore/includes.h new file mode 100644 index 0000000..b26aec9 --- /dev/null +++ b/reiserfscore/includes.h @@ -0,0 +1,18 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <asm/types.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <malloc.h> +#include <sys/vfs.h> +#include <unistd.h> +#include <time.h> +#include "io.h" +#include "misc.h" +#include "reiserfs_lib.h" diff --git a/reiserfscore/lbalance.c b/reiserfscore/lbalance.c new file mode 100644 index 0000000..c5036fc --- /dev/null +++ b/reiserfscore/lbalance.c @@ -0,0 +1,1367 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +#include "includes.h" + + +/* these are used in do_balance.c */ + +/* leaf_move_items + leaf_shift_left + leaf_shift_right + leaf_delete_items + leaf_insert_into_buf + leaf_paste_in_buffer + leaf_cut_from_buffer + leaf_paste_entries + */ + + +extern struct tree_balance init_tb; +extern int init_item_pos; +extern int init_pos_in_item; +extern int init_mode; + + + +/* copy copy_count entries from source directory item to dest buffer (creating new item if needed) */ +static void leaf_copy_dir_entries (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, struct buffer_head * source, + int last_first, int item_num, int from, int copy_count) +{ + struct buffer_head * dest = dest_bi->bi_bh; + int item_num_in_dest; /* either the number of target item, + or if we must create a new item, + the number of the item we will + create it next to */ + struct item_head * ih; + struct reiserfs_de_head * deh; + int copy_records_len; /* length of all records in item to be copied */ + char * records; + + ih = B_N_PITEM_HEAD (source, item_num); + +#ifdef CONFIG_REISERFS_CHECK + if (!I_IS_DIRECTORY_ITEM (ih)) + reiserfs_panic(0, "vs-10000: leaf_copy_dir_entries: item must be directory item"); +#endif + + /* length of all record to be copied and first byte of the last of them */ + deh = B_I_DEH (source, ih); + if (copy_count) { + copy_records_len = (from ? deh[from - 1].deh_location : ih->ih_item_len) - + deh[from + copy_count - 1].deh_location; + records = source->b_data + ih->ih_item_location + deh[from + copy_count - 1].deh_location; + } else { + copy_records_len = 0; + records = 0; + } + + /* when copy last to first, dest buffer can contain 0 items */ + item_num_in_dest = (last_first == LAST_TO_FIRST) ? (( B_NR_ITEMS(dest) ) ? 0 : -1) : (B_NR_ITEMS(dest) - 1); + + /* if there are no items in dest or the first/last item in dest is not item of the same directory */ + if ( (item_num_in_dest == - 1) || +#ifndef FU //REISERFS_FSCK + (last_first == FIRST_TO_LAST && are_items_mergeable (B_N_PITEM_HEAD (dest, item_num_in_dest), ih, dest->b_size) == 0) || + (last_first == LAST_TO_FIRST && are_items_mergeable (ih, B_N_PITEM_HEAD (dest, item_num_in_dest), dest->b_size) == 0)) { +#else + (last_first == FIRST_TO_LAST && get_offset (&ih->ih_key) == DOT_OFFSET) || + (last_first == LAST_TO_FIRST && not_of_one_file (&ih->ih_key, B_N_PKEY (dest, item_num_in_dest)))) { +#endif + /* create new item in dest */ + struct item_head new_ih; + + /* form item header */ + memcpy (&new_ih.ih_key, &ih->ih_key, KEY_SIZE); + + /* calculate item len */ + new_ih.ih_item_len = cpu_to_le16 (DEH_SIZE * copy_count + copy_records_len); + set_entry_count (&new_ih, 0); + + if (last_first == LAST_TO_FIRST) { + /* form key by the following way */ + if (from < ih_entry_count (ih)) { + new_ih.ih_key.u.k_offset_v1.k_offset = deh[from].deh_offset; + new_ih.ih_key.u.k_offset_v1.k_uniqueness = DIRENTRY_UNIQUENESS; + /*memcpy (&new_ih.ih_key.k_offset, &deh[from].deh_offset, SHORT_KEY_SIZE);*/ + } else { + /* no entries will be copied to this item in this function */ + new_ih.ih_key.u.k_offset_v1.k_offset = MAX_KEY1_OFFSET; + /* this item is not yet valid, but we want I_IS_DIRECTORY_ITEM to return 1 for it, so we -1 */ + new_ih.ih_key.u.k_offset_v1.k_uniqueness = DIRENTRY_UNIQUENESS/*TYPE_DIRECTORY_MAX*/; + } + } + set_key_format (&new_ih, ih_key_format (ih)); + new_ih.ih_format.fsck_need = ih->ih_format.fsck_need; + + /* insert item into dest buffer */ + leaf_insert_into_buf (fs, dest_bi, (last_first == LAST_TO_FIRST) ? 0 : B_NR_ITEMS(dest), &new_ih, NULL, 0); + } else { + /* prepare space for entries */ + leaf_paste_in_buffer (fs, dest_bi, (last_first==FIRST_TO_LAST) ? (B_NR_ITEMS(dest) - 1) : 0, USHRT_MAX, + DEH_SIZE * copy_count + copy_records_len, records, 0); + } + + item_num_in_dest = (last_first == FIRST_TO_LAST) ? (B_NR_ITEMS(dest)-1) : 0; + + leaf_paste_entries (dest_bi->bi_bh, item_num_in_dest, + (last_first == FIRST_TO_LAST) ? ih_entry_count (B_N_PITEM_HEAD (dest, item_num_in_dest)) : 0, + copy_count, deh + from, records, + DEH_SIZE * copy_count + copy_records_len + ); +} + + +/* Copy the first (if last_first == FIRST_TO_LAST) or last (last_first == LAST_TO_FIRST) item or + part of it or nothing (see the return 0 below) from SOURCE to the end + (if last_first) or beginning (!last_first) of the DEST */ +/* returns 1 if anything was copied, else 0 */ +static int leaf_copy_boundary_item (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, struct buffer_head * src, int last_first, + int bytes_or_entries) +{ + struct buffer_head * dest = dest_bi->bi_bh; + int dest_nr_item, src_nr_item; /* number of items in the source and destination buffers */ + struct item_head * ih; + struct item_head * dih; + + dest_nr_item = B_NR_ITEMS(dest); + + if ( last_first == FIRST_TO_LAST ) { + /* if ( DEST is empty or first item of SOURCE and last item of DEST are the items of different objects + or of different types ) then there is no need to treat this item differently from the other items + that we copy, so we return */ + ih = B_N_PITEM_HEAD (src, 0); + dih = B_N_PITEM_HEAD (dest, dest_nr_item - 1); +#ifndef FU //REISERFS_FSCK + if (!dest_nr_item || (are_items_mergeable (dih, ih, src->b_size) == 0)) +#else + if (!dest_nr_item || (!is_left_mergeable (ih, src->b_size))) +#endif + /* there is nothing to merge */ + return 0; + +#ifdef CONFIG_REISERFS_CHECK + if ( ! ih->ih_item_len ) + reiserfs_panic (0, "vs-10010: leaf_copy_boundary_item: item can not have empty dynamic length"); +#endif + + if ( I_IS_DIRECTORY_ITEM(ih) ) { + if ( bytes_or_entries == -1 ) + /* copy all entries to dest */ + bytes_or_entries = ih_entry_count(ih); + leaf_copy_dir_entries (fs, dest_bi, src, FIRST_TO_LAST, 0, 0, bytes_or_entries); + return 1; + } + + /* copy part of the body of the first item of SOURCE to the end of the body of the last item of the DEST + part defined by 'bytes_or_entries'; if bytes_or_entries == -1 copy whole body; don't create new item header + */ + if ( bytes_or_entries == -1 ) + bytes_or_entries = ih->ih_item_len; + +#ifdef CONFIG_REISERFS_CHECK + else { + if (bytes_or_entries == ih->ih_item_len && I_IS_INDIRECT_ITEM(ih)) + if (ih_free_space (ih)) + reiserfs_panic (0, "vs-10020: leaf_copy_boundary_item: " + "last unformatted node must be filled entirely (free_space=%d)", + ih_free_space (ih)); + } +#endif + + /* merge first item (or its part) of src buffer with the last + item of dest buffer. Both are of the same file */ + leaf_paste_in_buffer (fs, dest_bi, + dest_nr_item - 1, dih->ih_item_len, bytes_or_entries, B_I_PITEM(src,ih), 0 + ); + + if (I_IS_INDIRECT_ITEM(dih)) { +#ifdef CONFIG_REISERFS_CHECK + if (ih_free_space (dih)) + reiserfs_panic (0, "vs-10030: leaf_copy_boundary_item: " + "merge to left: last unformatted node of non-last indirect item must be filled entirely (free_space=%d)", + ih_free_space (dih)); +#endif + if (bytes_or_entries == ih->ih_item_len) + //dih->u.ih_free_space = ih->u.ih_free_space; + set_free_space (dih, ih_free_space (ih)); + } + + return 1; + } + + + /* copy boundary item to right (last_first == LAST_TO_FIRST) */ + + /* ( DEST is empty or last item of SOURCE and first item of DEST + are the items of different object or of different types ) + */ + src_nr_item = B_NR_ITEMS (src); + ih = B_N_PITEM_HEAD (src, src_nr_item - 1); + dih = B_N_PITEM_HEAD (dest, 0); + +#ifndef FU //REISERFS_FSCK + if (!dest_nr_item || are_items_mergeable (ih, dih, src->b_size) == 0) +#else + if (!dest_nr_item || !is_left_mergeable (dih, src->b_size)) +#endif + return 0; + + if ( I_IS_DIRECTORY_ITEM(ih)) { + if ( bytes_or_entries == -1 ) + /* bytes_or_entries = entries number in last item body of SOURCE */ + bytes_or_entries = ih_entry_count(ih); + + leaf_copy_dir_entries (fs, dest_bi, src, LAST_TO_FIRST, src_nr_item - 1, ih_entry_count(ih) - bytes_or_entries, + bytes_or_entries); + return 1; + } + + /* copy part of the body of the last item of SOURCE to the begin of the body of the first item of the DEST; + part defined by 'bytes_or_entries'; if byte_or_entriess == -1 copy whole body; change first item key of the DEST; + don't create new item header + */ + +#ifdef CONFIG_REISERFS_CHECK + if (I_IS_INDIRECT_ITEM(ih) && ih_free_space (ih)) + reiserfs_panic (0, "vs-10040: leaf_copy_boundary_item: " + "merge to right: last unformatted node of non-last indirect item must be filled entirely (free_space=%d)", + ih_free_space (ih)); +#endif + + if ( bytes_or_entries == -1 ) { + /* bytes_or_entries = length of last item body of SOURCE */ + bytes_or_entries = ih->ih_item_len; + +#ifdef CONFIG_REISERFS_CHECK + if (get_offset (&dih->ih_key) != get_offset (&ih->ih_key) + get_bytes_number (ih, src->b_size))/*I_DNM_DATA_LEN(ih))*/ + reiserfs_panic (0, "vs-10050: leaf_copy_boundary_item: right item offset (%lu) must not be (%lu),it must be %lu", + get_offset (&dih->ih_key), get_offset (&ih->ih_key) + get_bytes_number (ih, src->b_size), get_offset (&dih->ih_key)); +#endif + + /* change first item key of the DEST */ + //dih->ih_key.k_offset = ih->ih_key.k_offset; + set_offset (key_format (&dih->ih_key), &dih->ih_key, get_offset (&ih->ih_key)); + + /* item becomes non-mergeable */ + /* or mergeable if left item was */ + //dih->ih_key.k_uniqueness = ih->ih_key.k_uniqueness; + set_type (key_format (&dih->ih_key), &dih->ih_key, get_type (&ih->ih_key)); + } else { + /* merge to right only part of item */ +#ifdef CONFIG_REISERFS_CHECK + if ( ih->ih_item_len <= bytes_or_entries ) + reiserfs_panic (0, "vs-10060: leaf_copy_boundary_item: no so much bytes %lu (needed %lu)", + ih->ih_item_len, bytes_or_entries); +#endif + + /* change first item key of the DEST */ + if ( I_IS_DIRECT_ITEM(dih) ) { +#ifdef CONFIG_REISERFS_CHECK + if (get_offset (&dih->ih_key) <= (unsigned long)bytes_or_entries) + reiserfs_panic (0, "vs-10070: leaf_copy_boundary_item: dih->ih_key.k_offset(%d) <= bytes_or_entries(%d)", + get_offset (&dih->ih_key), bytes_or_entries); +#endif + //dih->ih_key.k_offset -= bytes_or_entries; + set_offset (key_format (&dih->ih_key), &dih->ih_key, get_offset (&dih->ih_key) - bytes_or_entries); + } else { +#ifdef CONFIG_REISERFS_CHECK + if (get_offset (&dih->ih_key) <=(bytes_or_entries/UNFM_P_SIZE)*dest->b_size ) + reiserfs_panic (0, "vs-10080: leaf_copy_boundary_item: dih->ih_key.k_offset(%d) <= bytes_or_entries(%d)", + get_offset (&dih->ih_key), (bytes_or_entries/UNFM_P_SIZE)*dest->b_size); +#endif + //dih->ih_key.k_offset -= ((bytes_or_entries/UNFM_P_SIZE)*dest->b_size); + set_offset (key_format (&dih->ih_key), &dih->ih_key, + get_offset (&dih->ih_key) - ((bytes_or_entries/UNFM_P_SIZE)*dest->b_size)); + } + } + + leaf_paste_in_buffer (fs, dest_bi, 0, 0, bytes_or_entries, B_I_PITEM(src,ih) + ih->ih_item_len - bytes_or_entries, 0); + return 1; +} + + +/* copy cpy_mun items from buffer src to buffer dest + * last_first == FIRST_TO_LAST means, that we copy cpy_num items beginning from first-th item in src to tail of dest + * last_first == LAST_TO_FIRST means, that we copy cpy_num items beginning from first-th item in src to head of dest + */ +static void leaf_copy_items_entirely (reiserfs_filsys_t fs, struct buffer_info * dest_bi, + struct buffer_head * src, int last_first, + int first, int cpy_num) +{ + struct buffer_head * dest; + int nr; + int dest_before; + int last_loc, last_inserted_loc, location; + int i, j; + struct block_head * blkh; + struct item_head * ih; + + +#ifdef CONFIG_REISERFS_CHECK + if (last_first != LAST_TO_FIRST && last_first != FIRST_TO_LAST) + reiserfs_panic (0, "vs-10090: leaf_copy_items_entirely: bad last_first parameter %d", last_first); + + if (B_NR_ITEMS (src) - first < cpy_num) + reiserfs_panic (0, "vs-10100: leaf_copy_items_entirely: too few items in source %d, required %d from %d", + B_NR_ITEMS(src), cpy_num, first); + + if (cpy_num < 0) + reiserfs_panic (0, "vs-10110: leaf_copy_items_entirely: can not copy negative amount of items"); + + if ( ! dest_bi ) + reiserfs_panic (0, "vs-10120: leaf_copy_items_entirely: can not copy negative amount of items"); +#endif + + dest = dest_bi->bi_bh; + +#ifdef CONFIG_REISERFS_CHECK + if ( ! dest ) + reiserfs_panic (0, "vs-10130: leaf_copy_items_entirely: can not copy negative amount of items"); +#endif + + if (cpy_num == 0) + return; + + nr = (blkh = B_BLK_HEAD(dest))->blk_nr_item; + + /* we will insert items before 0-th or nr-th item in dest buffer. It depends of last_first parameter */ + dest_before = (last_first == LAST_TO_FIRST) ? 0 : nr; + + /* location of head of first new item */ + ih = B_N_PITEM_HEAD (dest, dest_before); + +#ifdef CONFIG_REISERFS_CHECK + if (blkh->blk_free_space < cpy_num * IH_SIZE) { + reiserfs_panic (0, "vs-10140: leaf_copy_items_entirely: not enough free space for headers %d (needed %d)", + blkh->blk_free_space, cpy_num * IH_SIZE); + } +#endif + + /* prepare space for headers */ + memmove (ih + cpy_num, ih, (nr-dest_before) * IH_SIZE); + + /* copy item headers */ + memcpy (ih, B_N_PITEM_HEAD (src, first), cpy_num * IH_SIZE); + + blkh->blk_free_space -= IH_SIZE * cpy_num; + + /* location of unmovable item */ + j = location = (dest_before == 0) ? dest->b_size : (ih-1)->ih_item_location; + for (i = dest_before; i < nr + cpy_num; i ++) + ih[i-dest_before].ih_item_location = + (location -= ih[i-dest_before].ih_item_len); + + /* prepare space for items */ + last_loc = ih[nr+cpy_num-1-dest_before].ih_item_location; + last_inserted_loc = ih[cpy_num-1].ih_item_location; + + /* check free space */ +#ifdef CONFIG_REISERFS_CHECK + if (blkh->blk_free_space < j - last_inserted_loc) { + reiserfs_panic (0, "vs-10150: leaf_copy_items_entirely: not enough free space for items %d (needed %d)", + blkh->blk_free_space, j - last_inserted_loc); + } +#endif + + memmove (dest->b_data + last_loc, + dest->b_data + last_loc + j - last_inserted_loc, + last_inserted_loc - last_loc); + + /* copy items */ + memcpy (dest->b_data + last_inserted_loc, B_N_PITEM(src,(first + cpy_num - 1)), + j - last_inserted_loc); + + /* sizes, item number */ + blkh->blk_nr_item += cpy_num; + blkh->blk_free_space -= j - last_inserted_loc; + + mark_buffer_dirty (dest); + + if (dest_bi->bi_parent) { +#ifdef CONFIG_REISERFS_CHECK + if (B_N_CHILD (dest_bi->bi_parent, dest_bi->bi_position)->dc_block_number != dest->b_blocknr) { + reiserfs_panic (0, "vs-10160: leaf_copy_items_entirely: " + "block number in bh does not match to field in disk_child structure %lu and %lu", + dest->b_blocknr, B_N_CHILD (dest_bi->bi_parent, dest_bi->bi_position)->dc_block_number); + } +#endif + B_N_CHILD (dest_bi->bi_parent, dest_bi->bi_position)->dc_size += + j - last_inserted_loc + IH_SIZE * cpy_num; + + mark_buffer_dirty(dest_bi->bi_parent); + } +} + + +/* This function splits the (liquid) item into two items (useful when + shifting part of an item into another node.) */ +static void leaf_item_bottle (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, struct buffer_head * src, int last_first, + int item_num, int cpy_bytes) +{ + struct buffer_head * dest = dest_bi->bi_bh; + struct item_head * ih; + +#ifdef CONFIG_REISERFS_CHECK + if ( cpy_bytes == -1 ) + reiserfs_panic (0, "vs-10170: leaf_item_bottle: bytes == - 1 means: do not split item"); +#endif + + if ( last_first == FIRST_TO_LAST ) { + /* if ( if item in position item_num in buffer SOURCE is directory item ) */ + if (I_IS_DIRECTORY_ITEM(ih = B_N_PITEM_HEAD(src,item_num))) + leaf_copy_dir_entries (fs, dest_bi, src, FIRST_TO_LAST, item_num, 0, cpy_bytes); + else { + struct item_head n_ih; + + /* copy part of the body of the item number 'item_num' of SOURCE to the end of the DEST + part defined by 'cpy_bytes'; create new item header; change old item_header (????); + n_ih = new item_header; + */ + memcpy (&n_ih, ih, IH_SIZE); + n_ih.ih_item_len = cpy_bytes; + if (I_IS_INDIRECT_ITEM(ih)) { +#ifdef CONFIG_REISERFS_CHECK + if (cpy_bytes == ih->ih_item_len && ih_free_space (ih)) + reiserfs_panic (0, "vs-10180: leaf_item_bottle: " + "when whole indirect item is bottle to left neighbor, it must have free_space==0 (not %lu)", + ih_free_space (ih)); +#endif + //n_ih.u.ih_free_space = 0; + set_free_space (&n_ih, 0);; + } + +#ifdef CONFIG_REISERFS_CHECK + if (is_left_mergeable (ih, src->b_size)) + reiserfs_panic (0, "vs-10190: leaf_item_bottle: item %h should not be mergeable", ih); +#endif + //n_ih.ih_version = ih->ih_version; + set_key_format (&n_ih, ih_key_format (ih)); + n_ih.ih_format.fsck_need = ih->ih_format.fsck_need; + leaf_insert_into_buf (fs, dest_bi, B_NR_ITEMS(dest), &n_ih, B_N_PITEM (src, item_num), 0); + } + } else { + /* if ( if item in position item_num in buffer SOURCE is directory item ) */ + if (I_IS_DIRECTORY_ITEM(ih = B_N_PITEM_HEAD (src, item_num))) + leaf_copy_dir_entries (fs, dest_bi, src, LAST_TO_FIRST, item_num, ih_entry_count(ih) - cpy_bytes, cpy_bytes); + else { + struct item_head n_ih; + + /* copy part of the body of the item number 'item_num' of SOURCE to the begin of the DEST + part defined by 'cpy_bytes'; create new item header; + n_ih = new item_header; + */ + memcpy (&n_ih, ih, SHORT_KEY_SIZE); + + if (I_IS_DIRECT_ITEM(ih)) { + //n_ih.ih_key.k_offset = ih->ih_key.k_offset + ih->ih_item_len - cpy_bytes; + set_offset (key_format (&ih->ih_key), &n_ih.ih_key, get_offset (&ih->ih_key) + ih->ih_item_len - cpy_bytes); + //n_ih.ih_key.k_uniqueness = TYPE_DIRECT; + set_type (key_format (&ih->ih_key), &n_ih.ih_key, TYPE_DIRECT); + //n_ih.u.ih_free_space = USHRT_MAX; + set_free_space (&n_ih, USHRT_MAX); + } else { + /* indirect item */ +#ifdef CONFIG_REISERFS_CHECK + if (!cpy_bytes && ih_free_space (ih)) + reiserfs_panic (0, "vs-10200: leaf_item_bottle: ih->ih_free_space must be 0 when indirect item will be appended"); +#endif + //n_ih.ih_key.k_offset = ih->ih_key.k_offset + (ih->ih_item_len - cpy_bytes) / UNFM_P_SIZE * dest->b_size; + set_offset (key_format (&ih->ih_key), &n_ih.ih_key, + get_offset (&ih->ih_key) + (ih->ih_item_len - cpy_bytes) / UNFM_P_SIZE * dest->b_size); + //n_ih.ih_key.k_uniqueness = TYPE_INDIRECT; + set_type (key_format (&ih->ih_key), &n_ih.ih_key, TYPE_INDIRECT); + //n_ih.u.ih_free_space = ih->u.ih_free_space; + set_free_space (&n_ih, ih_free_space (ih)); + } + + /* set item length */ + n_ih.ih_item_len = cpu_to_le16 (cpy_bytes); + //n_ih.ih_version = ih->ih_version; + set_key_format (&n_ih, ih_key_format (ih)); + n_ih.ih_format.fsck_need = ih->ih_format.fsck_need; + leaf_insert_into_buf (fs, dest_bi, 0, &n_ih, B_N_PITEM(src,item_num) + ih->ih_item_len - cpy_bytes, 0); + } + } +} + + +/* If cpy_bytes equals minus one than copy cpy_num whole items from SOURCE to DEST. + If cpy_bytes not equal to minus one than copy cpy_num-1 whole items from SOURCE to DEST. + From last item copy cpy_num bytes for regular item and cpy_num directory entries for + directory item. */ +static int leaf_copy_items (reiserfs_filsys_t fs, + struct buffer_info * dest_bi, struct buffer_head * src, + int last_first, int cpy_num, + int cpy_bytes) +{ + struct buffer_head * dest; + int pos, i, src_nr_item, bytes; + + dest = dest_bi->bi_bh; +#ifdef CONFIG_REISERFS_CHECK + if (!dest || !src) + reiserfs_panic (0, "vs-10210: leaf_copy_items: !dest || !src"); + + if ( last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST ) + reiserfs_panic (0, "vs-10220: leaf_copy_items: last_first != FIRST_TO_LAST && last_first != LAST_TO_FIRST"); + + if ( B_NR_ITEMS(src) < cpy_num ) + reiserfs_panic (0, "vs-10230: leaf_copy_items: No enough items: %d, required %d", B_NR_ITEMS(src), cpy_num); + + if ( cpy_num < 0 ) + reiserfs_panic (0, "vs-10240: leaf_copy_items: cpy_num < 0 (%d)", cpy_num); +#endif + + if ( cpy_num == 0 ) + return 0; + + if ( last_first == FIRST_TO_LAST ) { + /* copy items to left */ + pos = 0; + if ( cpy_num == 1 ) + bytes = cpy_bytes; + else + bytes = -1; + + /* copy the first item or it part or nothing to the end of the DEST (i = leaf_copy_boundary_item(DEST,SOURCE,0,bytes)) */ + i = leaf_copy_boundary_item (fs, dest_bi, src, FIRST_TO_LAST, bytes); + cpy_num -= i; + if ( cpy_num == 0 ) + return i; + pos += i; + if ( cpy_bytes == -1 ) + /* copy first cpy_num items starting from position 'pos' of SOURCE to end of DEST */ + leaf_copy_items_entirely(fs, dest_bi, src, FIRST_TO_LAST, pos, cpy_num); + else { + /* copy first cpy_num-1 items starting from position 'pos-1' of the SOURCE to the end of the DEST */ + leaf_copy_items_entirely(fs, dest_bi, src, FIRST_TO_LAST, pos, cpy_num-1); + + /* copy part of the item which number is cpy_num+pos-1 to the end of the DEST */ + leaf_item_bottle (fs, dest_bi, src, FIRST_TO_LAST, cpy_num+pos-1, cpy_bytes); + } + } else { + /* copy items to right */ + src_nr_item = B_NR_ITEMS (src); + if ( cpy_num == 1 ) + bytes = cpy_bytes; + else + bytes = -1; + + /* copy the last item or it part or nothing to the begin of the DEST (i = leaf_copy_boundary_item(DEST,SOURCE,1,bytes)); */ + i = leaf_copy_boundary_item (fs, dest_bi, src, LAST_TO_FIRST, bytes); + + cpy_num -= i; + if ( cpy_num == 0 ) + return i; + + pos = src_nr_item - cpy_num - i; + if ( cpy_bytes == -1 ) { + /* starting from position 'pos' copy last cpy_num items of SOURCE to begin of DEST */ + leaf_copy_items_entirely(fs, dest_bi, src, LAST_TO_FIRST, pos, cpy_num); + } else { + /* copy last cpy_num-1 items starting from position 'pos+1' of the SOURCE to the begin of the DEST; */ + leaf_copy_items_entirely(fs, dest_bi, src, LAST_TO_FIRST, pos+1, cpy_num-1); + + /* copy part of the item which number is pos to the begin of the DEST */ + leaf_item_bottle (fs, dest_bi, src, LAST_TO_FIRST, pos, cpy_bytes); + } + } + return i; +} + + +/* there are types of coping: from S[0] to L[0], from S[0] to R[0], + from R[0] to L[0]. for each of these we have to define parent and + positions of destination and source buffers */ +static void leaf_define_dest_src_infos (int shift_mode, struct tree_balance * tb, struct buffer_info * dest_bi, + struct buffer_info * src_bi, int * first_last, + struct buffer_head * Snew) +{ +#ifdef CONFIG_REISERFS_CHECK + memset (dest_bi, 0, sizeof (struct buffer_info)); + memset (src_bi, 0, sizeof (struct buffer_info)); +#endif + + /* define dest, src, dest parent, dest position */ + switch (shift_mode) { + case LEAF_FROM_S_TO_L: /* it is used in leaf_shift_left */ + src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path); + src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0); /* src->b_item_order */ + dest_bi->bi_bh = tb->L[0]; + dest_bi->bi_parent = tb->FL[0]; + dest_bi->bi_position = get_left_neighbor_position (tb, 0); + *first_last = FIRST_TO_LAST; + break; + + case LEAF_FROM_S_TO_R: /* it is used in leaf_shift_right */ + src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path); + src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0); + dest_bi->bi_bh = tb->R[0]; + dest_bi->bi_parent = tb->FR[0]; + dest_bi->bi_position = get_right_neighbor_position (tb, 0); + *first_last = LAST_TO_FIRST; + break; + + case LEAF_FROM_R_TO_L: /* it is used in balance_leaf_when_delete */ + src_bi->bi_bh = tb->R[0]; + src_bi->bi_parent = tb->FR[0]; + src_bi->bi_position = get_right_neighbor_position (tb, 0); + dest_bi->bi_bh = tb->L[0]; + dest_bi->bi_parent = tb->FL[0]; + dest_bi->bi_position = get_left_neighbor_position (tb, 0); + *first_last = FIRST_TO_LAST; + break; + + case LEAF_FROM_L_TO_R: /* it is used in balance_leaf_when_delete */ + src_bi->bi_bh = tb->L[0]; + src_bi->bi_parent = tb->FL[0]; + src_bi->bi_position = get_left_neighbor_position (tb, 0); + dest_bi->bi_bh = tb->R[0]; + dest_bi->bi_parent = tb->FR[0]; + dest_bi->bi_position = get_right_neighbor_position (tb, 0); + *first_last = LAST_TO_FIRST; + break; + + case LEAF_FROM_S_TO_SNEW: + src_bi->bi_bh = PATH_PLAST_BUFFER (tb->tb_path); + src_bi->bi_parent = PATH_H_PPARENT (tb->tb_path, 0); + src_bi->bi_position = PATH_H_B_ITEM_ORDER (tb->tb_path, 0); + dest_bi->bi_bh = Snew; + dest_bi->bi_parent = 0; + dest_bi->bi_position = 0; + *first_last = LAST_TO_FIRST; + break; + + default: + reiserfs_panic (0, "vs-10250: leaf_define_dest_src_infos: shift type is unknown (%d)", shift_mode); + } +#ifdef CONFIG_REISERFS_CHECK + if (src_bi->bi_bh == 0 || dest_bi->bi_bh == 0) { + reiserfs_panic (0, "vs-10260: leaf_define_dest_src_etc: mode==%d, source (%p) or dest (%p) buffer is initialized incorrectly", + shift_mode, src_bi->bi_bh, dest_bi->bi_bh); + } +#endif +} + + + + +/* copy mov_num items and mov_bytes of the (mov_num-1)th item to + neighbor. Delete them from source */ +int leaf_move_items (int shift_mode, struct tree_balance * tb, + int mov_num, int mov_bytes, struct buffer_head * Snew) +{ + int ret_value; + struct buffer_info dest_bi, src_bi; + int first_last; + + leaf_define_dest_src_infos (shift_mode, tb, &dest_bi, &src_bi, &first_last, Snew); + + ret_value = leaf_copy_items (tb->tb_sb, &dest_bi, src_bi.bi_bh, first_last, mov_num, mov_bytes); + + leaf_delete_items (tb->tb_sb, &src_bi, first_last, (first_last == FIRST_TO_LAST) ? 0 : + (B_NR_ITEMS(src_bi.bi_bh) - mov_num), mov_num, mov_bytes); + + + return ret_value; +} + + +/* Shift shift_num items (and shift_bytes of last shifted item if shift_bytes != -1) + from S[0] to L[0] and replace the delimiting key */ +int leaf_shift_left (struct tree_balance * tb, int shift_num, int shift_bytes) +{ + struct buffer_head * S0 = PATH_PLAST_BUFFER (tb->tb_path); + int i; + + /* move shift_num (and shift_bytes bytes) items from S[0] to left neighbor L[0] */ + i = leaf_move_items (LEAF_FROM_S_TO_L, tb, shift_num, shift_bytes, 0); + + if ( shift_num ) { + if (B_NR_ITEMS (S0) == 0) { + /* everything is moved from S[0] */ + +#ifdef CONFIG_REISERFS_CHECK + if ( shift_bytes != -1 ) + reiserfs_panic (tb->tb_sb, "vs-10270: leaf_shift_left: S0 is empty now, but shift_bytes != -1 (%d)", shift_bytes); + + if (init_mode == M_PASTE || init_mode == M_INSERT) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "vs-10275"); + reiserfs_panic (tb->tb_sb, "vs-10275: leaf_shift_left: balance condition corrupted (%c)", init_mode); + } +#endif + + if (PATH_H_POSITION (tb->tb_path, 1) == 0) + replace_key(tb->tb_sb, tb->CFL[0], tb->lkey[0], PATH_H_PPARENT (tb->tb_path, 0), 0); + + } else { + /* replace lkey in CFL[0] by 0-th key from S[0]; */ + replace_key(tb->tb_sb, tb->CFL[0], tb->lkey[0], S0, 0); + +#ifdef CONFIG_REISERFS_CHECK + if (shift_bytes != -1 && !(I_IS_DIRECTORY_ITEM (B_N_PITEM_HEAD (S0, 0)) + && !ih_entry_count (B_N_PITEM_HEAD (S0, 0)))) { + if (!is_left_mergeable (B_N_PITEM_HEAD (S0, 0), S0->b_size)) { + reiserfs_panic (tb->tb_sb, "vs-10280: leaf_shift_left: item must be mergeable"); + } + } +#endif + } + } + + return i; +} + + + + + +/* CLEANING STOPPED HERE */ + + + + +/* Shift shift_num (shift_bytes) items from S[0] to the right neighbor, and replace the delimiting key */ +int leaf_shift_right (struct tree_balance * tb, int shift_num, int shift_bytes) +{ + int ret_value; + + /* move shift_num (and shift_bytes) items from S[0] to right neighbor R[0] */ + ret_value = leaf_move_items (LEAF_FROM_S_TO_R, tb, shift_num, shift_bytes, 0); + + /* replace rkey in CFR[0] by the 0-th key from R[0] */ + if (shift_num) { + replace_key(tb->tb_sb, tb->CFR[0], tb->rkey[0], tb->R[0], 0); + } + + return ret_value; +} + + + +static void leaf_delete_items_entirely (struct super_block * sb, + /*struct reiserfs_transaction_handle *th,*/ + struct buffer_info * bi, + int first, + int del_num); +/* If del_bytes == -1, starting from position 'first' delete del_num items in whole in buffer CUR. + If not. + If last_first == 0. Starting from position 'first' delete del_num-1 items in whole. Delete part of body of + the first item. Part defined by del_bytes. Don't delete first item header + If last_first == 1. Starting from position 'first+1' delete del_num-1 items in whole. Delete part of body of + the last item . Part defined by del_bytes. Don't delete last item header. +*/ +void leaf_delete_items (reiserfs_filsys_t fs, + struct buffer_info * cur_bi, + int last_first, + int first, int del_num, int del_bytes) +{ + struct buffer_head * bh; + int item_amount = B_NR_ITEMS (bh = cur_bi->bi_bh); + +#ifdef CONFIG_REISERFS_CHECK + if ( !bh ) + reiserfs_panic (0, "leaf_delete_items: 10155: bh is not defined"); + + if ( del_num < 0 ) + reiserfs_panic (0, "leaf_delete_items: 10160: del_num can not be < 0. del_num==%d", del_num); + + if ( first < 0 || first + del_num > item_amount ) + reiserfs_panic (0, "leaf_delete_items: 10165: invalid number of first item to be deleted (%d) or " + "no so much items (%d) to delete (only %d)", first, first + del_num, item_amount); +#endif + + if ( del_num == 0 ) + return; + + if ( first == 0 && del_num == item_amount && del_bytes == -1 ) { + make_empty_node (cur_bi); + mark_buffer_dirty (bh); + return; + } + + if ( del_bytes == -1 ) + /* delete del_num items beginning from item in position first */ + leaf_delete_items_entirely (fs, cur_bi, first, del_num); + else { + if ( last_first == FIRST_TO_LAST ) { + /* delete del_num-1 items beginning from item in position first */ + leaf_delete_items_entirely (fs, cur_bi, first, del_num-1); + + /* delete the part of the first item of the bh do not + delete item header */ + leaf_cut_from_buffer (fs, cur_bi, 0, 0, del_bytes); + } else { + struct item_head * ih; + int len; + + /* delete del_num-1 items beginning from item in position first+1 */ + leaf_delete_items_entirely (fs, cur_bi, first+1, del_num-1); + + if (I_IS_DIRECTORY_ITEM(ih = B_N_PITEM_HEAD(bh, B_NR_ITEMS(bh)-1))) /* the last item is directory */ + /* len = numbers of directory entries in this item */ + len = ih_entry_count(ih); + else + /* len = body len of item */ + len = ih->ih_item_len; + + /* delete the part of the last item of the bh + do not delete item header + */ + leaf_cut_from_buffer (fs, cur_bi, B_NR_ITEMS(bh) - 1, len - del_bytes, del_bytes); + } + } +} + + +/* insert item into the leaf node in position before */ +void leaf_insert_into_buf (struct super_block * s, + struct buffer_info * bi, + int before, + struct item_head * inserted_item_ih, + const char * inserted_item_body, + int zeros_number + ) +{ + struct buffer_head * bh = bi->bi_bh; + int nr; + struct block_head * blkh; + struct item_head * ih; + int i; + int last_loc, unmoved_loc; + char * to; + + nr = (blkh = B_BLK_HEAD (bh))->blk_nr_item; + +#ifdef CONFIG_REISERFS_CHECK + /* check free space */ + if (blkh->blk_free_space < inserted_item_ih->ih_item_len + IH_SIZE) + reiserfs_panic (0, "leaf_insert_into_buf: 10170: not enough free space: needed %d, available %d", + inserted_item_ih->ih_item_len + IH_SIZE, blkh->blk_free_space); + if (zeros_number > inserted_item_ih->ih_item_len) + reiserfs_panic (0, "vs-10172: leaf_insert_into_buf: zero number == %d, item length == %d", zeros_number, inserted_item_ih->ih_item_len); +#endif /* CONFIG_REISERFS_CHECK */ + + + /* get item new item must be inserted before */ + ih = B_N_PITEM_HEAD (bh, before); + + /* prepare space for the body of new item */ + last_loc = nr ? ih[nr - before - 1].ih_item_location : bh->b_size; + unmoved_loc = before ? (ih-1)->ih_item_location : bh->b_size; + + memmove (bh->b_data + last_loc - inserted_item_ih->ih_item_len, + bh->b_data + last_loc, unmoved_loc - last_loc); + + to = bh->b_data + unmoved_loc - inserted_item_ih->ih_item_len; + memset (to, 0, zeros_number); + to += zeros_number; + + /* copy body to prepared space */ + if (inserted_item_body) + //if (mem_mode == REISERFS_USER_MEM) + // copy_from_user (to, inserted_item_body, inserted_item_ih->ih_item_len - zeros_number); + //else { + memmove (to, inserted_item_body, inserted_item_ih->ih_item_len - zeros_number); + //} + else + memset(to, '\0', inserted_item_ih->ih_item_len - zeros_number); + + /* insert item header */ + memmove (ih + 1, ih, IH_SIZE * (nr - before)); + memmove (ih, inserted_item_ih, IH_SIZE); + + /* change locations */ + for (i = before; i < nr + 1; i ++) + ih[i-before].ih_item_location = + (unmoved_loc -= ih[i-before].ih_item_len); + + /* sizes, free space, item number */ + blkh->blk_nr_item ++; + blkh->blk_free_space -= (IH_SIZE + inserted_item_ih->ih_item_len); + + mark_buffer_dirty(bh) ; + + if (bi->bi_parent) { + B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size += (IH_SIZE + inserted_item_ih->ih_item_len); + mark_buffer_dirty(bi->bi_parent) ; + } + + if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) + reiserfs_panic ("leaf_insert_into_buf: bad leaf %lu: %b", + bh->b_blocknr, bh); +} + + +/* paste paste_size bytes to affected_item_num-th item. + When item is a directory, this only prepare space for new entries */ +void leaf_paste_in_buffer (reiserfs_filsys_t fs, + struct buffer_info * bi, + int affected_item_num, + int pos_in_item, + int paste_size, + const char * body, + int zeros_number) +{ + struct buffer_head * bh = bi->bi_bh; + int nr; + struct block_head * blkh; + struct item_head * ih; + int i; + int last_loc, unmoved_loc; + + + nr = (blkh = B_BLK_HEAD(bh))->blk_nr_item; + +#ifdef CONFIG_REISERFS_CHECK + /* check free space */ + if (blkh->blk_free_space < paste_size) + reiserfs_panic (th->t_super, "vs-10175: leaf_paste_in_buffer: " + "not enough free space: needed %d, available %d", + paste_size, blkh->blk_free_space); + if (zeros_number > paste_size) { + print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "10177"); + reiserfs_panic (th->t_super, "vs-10177: leaf_paste_in_buffer: " + "zero number == %d, paste_size == %d", zeros_number, paste_size); + } +#endif /* CONFIG_REISERFS_CHECK */ + + + /* item to be appended */ + ih = B_N_PITEM_HEAD(bh, affected_item_num); + + last_loc = ih[nr - affected_item_num - 1].ih_item_location; + unmoved_loc = affected_item_num ? (ih-1)->ih_item_location : bh->b_size; + + /* prepare space */ + memmove (bh->b_data + last_loc - paste_size, bh->b_data + last_loc, + unmoved_loc - last_loc); + + + /* change locations */ + for (i = affected_item_num; i < nr; i ++) + ih[i-affected_item_num].ih_item_location -= paste_size; + + if ( body ) { + if (!I_IS_DIRECTORY_ITEM(ih)) { + //if (mem_mode == REISERFS_USER_MEM) { + //memset (bh->b_data + unmoved_loc - paste_size, 0, zeros_number); + //copy_from_user (bh->b_data + unmoved_loc - paste_size + zeros_number, body, paste_size - zeros_number); + //} else + { + if (!pos_in_item) { + /* shift data to right */ + memmove (bh->b_data + ih->ih_item_location + paste_size, + bh->b_data + ih->ih_item_location, ih->ih_item_len); + /* paste data in the head of item */ + memset (bh->b_data + ih->ih_item_location, 0, zeros_number); + memcpy (bh->b_data + ih->ih_item_location + zeros_number, body, paste_size - zeros_number); + } else { + memset (bh->b_data + unmoved_loc - paste_size, 0, zeros_number); + memcpy (bh->b_data + unmoved_loc - paste_size + zeros_number, body, paste_size - zeros_number); + } + } + } + } + else + memset(bh->b_data + unmoved_loc - paste_size,'\0',paste_size); + + ih->ih_item_len += paste_size; + + /* change free space */ + blkh->blk_free_space -= paste_size; + + mark_buffer_dirty(bh) ; + + if (bi->bi_parent) { + B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size += paste_size; + mark_buffer_dirty(bi->bi_parent); + } + if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) + reiserfs_panic ("leaf_paste_in_buffer: bad leaf %lu: %b", + bh->b_blocknr, bh); +} + +/* cuts DEL_COUNT entries beginning from FROM-th entry. Directory item + does not have free space, so it moves DEHs and remaining records as + necessary. Return value is size of removed part of directory item + in bytes. */ +static int leaf_cut_entries (struct buffer_head * bh, + struct item_head * ih, + int from, int del_count) +{ + char * item; + struct reiserfs_de_head * deh; + int prev_record_offset; /* offset of record, that is (from-1)th */ + char * prev_record; /* */ + int cut_records_len; /* length of all removed records */ + int i; + int entry_count; + + + /* first byte of item */ + item = bh->b_data + ih_location (ih); + + /* entry head array */ + deh = B_I_DEH (bh, ih); + entry_count = ih_entry_count (ih); + + if (del_count == 0) { + int shift; + int last_location; + + last_location = deh_location (deh + entry_count - 1); + shift = last_location - DEH_SIZE * entry_count; + + memmove (deh + entry_count, item + last_location, + ih_item_len (ih) - last_location); + for (i = 0; i < entry_count; i ++) + deh[i].deh_location = cpu_to_le16 (deh_location (deh + i) - shift); + return shift; + } + + /* first byte of remaining entries, those are BEFORE cut entries + (prev_record) and length of all removed records (cut_records_len) */ + prev_record_offset = (from ? deh[from - 1].deh_location : ih->ih_item_len); + cut_records_len = prev_record_offset/*from_record*/ - deh[from + del_count - 1].deh_location; + prev_record = item + prev_record_offset; + + + /* adjust locations of remaining entries */ + for (i = ih_entry_count (ih) - 1; i > from + del_count - 1; i --) + deh[i].deh_location -= (DEH_SIZE * del_count); + + for (i = 0; i < from; i ++) + deh[i].deh_location -= DEH_SIZE * del_count + cut_records_len; + + set_entry_count (ih, ih_entry_count (ih) - del_count); + + /* shift entry head array and entries those are AFTER removed entries */ + memmove ((char *)(deh + from), + deh + from + del_count, + prev_record - cut_records_len - (char *)(deh + from + del_count)); + + /* shift records, those are BEFORE removed entries */ + memmove (prev_record - cut_records_len - DEH_SIZE * del_count, + prev_record, item + ih->ih_item_len - prev_record); + + return DEH_SIZE * del_count + cut_records_len; +} + + +/* when cut item is part of regular file + pos_in_item - first byte that must be cut + cut_size - number of bytes to be cut beginning from pos_in_item + + when cut item is part of directory + pos_in_item - number of first deleted entry + cut_size - count of deleted entries + */ +void leaf_cut_from_buffer (reiserfs_filsys_t fs, + struct buffer_info * bi, int cut_item_num, + int pos_in_item, int cut_size) +{ + int nr; + struct buffer_head * bh = bi->bi_bh; + struct block_head * blkh; + struct item_head * ih; + int last_loc, unmoved_loc; + int i; + + nr = (blkh = B_BLK_HEAD (bh))->blk_nr_item; + + /* item head of truncated item */ + ih = B_N_PITEM_HEAD (bh, cut_item_num); + + if (I_IS_DIRECTORY_ITEM (ih)) { + /* first cut entry ()*/ + cut_size = leaf_cut_entries (bh, ih, pos_in_item, cut_size); + if (pos_in_item == 0) { + /* change key */ +#ifdef CONFIG_REISERFS_CHECK + if (cut_item_num) + reiserfs_panic (th->t_super, "leaf_cut_from_buffer: 10190: " + "when 0-th enrty of item is cut, that item must be first in the node, not %d-th", cut_item_num); +#endif + /* change item key by key of first entry in the item */ + ih->ih_key.u.k_offset_v1.k_offset = B_I_DEH (bh, ih)->deh_offset; + + /*memcpy (&ih->ih_key.k_offset, &(B_I_DEH (bh, ih)->deh_offset), SHORT_KEY_SIZE);*/ + } + } else { + /* item is direct or indirect */ +#ifdef CONFIG_REISERFS_CHECK + if (I_IS_STAT_DATA_ITEM (ih)) + reiserfs_panic (th->t_super, "leaf_cut_from_buffer: 10195: item is stat data"); + + if (pos_in_item && pos_in_item + cut_size != ih->ih_item_len ) + reiserfs_panic (th->t_super, "cut_from_buf: 10200: invalid offset (%lu) or trunc_size (%lu) or ih_item_len (%lu)", + pos_in_item, cut_size, ih->ih_item_len); +#endif + + /* shift item body to left if cut is from the head of item */ + if (pos_in_item == 0) { + memmove (bh->b_data + ih->ih_item_location, bh->b_data + ih->ih_item_location + cut_size, + ih->ih_item_len - cut_size); + + /* change key of item */ + if (I_IS_DIRECT_ITEM(ih)) { + //ih->ih_key.k_offset += cut_size; + set_offset (key_format (&ih->ih_key), &ih->ih_key, get_offset (&ih->ih_key) + cut_size); + } else { + //ih->ih_key.k_offset += (cut_size / UNFM_P_SIZE) * bh->b_size; + set_offset (key_format (&ih->ih_key), &ih->ih_key, + get_offset (&ih->ih_key) + (cut_size / UNFM_P_SIZE) * bh->b_size); +#ifdef CONFIG_REISERFS_CHECK + if ( ih->ih_item_len == cut_size && ih_free_space (ih) ) + reiserfs_panic (th->t_super, "leaf_cut_from_buf: 10205: invalid ih_free_space (%lu)", ih_free_space (ih)); +#endif + } + } + } + + + /* location of the last item */ + last_loc = ih[nr - cut_item_num - 1].ih_item_location; + + /* location of the item, which is remaining at the same place */ + unmoved_loc = cut_item_num ? (ih-1)->ih_item_location : bh->b_size; + + + /* shift */ + memmove (bh->b_data + last_loc + cut_size, bh->b_data + last_loc, + unmoved_loc - last_loc - cut_size); + + /* change item length */ + ih->ih_item_len -= cut_size; + + if (I_IS_INDIRECT_ITEM(ih)) { + if (pos_in_item) + //ih->u.ih_free_space = 0; + set_free_space (ih, 0); + } + + /* change locations */ + for (i = cut_item_num; i < nr; i ++) + ih[i-cut_item_num].ih_item_location += cut_size; + + /* size, free space */ + blkh->blk_free_space += cut_size; + + mark_buffer_dirty(bh); + + if (bi->bi_parent) { + B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size -= cut_size; + mark_buffer_dirty(bi->bi_parent); + } + if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) + reiserfs_panic ("leaf_cut_from_buffer: bad leaf %lu: %b", + bh->b_blocknr, bh); +} + + +/* delete del_num items from buffer starting from the first'th item */ +static void leaf_delete_items_entirely (reiserfs_filsys_t fs, + struct buffer_info * bi, + int first, int del_num) +{ + struct buffer_head * bh = bi->bi_bh; + int nr; + int i, j; + int last_loc, last_removed_loc; + struct block_head * blkh; + struct item_head * ih; + + +#ifdef CONFIG_REISERFS_CHECK + if (bh == NULL) + reiserfs_panic (0, "leaf_delete_items_entirely: 10210: buffer is 0"); + + if (del_num < 0) + reiserfs_panic (0, "leaf_delete_items_entirely: 10215: del_num less than 0 (%d)", del_num); +#endif /* CONFIG_REISERFS_CHECK */ + + if (del_num == 0) + return; + + nr = (blkh = B_BLK_HEAD(bh))->blk_nr_item; + +#ifdef CONFIG_REISERFS_CHECK + if (first < 0 || first + del_num > nr) + reiserfs_panic (0, "leaf_delete_items_entirely: 10220: first=%d, number=%d, there is %d items", first, del_num, nr); +#endif /* CONFIG_REISERFS_CHECK */ + + if (first == 0 && del_num == nr) { + /* this does not work */ + make_empty_node (bi); + + mark_buffer_dirty(bh); + return; + } + + ih = B_N_PITEM_HEAD (bh, first); + + /* location of unmovable item */ + j = (first == 0) ? bh->b_size : (ih-1)->ih_item_location; + + /* delete items */ + last_loc = ih[nr-1-first].ih_item_location; + last_removed_loc = ih[del_num-1].ih_item_location; + + memmove (bh->b_data + last_loc + j - last_removed_loc, + bh->b_data + last_loc, last_removed_loc - last_loc); + + /* delete item headers */ + memmove (ih, ih + del_num, (nr - first - del_num) * IH_SIZE); + + /* change item location */ + for (i = first; i < nr - del_num; i ++) + ih[i-first].ih_item_location += j - last_removed_loc; + + /* sizes, item number */ + blkh->blk_nr_item -= del_num; + blkh->blk_free_space += j - last_removed_loc + IH_SIZE * del_num; + + mark_buffer_dirty(bh); + + if (bi->bi_parent) { + B_N_CHILD (bi->bi_parent, bi->bi_position)->dc_size -= j - last_removed_loc + IH_SIZE * del_num; + mark_buffer_dirty(bi->bi_parent); + } + if (who_is_this (bh->b_data, bh->b_size) != THE_LEAF) + reiserfs_panic ("leaf_delete_items_entirely: bad leaf %lu: %b", + bh->b_blocknr, bh); +} + + + + + +/* paste new_entry_count entries (new_dehs, records) into position before to item_num-th item */ +void leaf_paste_entries (struct buffer_head * bh, + int item_num, int before, int new_entry_count, + struct reiserfs_de_head * new_dehs, + const char * records, int paste_size) +{ + struct item_head * ih; + char * item; + struct reiserfs_de_head * deh; + char * insert_point; + int i, old_entry_num; + + if (new_entry_count == 0) + return; + + ih = B_N_PITEM_HEAD(bh, item_num); + +#ifdef CONFIG_REISERFS_CHECK + /* make sure, that item is directory, and there are enough records in it */ + if (!I_IS_DIRECTORY_ITEM (ih)) + reiserfs_panic (0, "leaf_paste_entries: 10225: item is not directory item"); + + if (ih_entry_count (ih) < before) + reiserfs_panic (0, "leaf_paste_entries: 10230: there are no entry we paste entries before. entry_count = %d, before = %d", + ih_entry_count (ih), before); +#endif + + + /* first byte of dest item */ + item = bh->b_data + ih->ih_item_location; + + /* entry head array */ + deh = B_I_DEH (bh, ih); + + /* new records will be pasted at this point */ + insert_point = item + (before ? deh[before - 1].deh_location : (ih->ih_item_len - paste_size)); + + /* adjust locations of records that will be AFTER new records */ + for (i = ih_entry_count (ih) - 1; i >= before; i --) + deh[i].deh_location += DEH_SIZE * new_entry_count; + + /* adjust locations of records that will be BEFORE new records */ + for (i = 0; i < before; i ++) + deh[i].deh_location += paste_size; + + old_entry_num = ih_entry_count (ih); + //I_ENTRY_COUNT(ih) += new_entry_count; + set_entry_count (ih, old_entry_num + new_entry_count); + + /* prepare space for pasted records */ + memmove (insert_point + paste_size, insert_point, item + (ih->ih_item_len - paste_size) - insert_point); + + /* copy new records */ + memcpy (insert_point + DEH_SIZE * new_entry_count, records, + paste_size - DEH_SIZE * new_entry_count); + + /* prepare space for new entry heads */ + deh += before; + memmove ((char *)(deh + new_entry_count), deh, insert_point - (char *)deh); + + /* copy new entry heads */ + deh = (struct reiserfs_de_head *)((char *)deh); + memcpy (deh, new_dehs, DEH_SIZE * new_entry_count); + + /* set locations of new records */ + for (i = 0; i < new_entry_count; i ++) + deh[i].deh_location += + (- new_dehs[new_entry_count - 1].deh_location + insert_point + DEH_SIZE * new_entry_count - item); + + + /* change item key if neccessary (when we paste before 0-th entry */ + if (!before) + ih->ih_key.u.k_offset_v1.k_offset = new_dehs->deh_offset; + + +#ifdef CONFIG_REISERFS_CHECK + { + int prev, next; + /* check record locations */ + deh = B_I_DEH (bh, ih); + for (i = 0; i < ih_entry_count(ih); i ++) { + next = (i < ih_entry_count(ih) - 1) ? deh[i + 1].deh_location : 0; + prev = (i != 0) ? deh[i - 1].deh_location : 0; + + if (prev && prev <= deh[i].deh_location) + reiserfs_warning ("vs-10240: leaf_paste_entries: directory item corrupted (%d %d)\n", prev, deh[i].deh_location); + if (next && next >= deh[i].deh_location) + reiserfs_warning ("vs-10250: leaf_paste_entries: directory item corrupted (%d %d)\n", prev, deh[i].deh_location); + } + } +#endif + +} + + + +/* wrappers for operations on one separated node */ + +void delete_item (reiserfs_filsys_t fs, + struct buffer_head * bh, int item_num) +{ + struct buffer_info bi; + + bi.bi_bh = bh; + bi.bi_parent = 0; + bi.bi_position = 0; + leaf_delete_items_entirely (fs, &bi, item_num, 1); +} + + +void cut_entry (reiserfs_filsys_t fs, struct buffer_head * bh, + int item_num, int entry_num, int del_count) +{ + struct buffer_info bi; + + bi.bi_bh = bh; + bi.bi_parent = 0; + bi.bi_position = 0; + leaf_cut_from_buffer (fs, &bi, item_num, entry_num, del_count); +} + diff --git a/reiserfscore/node_formats.c b/reiserfscore/node_formats.c new file mode 100644 index 0000000..43ed843 --- /dev/null +++ b/reiserfscore/node_formats.c @@ -0,0 +1,887 @@ +/* + * Copyrright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + + +#include "includes.h" + + + +/* this only checks that the node looks like a correct leaf. Item + internals are not checked */ +static int is_correct_leaf (char * buf, int blocksize) +{ + struct block_head * blkh; + struct item_head * ih; + int used_space; + int prev_location; + int i; + int nr; + + blkh = (struct block_head *)buf; + if (!is_leaf_block_head (buf)) + return 0; + + nr = le16_to_cpu (blkh->blk_nr_item); + if (nr < 1 || nr > ((blocksize - BLKH_SIZE) / (IH_SIZE + MIN_ITEM_LEN))) + /* item number is too big or too small */ + return 0; + + ih = (struct item_head *)(buf + BLKH_SIZE) + nr - 1; + used_space = BLKH_SIZE + IH_SIZE * nr + (blocksize - ih_location (ih)); + if (used_space != blocksize - le16_to_cpu (blkh->blk_free_space)) + /* free space does not match to calculated amount of use space */ + return 0; + + // FIXME: it is_leaf will hit performance too much - we may have + // return 1 here + + /* check tables of item heads */ + ih = (struct item_head *)(buf + BLKH_SIZE); + prev_location = blocksize; + for (i = 0; i < nr; i ++, ih ++) { + /* items of length are allowed - they may exist for short time + during balancing */ + if (ih_location (ih) > blocksize || ih_location (ih) < IH_SIZE * nr) + return 0; + if (/*ih_item_len (ih) < 1 ||*/ ih_item_len (ih) > MAX_ITEM_LEN (blocksize)) + return 0; + if (prev_location - ih_location (ih) != ih_item_len (ih)) + return 0; + prev_location = ih_location (ih); + } + + // one may imagine much more checks + return 1; +} + + +/* returns 1 if buf looks like an internal node, 0 otherwise */ +static int is_correct_internal (char * buf, int blocksize) +{ + struct block_head * blkh; + int nr; + int used_space; + + blkh = (struct block_head *)buf; + + if (!is_internal_block_head (buf)) + return 0; + + nr = le16_to_cpu (blkh->blk_nr_item); + if (nr > (blocksize - BLKH_SIZE - DC_SIZE) / (KEY_SIZE + DC_SIZE)) + /* for internal which is not root we might check min number of keys */ + return 0; + + used_space = BLKH_SIZE + KEY_SIZE * nr + DC_SIZE * (nr + 1); + if (used_space != blocksize - le16_to_cpu (blkh->blk_free_space)) + return 0; + + // one may imagine much more checks + return 1; +} + + +// make sure that bh contains formatted node of reiserfs tree of +// 'level'-th level +int is_tree_node (struct buffer_head * bh, int level) +{ + if (B_LEVEL (bh) != level) + return 0; + if (is_leaf_node (bh)) + return is_correct_leaf (bh->b_data, bh->b_size); + + return is_correct_internal (bh->b_data, bh->b_size); +} + + +static int is_desc_block (struct reiserfs_journal_desc * desc) +{ + if (!memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8) && + le32_to_cpu (desc->j_len) > 0) + return 1; + return 0; +} + + +int is_reiserfs_magic_string (struct reiserfs_super_block * rs) +{ + return (!strncmp (rs->s_v1.s_magic, REISERFS_SUPER_MAGIC_STRING, + strlen ( REISERFS_SUPER_MAGIC_STRING))); +} + + +int is_reiser2fs_magic_string (struct reiserfs_super_block * rs) +{ + return (!strncmp (rs->s_v1.s_magic, REISER2FS_SUPER_MAGIC_STRING, + strlen ( REISER2FS_SUPER_MAGIC_STRING))); +} + + +/* this one had signature in different place of the super_block + structure */ +int is_prejournaled_reiserfs (struct reiserfs_super_block * rs) +{ + return (!strncmp((char*)rs + REISERFS_SUPER_MAGIC_STRING_OFFSET_NJ, + REISERFS_SUPER_MAGIC_STRING, strlen(REISERFS_SUPER_MAGIC_STRING))); +} + + +/* compares description block with commit block. returns 1 if they differ, 0 if they are the same */ +int does_desc_match_commit (struct reiserfs_journal_desc *desc, + struct reiserfs_journal_commit *commit) +{ + if (commit->j_trans_id != desc->j_trans_id || commit->j_len != desc->j_len || + commit->j_len > JOURNAL_TRANS_MAX || commit->j_len <= 0) { + return 1 ; + } + return 0 ; +} + + +/* returns code of reiserfs metadata block (leaf, internal, super + block, journal descriptor), unformatted */ +int who_is_this (char * buf, int blocksize) +{ + if (is_correct_leaf (buf, blocksize)) + /* block head and item head array seem matching (node level, free + space, item number, item locations and length) */ + return THE_LEAF; + + if (is_correct_internal (buf, blocksize)) + return THE_INTERNAL; + + /* super block? */ + if (is_reiser2fs_magic_string ((void *)buf) || + is_reiserfs_magic_string ((void *)buf) || + is_prejournaled_reiserfs ((void *)buf)) + return THE_SUPER; + + /* journal descriptor block? */ + if (is_desc_block ((void *)buf)) + return THE_JDESC; + + /* contents of buf does not look like reiserfs metadata. Bitmaps + are possible here */ + return THE_UNKNOWN; +} + + +int block_of_journal (reiserfs_filsys_t fs, unsigned long block) +{ + if (block >= SB_JOURNAL_BLOCK (fs) && + block <= SB_JOURNAL_BLOCK (fs) + JOURNAL_BLOCK_COUNT) + return 1; + + return 0; +} + + +int block_of_bitmap (reiserfs_filsys_t fs, unsigned long block) +{ + if (spread_bitmaps (fs)) { + if (!(block % (fs->s_blocksize * 8))) + /* bitmap block */ + return 1; + return block == 17; + } else { + /* bitmap in */ + if (block > 2 && block < 3 + SB_BMAP_NR (fs)) + return 1; + return 0; + } +#if 0 + int i; + int bmap_nr; + + bmap_nr = SB_BMAP_NR (fs); + for (i = 0; i < bmap_nr; i ++) + if (block == SB_AP_BITMAP (fs)[i]->b_blocknr) + return 1; +#endif + return 0; +} + + +/* check whether 'block' can be pointed to by an indirect item */ +int not_data_block (reiserfs_filsys_t fs, unsigned long block) +{ + if (block_of_bitmap (fs, block)) + /* it is one of bitmap blocks */ + return 1; + + if (block > 32768) + return 0; + + if (block_of_journal (fs, block)) + /* block of journal area */ + return 1; + + if (block <= fs->s_sbh->b_blocknr) + /* either super block or a block from skipped area at the + beginning of filesystem */ + return 1; + + return 0; +} + + +/* check whether 'block' can be logged */ +int not_journalable (reiserfs_filsys_t fs, unsigned long block) +{ + /* we should not update SB with journal copy during fsck */ + if (block < fs->s_sbh->b_blocknr) + return 1; + + if (block_of_journal (fs, block)) + return 1; + + if (block >= SB_BLOCK_COUNT (fs)) + return 1; + + return 0; +} + + +// in reiserfs version 0 (undistributed bitmap) +// FIXME: what if number of bitmaps is 15? +int get_journal_old_start_must (struct reiserfs_super_block * rs) +{ + return 3 + rs_bmap_nr (rs); +} + + +// in reiserfs version 1 (distributed bitmap) journal starts at 18-th +// +int get_journal_start_must (int blocksize) +{ + return (REISERFS_DISK_OFFSET_IN_BYTES / blocksize) + 2; +} + +int get_bmap_num (struct super_block * s) +{ + return ((is_prejournaled_reiserfs (s->s_rs)) ? + (((struct reiserfs_super_block_v0 *)s->s_rs)->s_bmap_nr) : + SB_BMAP_NR (s)); +} + +int get_block_count (struct super_block * s) +{ + return ((is_prejournaled_reiserfs (s->s_rs)) ? + (((struct reiserfs_super_block_v0 *)s->s_rs)->s_block_count) : + SB_BLOCK_COUNT (s)); +} + +int get_root_block (struct super_block * s) +{ + return ((is_prejournaled_reiserfs (s->s_rs)) ? + (((struct reiserfs_super_block_v0 *)s->s_rs)->s_root_block) : + SB_ROOT_BLOCK (s)); +} + + + +int journal_size (struct super_block * s) +{ + return JOURNAL_BLOCK_COUNT; +} + + + +int check_item_f (reiserfs_filsys_t fs, struct item_head * ih, char * item); + + +/* make sure that key format written in item_head matches to key format + defined looking at the key */ +static int is_key_correct (struct item_head * ih) +{ + if (is_stat_data_ih (ih)) { + /* stat data key looks identical in both formats */ + if (ih_item_len (ih) == SD_SIZE && ih_key_format (ih) == KEY_FORMAT_2) { + /*printf ("new stat data\n");*/ + return 1; + } + if (ih_item_len (ih) == SD_V1_SIZE && ih_key_format (ih) == KEY_FORMAT_1) { + /*printf ("old stat data\n");*/ + return 1; + } + return 0; + } + if (ih_key_format (ih) == key_format (&ih->ih_key)) + return 1; + return 0; +} + + +/* check stat data item length, ih_free_space, mode */ +static int is_bad_sd (reiserfs_filsys_t fs, struct item_head * ih, char * item) +{ + mode_t mode; + + if (ih_entry_count (ih) != 0xffff) + return 1; + + if (ih_key_format (ih) == KEY_FORMAT_1) { + struct stat_data_v1 * sd = (struct stat_data_v1 *)item; + + if (ih_item_len (ih) != SD_V1_SIZE) + /* old stat data must be 32 bytes long */ + return 1; + mode = le16_to_cpu (sd->sd_mode); + } else if (ih_key_format (ih) == KEY_FORMAT_2) { + struct stat_data * sd = (struct stat_data *)item; + + if (ih_item_len (ih) != SD_SIZE) + /* new stat data must be 44 bytes long */ + return 1; + mode = le16_to_cpu (sd->sd_mode); + } else + return 1; + + if (!S_ISDIR (mode) && !S_ISREG (mode) && !S_ISCHR (mode) && + !S_ISBLK (mode) && !S_ISLNK (mode) && !S_ISFIFO (mode) && + !S_ISSOCK (mode)) + return 1; + + return 0; +} + + +/* symlinks created by 3.6.x have direct items with ih_free_space == 0 */ +static int is_bad_direct (reiserfs_filsys_t fs, struct item_head * ih, char * item) +{ + if (ih_entry_count (ih) != 0xffff && ih_entry_count (ih) != 0) + return 1; + return 0; +} + + +/* check item length, ih_free_space for pure 3.5 format, unformatted node + pointers */ +static int is_bad_indirect (reiserfs_filsys_t fs, struct item_head * ih, char * item, + check_unfm_func_t check_unfm_func) +{ + int i; + __u32 * ind = (__u32 *)item; + + if (ih_item_len (ih) % UNFM_P_SIZE) + return 1; + + for (i = 0; i < I_UNFM_NUM (ih); i ++) { + if (!ind [i]) + continue; + if (check_unfm_func && check_unfm_func (fs, ind [i])) + return 1; + } + + if (fs->s_version == REISERFS_VERSION_1) { + /* check ih_free_space for 3.5 format only */ + if (ih_free_space (ih) > fs->s_blocksize - 1) + return 1; + } + + return 0; +} + + +static const struct { + hashf_t func; + char * name; +} hashes[] = {{0, "not set"}, + {keyed_hash, "\"tea\""}, + {yura_hash, "\"rupasov\""}, + {r5_hash, "\"r5\""}}; + +#define HASH_AMOUNT (sizeof (hashes) / sizeof (hashes [0])) + + +int known_hashes (void) +{ + return HASH_AMOUNT; +} + + +#define good_name(hashfn,name,namelen,deh_offset) \ +(GET_HASH_VALUE ((hashfn) (name, namelen)) == GET_HASH_VALUE (deh_offset)) + + +/* this also sets hash function */ +int is_properly_hashed (reiserfs_filsys_t fs, + char * name, int namelen, __u32 offset) +{ + int i; + + if (namelen == 1 && name[0] == '.') { + if (offset == DOT_OFFSET) + return 1; + return 0; + } + + if (namelen == 2 && name[0] == '.' && name[1] == '.') { + if (offset == DOT_DOT_OFFSET) + return 1; + return 0; + } + + if (hash_func_is_unknown (fs)) { + /* try to find what hash function the name is sorted with */ + for (i = 1; i < HASH_AMOUNT; i ++) { + if (good_name (hashes [i].func, name, namelen, offset)) { + if (!hash_func_is_unknown (fs)) { + /* two or more hash functions give the same value for this + name */ + fprintf (stderr, "Detecting hash code: could not detect hash with name \"%.*s\"\n", + namelen, name); + reiserfs_hash (fs) = 0; + return 1; + } + + /* set hash function */ + reiserfs_hash(fs) = hashes [i].func; + } + } + } + + if (good_name (reiserfs_hash(fs), name, namelen, offset)) + return 1; +#if 0 + fprintf (stderr, "is_properly_hashed: namelen %d, name \"%s\", offset %u, hash %u\n", + namelen, name_from_entry (name, namelen), GET_HASH_VALUE (offset), + GET_HASH_VALUE (reiserfs_hash(fs) (name, namelen))); + + /* we could also check whether more than one hash function match on the + name */ + for (i = 1; i < sizeof (hashes) / sizeof (hashes [0]); i ++) { + if (i == g_real_hash) + continue; + if (good_name (hashes[i], name, namelen, deh_offset)) { + die ("bad_hash: at least two hashes got screwed up with this name: \"%s\"", + bad_name (name, namelen)); + } + } +#endif + return 0; +} + + +int find_hash_in_use (char * name, int namelen, __u32 hash_value_masked, int code_to_try_first) +{ + int i; + + if (code_to_try_first) { + if (hash_value_masked == GET_HASH_VALUE (hashes [code_to_try_first].func (name, namelen))) + return code_to_try_first; + } + for (i = 1; i < HASH_AMOUNT; i ++) { + if (i == code_to_try_first) + continue; + if (hash_value_masked == GET_HASH_VALUE (hashes [i].func (name, namelen))) + return i; + } + + /* not matching hash found */ + return UNSET_HASH; +} + + +char * code2name (int code) +{ + if (code >= HASH_AMOUNT) + code = 0; + return hashes [code].name; +} + + +int func2code (hashf_t func) +{ + int i; + + for (i = 0; i < HASH_AMOUNT; i ++) + if (func == hashes [i].func) + return i; + + reiserfs_panic ("func2code: no hashes matches this function\n"); + return 0; +} + + +hashf_t code2func (int code) +{ + if (code >= HASH_AMOUNT) { + reiserfs_warning (stderr, "code2func: wrong hash code %d.\n" + "Using default %s hash function\n", code, + code2name (DEFAULT_HASH)); + code = DEFAULT_HASH; + } + return hashes [code].func; +} + + +int dir_entry_bad_location (struct reiserfs_de_head * deh, struct item_head * ih, int first) +{ + if (deh_location (deh) < DEH_SIZE * ih_entry_count (ih)) + return 1; + + if (deh_location (deh) >= ih_item_len (ih)) + return 1; + + if (!first && deh_location (deh) >= deh_location (deh - 1)) + return 1; + + return 0; +} + + +/* the only corruption which is not considered fatal - is hash mismatching. If + bad_dir is set - directory item having such names is considered bad */ +static int is_bad_directory (reiserfs_filsys_t fs, struct item_head * ih, char * item, + int bad_dir) +{ + int i; + int namelen; + struct reiserfs_de_head * deh = (struct reiserfs_de_head *)item; + __u32 prev_offset = 0; + __u16 prev_location = ih_item_len (ih); + + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + if (deh_location (deh) >= prev_location) + return 1; + prev_location = deh_location (deh); + + namelen = name_length (ih, deh, i); + if (namelen > REISERFS_MAX_NAME_LEN (fs->s_blocksize)) { + return 1; + } + if (deh_offset (deh) <= prev_offset) + return 1; + prev_offset = deh_offset (deh); + + /* check hash value */ + if (!is_properly_hashed (fs, item + prev_location, namelen, prev_offset)) { + if (bad_dir) + /* make is_bad_leaf to not insert whole leaf. Node will be + marked not-insertable and put into tree item by item in + pass 2 */ + return 1; + } + } + + return 0; +} + +/* used by debugreisrefs -p only yet */ +#if 1 +int is_it_bad_item (reiserfs_filsys_t fs, struct item_head * ih, char * item, + check_unfm_func_t check_unfm, int bad_dir) +{ + int retval; + + if (!is_key_correct (ih)) { + reiserfs_warning (stderr, "is_key_correct %H\n", ih); + return 1; + } + + if (is_stat_data_ih (ih)) { + retval = is_bad_sd (fs, ih, item); + /* + if (retval) + reiserfs_warning (stderr, "is_bad_sd %H\n", ih);*/ + return retval; + } + if (is_direntry_ih (ih)) { + retval = is_bad_directory (fs, ih, item, bad_dir); + /* + if (retval) + reiserfs_warning (stderr, "is_bad_directory %H\n", ih);*/ + return retval; + } + if (is_indirect_ih (ih)) { + retval = is_bad_indirect (fs, ih, item, check_unfm); + /* + if (retval) + reiserfs_warning (stderr, "is_bad_indirect %H\n", ih);*/ + return retval; + } + if (is_direct_ih (ih)) { + retval = is_bad_direct (fs, ih, item); + /* + if (retval) + reiserfs_warning (stderr, "is_bad_direct %H\n", ih);*/ + return retval; + } + return 1; +} +#endif + + + +/* prepare new or old stat data for the new directory */ +void make_dir_stat_data (int blocksize, int key_format, + __u32 dirid, __u32 objectid, + struct item_head * ih, void * sd) +{ + memset (ih, 0, IH_SIZE); + ih->ih_key.k_dir_id = cpu_to_le32 (dirid); + ih->ih_key.k_objectid = cpu_to_le32 (objectid); + set_offset (key_format, &ih->ih_key, SD_OFFSET); + set_type (key_format, &ih->ih_key, TYPE_STAT_DATA); + + set_key_format (ih, key_format); + set_free_space (ih, MAX_US_INT); + + if (key_format == KEY_FORMAT_2) + { + struct stat_data *sd_v2 = (struct stat_data *)sd; + + set_ih_item_len (ih, SD_SIZE); + sd_v2->sd_mode = cpu_to_le16 (S_IFDIR + 0755); + sd_v2->sd_nlink = cpu_to_le32 (2); + sd_v2->sd_uid = 0; + sd_v2->sd_gid = 0; + sd_v2->sd_size = cpu_to_le64 (EMPTY_DIR_SIZE); + sd_v2->sd_atime = sd_v2->sd_ctime = sd_v2->sd_mtime = cpu_to_le32 (time (NULL)); + sd_v2->u.sd_rdev = 0; + sd_v2->sd_blocks = cpu_to_le32 (dir_size2st_blocks (blocksize, EMPTY_DIR_SIZE)); + }else{ + struct stat_data_v1 *sd_v1 = (struct stat_data_v1 *)sd; + + set_ih_item_len (ih, SD_V1_SIZE); + sd_v1->sd_mode = cpu_to_le16 (S_IFDIR + 0755); + sd_v1->sd_nlink = cpu_to_le16 (2); + sd_v1->sd_uid = 0; + sd_v1->sd_gid = 0; + sd_v1->sd_size = cpu_to_le32 (EMPTY_DIR_SIZE_V1); + sd_v1->sd_atime = sd_v1->sd_ctime = sd_v1->sd_mtime = cpu_to_le32 (time (NULL)); + sd_v1->u.sd_blocks = cpu_to_le32 (dir_size2st_blocks (blocksize, EMPTY_DIR_SIZE_V1)); + sd_v1->sd_first_direct_byte = cpu_to_le32 (NO_BYTES_IN_DIRECT_ITEM); + } +} + + +static void _empty_dir_item (int format, char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid) +{ + struct reiserfs_de_head * deh; + + memset (body, 0, (format == KEY_FORMAT_2 ? EMPTY_DIR_SIZE : EMPTY_DIR_SIZE_V1)); + deh = (struct reiserfs_de_head *)body; + + /* direntry header of "." */ + deh[0].deh_offset = cpu_to_le32 (DOT_OFFSET); + deh[0].deh_dir_id = cpu_to_le32 (dirid); + deh[0].deh_objectid = cpu_to_le32 (objid); + deh[0].deh_state = 0; + set_bit (DEH_Visible, &(deh[0].deh_state)); + + /* direntry header of ".." */ + deh[1].deh_offset = cpu_to_le32 (DOT_DOT_OFFSET); + /* key of ".." for the root directory */ + deh[1].deh_dir_id = cpu_to_le32 (par_dirid); + deh[1].deh_objectid = cpu_to_le32 (par_objid); + deh[1].deh_state = 0; + set_bit (DEH_Visible, &(deh[1].deh_state)); + + if (format == KEY_FORMAT_2) { + deh[0].deh_location = cpu_to_le16 (EMPTY_DIR_SIZE - ROUND_UP (strlen ("."))); + deh[1].deh_location = cpu_to_le16 (deh_location (&deh[0]) - ROUND_UP (strlen (".."))); + } else { + deh[0].deh_location = cpu_to_le16 (EMPTY_DIR_SIZE_V1 - strlen (".")); + deh[1].deh_location = cpu_to_le16 (deh_location (&deh[0]) - strlen ("..")); + } + + /* copy ".." and "." */ + memcpy (body + deh_location (&deh[0]), ".", 1); + memcpy (body + deh_location (&deh[1]), "..", 2); + +} + + +void make_empty_dir_item_v1 (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid) +{ + _empty_dir_item (KEY_FORMAT_1, body, dirid, objid, par_dirid, par_objid); +} + + +void make_empty_dir_item (char * body, __u32 dirid, __u32 objid, + __u32 par_dirid, __u32 par_objid) +{ + _empty_dir_item (KEY_FORMAT_2, body, dirid, objid, par_dirid, par_objid); +} + + + +/* for every item call common action and an action corresponding to + item type */ +void for_every_item (struct buffer_head * bh, item_head_action_t action, + item_action_t * actions) +{ + int i; + struct item_head * ih; + item_action_t iaction; + + ih = B_N_PITEM_HEAD (bh, 0); + for (i = 0; i < node_item_number (bh); i ++, ih ++) { + if (action) + action (ih); + + iaction = actions[get_type (&ih->ih_key)]; + if (iaction) + iaction (bh, ih); + } +} + + +/* old keys (on i386) have k_offset_v2.k_type == 15 (direct and + indirect) or == 0 (dir items and stat data) */ + +/* */ +int key_format (const struct key * key) +{ + int type; + + type = le16_to_cpu (key->u.k_offset_v2.k_type); + + if (type == 0 || type == 15) + return KEY_FORMAT_1; + + return KEY_FORMAT_2; +} + + +loff_t get_offset (const struct key * key) +{ + if (key_format (key) == KEY_FORMAT_1) + return le32_to_cpu (key->u.k_offset_v1.k_offset); + + return le64_to_cpu (key->u.k_offset_v2.k_offset); +} + + +int uniqueness2type (__u32 uniqueness) +{ + switch (uniqueness) { + case V1_SD_UNIQUENESS: return TYPE_STAT_DATA; + case V1_INDIRECT_UNIQUENESS: return TYPE_INDIRECT; + case V1_DIRECT_UNIQUENESS: return TYPE_DIRECT; + case V1_DIRENTRY_UNIQUENESS: return TYPE_DIRENTRY; + } + return TYPE_UNKNOWN; +} + + +__u32 type2uniqueness (int type) +{ + switch (type) { + case TYPE_STAT_DATA: return V1_SD_UNIQUENESS; + case TYPE_INDIRECT: return V1_INDIRECT_UNIQUENESS; + case TYPE_DIRECT: return V1_DIRECT_UNIQUENESS; + case TYPE_DIRENTRY: return V1_DIRENTRY_UNIQUENESS; + } + return V1_UNKNOWN_UNIQUENESS; +} + + +int get_type (const struct key * key) +{ + if (key_format (key) == KEY_FORMAT_1) + return uniqueness2type (le32_to_cpu (key->u.k_offset_v1.k_uniqueness)); + return le16_to_cpu (key->u.k_offset_v2.k_type); +} + + +char * key_of_what (const struct key * key) +{ + switch (get_type (key)) { + case TYPE_STAT_DATA: return "SD"; + case TYPE_INDIRECT: return "IND"; + case TYPE_DIRECT: return "DRCT"; + case TYPE_DIRENTRY: return "DIR"; + default: return "???"; + } +} + + +int type_unknown (struct key * key) +{ + int type = get_type (key); + + switch (type) { + case TYPE_STAT_DATA: + case TYPE_INDIRECT: + case TYPE_DIRECT: + case TYPE_DIRENTRY: + return 0; + default: + break; + } + return 1; +} + + +// this sets key format as well as type of item key belongs to +// +void set_type (int format, struct key * key, int type) +{ + if (format == KEY_FORMAT_1) + key->u.k_offset_v1.k_uniqueness = cpu_to_le32 (type2uniqueness (type)); + else + key->u.k_offset_v2.k_type = cpu_to_le16 (type); +} + + +// +void set_offset (int format, struct key * key, loff_t offset) +{ + if (format == KEY_FORMAT_1) + key->u.k_offset_v1.k_offset = cpu_to_le32 (offset); + else + key->u.k_offset_v2.k_offset = cpu_to_le64 (offset); + +} + + +void set_type_and_offset (int format, struct key * key, loff_t offset, int type) +{ + set_type (format, key, type); + set_offset (format, key, offset); +} + + +/* length of the directory entry in directory item. This define calculates + length of i-th directory entry using directory entry locations from dir + entry head. When it calculates length of 0-th directory entry, it uses + length of whole item in place of entry location of the non-existent + following entry in the calculation. See picture above.*/ + + +// NOTE: this is not name length. This is length of whole entry +int entry_length (struct item_head * ih, struct reiserfs_de_head * deh, int pos_in_item) +{ + if (pos_in_item) + return (deh_location (deh - 1) - deh_location (deh)); + return (ih_item_len (ih) - deh_location (deh)); +} + + +char * name_in_entry (struct reiserfs_de_head * deh, int pos_in_item) +{ + return ((char *)(deh - pos_in_item) + deh_location(deh)); +} + + +int name_length (struct item_head * ih, + struct reiserfs_de_head * deh, int pos_in_item) +{ + int len; + char * name; + + len = entry_length (ih, deh, pos_in_item); + name = name_in_entry (deh, pos_in_item); + + // name might be padded with 0s + while (!name [len - 1]) + len --; + + return len; +} diff --git a/reiserfscore/prints.c b/reiserfscore/prints.c new file mode 100644 index 0000000..70acc7b --- /dev/null +++ b/reiserfscore/prints.c @@ -0,0 +1,949 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +#include "includes.h" +#include <stdarg.h> +#include <limits.h> +#include <printf.h> + + +static int _arginfo (const struct printf_info *info, size_t n, + int *argtypes) +{ + if (n > 0) + argtypes[0] = PA_POINTER; + return 1; +} + +#if 0 +static int _arginfo2 (const struct printf_info *info, size_t n, + int *argtypes) +{ + if (n > 0) + argtypes[0] = PA_INT; + return 1; +} +#endif + + +#define FPRINTF \ + if (len == -1) {\ + return -1;\ + }\ + len = fprintf (stream, "%*s",\ + info->left ? -info->width : info->width, buffer);\ + free (buffer);\ + return len;\ + + +/* %z */ +static int print_block_head (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + const struct buffer_head * bh; + char * buffer; + int len; + + bh = *((const struct buffer_head **)(args[0])); + len = asprintf (&buffer, "level=%d, nr_items=%d, free_space=%d rdkey", + B_LEVEL (bh), B_NR_ITEMS (bh), node_free_space (bh)); + FPRINTF; +} + + +/* %K */ +static int print_short_key (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + const struct key * key; + char * buffer; + int len; + + key = *((const struct key **)(args[0])); + len = asprintf (&buffer, "%u %u", key->k_dir_id, key->k_objectid); + FPRINTF; +} + + +/* %k */ +static int print_key (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + const struct key * key; + char * buffer; + int len; + + key = *((const struct key **)(args[0])); + len = asprintf (&buffer, "%u %u 0x%Lx %s", + key->k_dir_id, key->k_objectid, get_offset (key), key_of_what (key)); + FPRINTF; +} + + +/* %H */ +static int print_item_head (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + const struct item_head * ih; + char * buffer; + int len; + + ih = *((const struct item_head **)(args[0])); + len = asprintf (&buffer, "%u %u 0x%Lx %s, " + "len %u, entry count %u, fsck need %u, format %s", + ih->ih_key.k_dir_id, ih->ih_key.k_objectid, + get_offset (&ih->ih_key), key_of_what (&ih->ih_key), + ih->ih_item_len, ih_entry_count (ih), + ih->ih_format.fsck_need, + ih_key_format (ih) == KEY_FORMAT_2 ? "new" : + ((ih_key_format (ih) == KEY_FORMAT_1) ? "old" : "BAD")); + FPRINTF; +} + + +static int print_disk_child (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + const struct disk_child * dc; + char * buffer; + int len; + + dc = *((const struct disk_child **)(args[0])); + len = asprintf (&buffer, "[dc_number=%u, dc_size=%u]", le32_to_cpu (dc->dc_block_number), + le16_to_cpu (dc->dc_size)); + FPRINTF; +} + + +char ftypelet (mode_t mode) +{ + if (S_ISBLK (mode)) + return 'b'; + if (S_ISCHR (mode)) + return 'c'; + if (S_ISDIR (mode)) + return 'd'; + if (S_ISREG (mode)) + return '-'; + if (S_ISFIFO (mode)) + return 'p'; + if (S_ISLNK (mode)) + return 'l'; + if (S_ISSOCK (mode)) + return 's'; + return '?'; +} + + +static int rwx (FILE * stream, mode_t mode) +{ + return fprintf (stream, "%c%c%c", + (mode & S_IRUSR) ? 'r' : '-', + (mode & S_IWUSR) ? 'w' : '-', + (mode & S_IXUSR) ? 'x' : '-'); +} + + +/* %M */ +static int print_sd_mode (FILE * stream, + const struct printf_info *info, + const void *const *args) +{ + int len = 0; + mode_t mode; + + mode = *(mode_t *)args[0]; + len = fprintf (stream, "%c", ftypelet (mode)); + len += rwx (stream, (mode & 0700) << 0); + len += rwx (stream, (mode & 0070) << 3); + len += rwx (stream, (mode & 0007) << 6); + return len; +} + + + +void reiserfs_warning (FILE * fp, const char * fmt, ...) +{ + static int registered = 0; + va_list args; + + if (!registered) { + registered = 1; + + register_printf_function ('K', print_short_key, _arginfo); + register_printf_function ('k', print_key, _arginfo); + register_printf_function ('H', print_item_head, _arginfo); + register_printf_function ('b', print_block_head, _arginfo); + register_printf_function ('y', print_disk_child, _arginfo); + register_printf_function ('M', print_sd_mode, _arginfo); + } + + va_start (args, fmt); + vfprintf (fp, fmt, args); + va_end (args); +} + + +static char * vi_type (struct virtual_item * vi) +{ + static char *types[]={"directory", "direct", "indirect", "stat data"}; + + if (vi->vi_type & VI_TYPE_STAT_DATA) + return types[3]; + if (vi->vi_type & VI_TYPE_INDIRECT) + return types[2]; + if (vi->vi_type & VI_TYPE_DIRECT) + return types[1]; + if (vi->vi_type & VI_TYPE_DIRECTORY) + return types[0]; + + reiserfs_panic ("vi_type: 6000: unknown type (0x%x)", vi->vi_type); + return NULL; +} + + +void print_virtual_node (struct virtual_node * vn) +{ + int i, j; + + printf ("VIRTUAL NODE CONTAINS %d items, has size %d,%s,%s, ITEM_POS=%d POS_IN_ITEM=%d MODE=\'%c\'\n", + vn->vn_nr_item, vn->vn_size, + (vn->vn_vi[0].vi_type & VI_TYPE_LEFT_MERGEABLE )? "left mergeable" : "", + (vn->vn_vi[vn->vn_nr_item - 1].vi_type & VI_TYPE_RIGHT_MERGEABLE) ? "right mergeable" : "", + vn->vn_affected_item_num, vn->vn_pos_in_item, vn->vn_mode); + + + for (i = 0; i < vn->vn_nr_item; i ++) { + printf ("%s %d %d", vi_type (&vn->vn_vi[i]), i, vn->vn_vi[i].vi_item_len); + if (vn->vn_vi[i].vi_entry_sizes) + { + printf ("It is directory with %d entries: ", vn->vn_vi[i].vi_entry_count); + for (j = 0; j < vn->vn_vi[i].vi_entry_count; j ++) + printf ("%d ", vn->vn_vi[i].vi_entry_sizes[j]); + } + printf ("\n"); + } +} + + +void print_path (struct tree_balance * tb, struct path * path) +{ + int offset = path->path_length; + struct buffer_head * bh; + + printf ("Offset Bh (b_blocknr, b_count) Position Nr_item\n"); + while ( offset > ILLEGAL_PATH_ELEMENT_OFFSET ) { + bh = PATH_OFFSET_PBUFFER (path, offset); + printf ("%6d %10p (%9lu, %7d) %8d %7d\n", offset, + bh, bh ? bh->b_blocknr : 0, bh ? bh->b_count : 0, + PATH_OFFSET_POSITION (path, offset), bh ? B_NR_ITEMS (bh) : -1); + + offset --; + } +} + + +#if 0 +void print_de (struct reiserfs_dir_entry * de) +{ + reiserfs_warning ("entry key: [%k], object_key: [%u %u], b_blocknr=%lu, item_num=%d, pos_in_item=%d\n", + &de->de_entry_key, de->de_dir_id, de->de_objectid, + de->de_bh->b_blocknr, de->de_item_num, de->de_entry_num); +} + +static char * item_type (struct item_head * ih) +{ + static char * types[] = { + "SD", "DIR", "DRCT", "IND", "???" + }; + + if (I_IS_STAT_DATA_ITEM(ih)) + return types[0]; + if (I_IS_DIRECTORY_ITEM(ih)) + return types[1]; + if (I_IS_DIRECT_ITEM(ih)) + return types[2]; + if (I_IS_INDIRECT_ITEM(ih)) + return types[3]; + return types[4]; +} + +#endif + + +void print_directory_item (FILE * fp, reiserfs_filsys_t fs, + struct buffer_head * bh, struct item_head * ih) +{ + int i; + int namelen; + struct reiserfs_de_head * deh; + char * name; +/* static char namebuf [80];*/ + + if (!I_IS_DIRECTORY_ITEM (ih)) + return; + + //printk ("\n%2%-25s%-30s%-15s%-15s%-15s\n", " Name", "length", "Object key", "Hash", "Gen number", "Status"); + reiserfs_warning (fp, "%3s: %-25s%s%-22s%-12s%s\n", "###", "Name", "length", " Object key", " Hash", "Gen number"); + deh = B_I_DEH (bh, ih); + for (i = 0; i < ih_entry_count (ih); i ++, deh ++) { + if (dir_entry_bad_location (deh, ih, i == 0 ? 1 : 0)) { + reiserfs_warning (fp, "%3d: wrong entry location %u, deh_offset %u\n", + i, deh_location (deh), deh_offset (deh)); + continue; + } + if (i && dir_entry_bad_location (deh - 1, ih, ((i - 1) == 0) ? 1 : 0)) + /* previous entry has bad location so we can not calculate entry + length */ + namelen = 25; + else + namelen = name_length (ih, deh, i); + + name = name_in_entry (deh, i); + reiserfs_warning (fp, "%3d: \"%-25.*s\"(%3d)%20K%12d%5d, loc %u, state %x %s\n", + i, namelen, name, namelen, + (struct key *)&(deh->deh_dir_id), + GET_HASH_VALUE (deh->deh_offset), GET_GENERATION_NUMBER (deh->deh_offset), + deh_location (deh), deh->deh_state, + fs ? (is_properly_hashed (fs, name, namelen, deh_offset (deh)) ? "" : "(BROKEN)") : "??"); + } +} + + +// +// printing of indirect item +// +static void start_new_sequence (__u32 * start, int * len, __u32 new) +{ + *start = new; + *len = 1; +} + + +static int sequence_finished (__u32 start, int * len, __u32 new) +{ + if (start == INT_MAX) + return 1; + + if (start == 0 && new == 0) { + (*len) ++; + return 0; + } + if (start != 0 && (start + *len) == new) { + (*len) ++; + return 0; + } + return 1; +} + +static void print_sequence (FILE * fp, __u32 start, int len) +{ + if (start == INT_MAX) + return; + + if (len == 1) + reiserfs_warning (fp, " %d", start); + else + reiserfs_warning (fp, " %d(%d)", start, len); +} + + +void print_indirect_item (FILE * fp, struct buffer_head * bh, int item_num) +{ + struct item_head * ih; + int j; + __u32 * unp, prev = INT_MAX; + int num; + + ih = B_N_PITEM_HEAD (bh, item_num); + unp = (__u32 *)B_I_PITEM (bh, ih); + + if (ih->ih_item_len % UNFM_P_SIZE) + reiserfs_warning (fp, "print_indirect_item: invalid item len"); + + reiserfs_warning (fp, "%d pointers\n[ ", I_UNFM_NUM (ih)); + for (j = 0; j < I_UNFM_NUM (ih); j ++) { + if (sequence_finished (prev, &num, unp[j])) { + print_sequence (fp, prev, num); + start_new_sequence (&prev, &num, unp[j]); + } + } + print_sequence (fp, prev, num); + reiserfs_warning (fp, "]\n"); +} + + +char timebuf[256]; + +char * timestamp (time_t t) +{ + strftime (timebuf, 256, "%m/%d/%Y %T", localtime (&t)); + return timebuf; +} + +static int print_stat_data (FILE * fp, struct buffer_head * bh, struct item_head * ih, int alltimes) +{ + struct stat_data * sd = (struct stat_data *)B_I_PITEM (bh, ih); + struct stat_data_v1 * sd_v1 = (struct stat_data_v1 *)B_I_PITEM (bh, ih); + int retval; + + + /* we can not figure out whether it is new stat data or old by key_format + macro. Stat data's key looks identical in both formats */ + if (ih_key_format (ih) == KEY_FORMAT_1) { + reiserfs_warning (fp, "(OLD SD), mode %M, size %u, nlink %u, uid %d, FDB %d, mtime %s blocks %d", + sd_v1->sd_mode, sd_v1->sd_size, sd_v1->sd_nlink, sd_v1->sd_uid, + sd_v1->sd_first_direct_byte, timestamp (sd_v1->sd_mtime), sd_v1->u.sd_blocks); + retval = (S_ISLNK (sd_v1->sd_mode)) ? 1 : 0; + } else { + reiserfs_warning (fp, "(NEW SD), mode %M, size %Lu, nlink %u, mtime %s blocks %d", + sd->sd_mode, sd->sd_size, sd->sd_nlink, + timestamp (sd->sd_mtime), sd->sd_blocks); + retval = (S_ISLNK (sd->sd_mode)) ? 1 : 0; + } + + if (alltimes) + reiserfs_warning (fp, "%s %s\n", timestamp (sd->sd_ctime), timestamp (sd->sd_atime)); + reiserfs_warning (fp, "\n"); + return retval; +} + + +/* this prints internal nodes (4 keys/items in line) (dc_number, + dc_size)[k_dirid, k_objectid, k_offset, k_uniqueness](dc_number, + dc_size)...*/ +static int print_internal (FILE * fp, struct buffer_head * bh, int first, int last) +{ + struct key * key; + struct disk_child * dc; + int i; + int from, to; + + if (!is_internal_node (bh)) + return 1; + + if (first == -1) { + from = 0; + to = B_NR_ITEMS (bh); + } else { + from = first; + to = last < B_NR_ITEMS (bh) ? last : B_NR_ITEMS (bh); + } + + reiserfs_warning (fp, "INTERNAL NODE (%ld) contains %b\n", bh->b_blocknr, bh); + + dc = B_N_CHILD (bh, from); + reiserfs_warning (fp, "PTR %d: %y ", from, dc); + + for (i = from, key = B_N_PDELIM_KEY (bh, from), dc ++; i < to; i ++, key ++, dc ++) { + reiserfs_warning (fp, "KEY %d: %20k PTR %d: %20y ", i, key, i + 1, dc); + if (i && i % 4 == 0) + reiserfs_warning (fp, "\n"); + } + reiserfs_warning (fp, "\n"); + return 0; +} + + + +static int is_symlink = 0; +static int print_leaf (FILE * fp, reiserfs_filsys_t fs, struct buffer_head * bh, + int print_mode, int first, int last) +{ + struct block_head * blkh; + struct item_head * ih; + int i; + int from, to; + + if (!is_leaf_node (bh)) + return 1; + + blkh = B_BLK_HEAD (bh); + ih = B_N_PITEM_HEAD (bh,0); + + reiserfs_warning (fp, "\n===================================================================\n"); + reiserfs_warning (fp, "LEAF NODE (%ld) contains %b\n", bh->b_blocknr, bh); + + if (!(print_mode & PRINT_LEAF_ITEMS)) { + reiserfs_warning (fp, "FIRST ITEM_KEY: %k, LAST ITEM KEY: %k\n", + &(ih->ih_key), &((ih + blkh->blk_nr_item - 1)->ih_key)); + return 0; + } + + if (first < 0 || first > blkh->blk_nr_item - 1) + from = 0; + else + from = first; + + if (last < 0 || last > blkh->blk_nr_item) + to = blkh->blk_nr_item; + else + to = last; + + + reiserfs_warning (fp, + "-------------------------------------------------------------------------------\n" + "|###|type|ilen|f/sp| loc|fmt|fsck| key |\n" + "| | | |e/cn| | |need| |\n"); + for (i = from; i < to; i++) { + reiserfs_warning (fp, + "-------------------------------------------------------------------------------\n" + "|%3d|%30H|\n", i, ih + i); + + if (I_IS_STAT_DATA_ITEM(ih+i) && print_mode & PRINT_ITEM_DETAILS) { + is_symlink = print_stat_data (fp, bh, ih + i, 0/*all times*/); + continue; + } + + if (I_IS_DIRECTORY_ITEM(ih+i) && print_mode & PRINT_ITEM_DETAILS) { + print_directory_item (fp, fs, bh, ih+i); + continue; + } + + if (I_IS_INDIRECT_ITEM(ih+i) && print_mode & PRINT_ITEM_DETAILS) { + print_indirect_item (fp, bh, i); + continue; + } + + if (I_IS_DIRECT_ITEM(ih+i)) { + int j = 0; + if (is_symlink || print_mode & PRINT_DIRECT_ITEMS) { + reiserfs_warning (fp, "\""); + while (j < ih[i].ih_item_len) { + if (B_I_PITEM(bh,ih+i)[j] == 10) + reiserfs_warning (fp, "\\n"); + else + reiserfs_warning (fp, "%c", B_I_PITEM(bh,ih+i)[j]); + j ++; + } + reiserfs_warning (fp, "\"\n"); + } + continue; + } + } + reiserfs_warning (fp, "===================================================================\n"); + return 0; +} + + + +/* return 1 if this is not super block */ +static int print_super_block (FILE * fp, struct buffer_head * bh) +{ + struct reiserfs_super_block * rs = (struct reiserfs_super_block *)(bh->b_data); + int skipped, data_blocks; + + if (is_reiser2fs_magic_string (rs)) + reiserfs_warning (fp, "Super block of format 3.6 found on the 0x%x in block %ld\n", + bh->b_dev, bh->b_blocknr); + else if (is_reiserfs_magic_string (rs)) + reiserfs_warning (fp, "Super block of format 3.5 found on the 0x%x in block %ld\n", + bh->b_dev, bh->b_blocknr); + else if (is_prejournaled_reiserfs (rs)) { + reiserfs_warning (fp, "Prejournaled reiserfs super block found. Not supported here. Use proper tools instead\n"); + return 1; + } else + // no reiserfs signature found in the block + return 1; + + reiserfs_warning (fp, "Block count %u\n", rs_block_count (rs)); + reiserfs_warning (fp, "Blocksize %d\n", rs_blocksize (rs)); + reiserfs_warning (fp, "Free blocks %u\n", rs_free_blocks (rs)); + skipped = bh->b_blocknr; // FIXME: this would be confusing if + // someone stores reiserfs super block in reiserfs ;) + data_blocks = rs_block_count (rs) - skipped - 1 - + rs_bmap_nr (rs) - (rs_journal_size (rs) + 1) - rs_free_blocks (rs); + reiserfs_warning (fp, "Busy blocks (skipped %d, bitmaps - %d, journal blocks - %d\n" + "1 super blocks, %d data blocks\n", + skipped, rs_bmap_nr (rs), + (rs_journal_size (rs) + 1), data_blocks); + reiserfs_warning (fp, "Root block %u\n", rs_root_block (rs)); + reiserfs_warning (fp, "Journal block (first) %d\n", rs_journal_start (rs)); + reiserfs_warning (fp, "Journal dev %d\n", rs->s_v1.s_journal_dev); + reiserfs_warning (fp, "Journal orig size %d\n", rs_journal_size (rs)); + reiserfs_warning (fp, "Filesystem state %s\n", (rs->s_v1.s_state == REISERFS_VALID_FS) ? "VALID" : "ERROR"); + if (fsck_state (rs) == TREE_IS_BUILT) + reiserfs_warning (fp, "fsck pass 2 completion code set\n"); + +#if 0 + __u32 s_journal_trans_max ; /* max number of blocks in a transaction. */ + __u32 s_journal_block_count ; /* total size of the journal. can change over time */ + __u32 s_journal_max_batch ; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age ; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age ; /* in seconds, how old can a transaction be */ +#endif + reiserfs_warning (fp, "Tree height %d\n", rs_tree_height (rs)); + reiserfs_warning (fp, "Hash function used to sort names: %s\n", + code2name (rs_hash (rs))); + reiserfs_warning (fp, "Objectid map size %d, max %d\n", rs_objectid_map_size (rs), + rs_objectid_map_max_size (rs)); + reiserfs_warning (fp, "Version %d\n", rs_version (rs)); + return 0; +} + + +static int print_desc_block (FILE * fp, struct buffer_head * bh) +{ + struct reiserfs_journal_desc * desc; + + desc = (struct reiserfs_journal_desc *)(bh->b_data); + + if (memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8)) + return 1; + + reiserfs_warning (fp, "Desc block %lu (j_trans_id %ld, j_mount_id %ld, j_len %ld)", + bh->b_blocknr, desc->j_trans_id, desc->j_mount_id, desc->j_len); + + return 0; +} + + +void print_block (FILE * fp, reiserfs_filsys_t fs, + struct buffer_head * bh, ...)//int print_mode, int first, int last) +{ + va_list args; + int mode, first, last; + + va_start (args, bh); + + if ( ! bh ) { + reiserfs_warning (stderr, "print_block: buffer is NULL\n"); + return; + } + + mode = va_arg (args, int); + first = va_arg (args, int); + last = va_arg (args, int); + if (print_desc_block (fp, bh)) + if (print_super_block (fp, bh)) + if (print_leaf (fp, fs, bh, mode, first, last)) + if (print_internal (fp, bh, first, last)) + reiserfs_warning (fp, "Block %ld contains unformatted data\n", bh->b_blocknr); +} + + +void print_tb (int mode, int item_pos, int pos_in_item, struct tree_balance * tb, char * mes) +{ + int h = 0; + int i; + struct buffer_head * tbSh, * tbFh; + + + if (!tb) + return; + + printf ("\n********************** PRINT_TB for %s *******************\n", mes); + printf ("MODE=%c, ITEM_POS=%d POS_IN_ITEM=%d\n", mode, item_pos, pos_in_item); + printf ("*********************************************************************\n"); + + printf ("* h * S * L * R * F * FL * FR * CFL * CFR *\n"); +/* +01234567890123456789012345678901234567890123456789012345678901234567890123456789 + 1 2 3 4 5 6 7 8 + printk ("*********************************************************************\n"); +*/ + + + for (h = 0; h < sizeof(tb->insert_size) / sizeof (tb->insert_size[0]); h ++) { + if (PATH_H_PATH_OFFSET (tb->tb_path, h) <= tb->tb_path->path_length && + PATH_H_PATH_OFFSET (tb->tb_path, h) > ILLEGAL_PATH_ELEMENT_OFFSET) { + tbSh = PATH_H_PBUFFER (tb->tb_path, h); + tbFh = PATH_H_PPARENT (tb->tb_path, h); + } else { + /* printk ("print_tb: h=%d, PATH_H_PATH_OFFSET=%d, path_length=%d\n", + h, PATH_H_PATH_OFFSET (tb->tb_path, h), tb->tb_path->path_length);*/ + tbSh = 0; + tbFh = 0; + } + printf ("* %d * %3ld(%2d) * %3ld(%2d) * %3ld(%2d) * %5ld * %5ld * %5ld * %5ld * %5ld *\n", + h, + (tbSh) ? (tbSh->b_blocknr):(-1), + (tbSh) ? tbSh->b_count : -1, + (tb->L[h]) ? (tb->L[h]->b_blocknr):(-1), + (tb->L[h]) ? tb->L[h]->b_count : -1, + (tb->R[h]) ? (tb->R[h]->b_blocknr):(-1), + (tb->R[h]) ? tb->R[h]->b_count : -1, + (tbFh) ? (tbFh->b_blocknr):(-1), + (tb->FL[h]) ? (tb->FL[h]->b_blocknr):(-1), + (tb->FR[h]) ? (tb->FR[h]->b_blocknr):(-1), + (tb->CFL[h]) ? (tb->CFL[h]->b_blocknr):(-1), + (tb->CFR[h]) ? (tb->CFR[h]->b_blocknr):(-1)); + } + + printf ("*********************************************************************\n"); + + + /* print balance parameters for leaf level */ + h = 0; + printf ("* h * size * ln * lb * rn * rb * blkn * s0 * s1 * s1b * s2 * s2b * curb * lk * rk *\n"); + printf ("* %d * %4d * %2d * %2d * %2d * %2d * %4d * %2d * %2d * %3d * %2d * %3d * %4d * %2d * %2d *\n", + h, tb->insert_size[h], tb->lnum[h], tb->lbytes, tb->rnum[h],tb->rbytes, tb->blknum[h], + tb->s0num, tb->s1num,tb->s1bytes, tb->s2num, tb->s2bytes, tb->cur_blknum, tb->lkey[h], tb->rkey[h]); + + +/* this prints balance parameters for non-leaf levels */ + do { + h++; + printf ("* %d * %4d * %2d * * %2d * * %2d *\n", + h, tb->insert_size[h], tb->lnum[h], tb->rnum[h], tb->blknum[h]); + } while (tb->insert_size[h]); + + printf ("*********************************************************************\n"); + + + /* print FEB list (list of buffers in form (bh (b_blocknr, b_count), that will be used for new nodes) */ + h = 0; + for (i = 0; i < sizeof (tb->FEB) / sizeof (tb->FEB[0]); i ++) + printf ("%s%p (%lu %d)", i == 0 ? "FEB list: " : ", ", tb->FEB[i], tb->FEB[i] ? tb->FEB[i]->b_blocknr : 0, + tb->FEB[i] ? tb->FEB[i]->b_count : 0); + printf ("\n"); + + printf ("********************** END OF PRINT_TB *******************\n\n"); + +} + + +static void print_bmap_block (FILE * fp, int i, struct buffer_head * bmap, int blocks, int silent) +{ + int j, k; + int bits = bmap->b_size * 8; + int zeros = 0, ones = 0; + + reiserfs_warning (fp, "#%d: block %lu: ", i, bmap->b_blocknr); + + if (test_bit (0, bmap->b_data)) { + /* first block addressed by this bitmap block is used */ + ones ++; + if (!silent) + reiserfs_warning (fp, "Busy (%d-", i * bits); + for (j = 1; j < blocks; j ++) { + while (test_bit (j, bmap->b_data)) { + ones ++; + if (j == blocks - 1) { + if (!silent) + reiserfs_warning (fp, "%d)\n", j + i * bits); + goto end; + } + j++; + } + if (!silent) + reiserfs_warning (fp, "%d) Free(%d-", j - 1 + i * bits, j + i * bits); + + while (!test_bit (j, bmap->b_data)) { + zeros ++; + if (j == blocks - 1) { + if (!silent) + reiserfs_warning (fp, "%d)\n", j + i * bits); + goto end; + } + j++; + } + if (!silent) + reiserfs_warning (fp, "%d) Busy(%d-", j - 1 + i * bits, j + i * bits); + + j --; + end: + } + } else { + /* first block addressed by this bitmap is free */ + zeros ++; + if (!silent) + reiserfs_warning (fp, "Free (%d-", i * bits); + for (j = 1; j < blocks; j ++) { + k = 0; + while (!test_bit (j, bmap->b_data)) { + k ++; + if (j == blocks - 1) { + if (!silent) + reiserfs_warning (fp, "%d)\n", j + i * bits); + zeros += k; + goto end2; + } + j++; + } + zeros += k; + if (!silent) + reiserfs_warning (fp, "%d) Busy(%d-", j - 1 + i * bits, j + i * bits); + + k = 0; + while (test_bit (j, bmap->b_data)) { + ones ++; + if (j == blocks - 1) { + if (!silent) + reiserfs_warning (fp, "%d)\n", j + i * bits); + ones += k; + goto end2; + } + j++; + } + ones += k; + if (!silent) + reiserfs_warning (fp, "%d) Free(%d-", j - 1 + i * bits, j + i * bits); + + j --; + end2: + } + } + + reiserfs_warning (fp, "used %d, free %d\n", ones, zeros); +} + + +/* if silent == 1, do not print details */ +void print_bmap (FILE * fp, reiserfs_filsys_t s, int silent) +{ + int bmapnr = SB_BMAP_NR (s); + int i; + int blocks = s->s_blocksize * 8; /* adressed by bitmap */ + + reiserfs_warning (fp, "Bitmap blocks are:\n"); + for (i = 0; i < bmapnr; i ++) { + + if (i == bmapnr - 1) + if (SB_BLOCK_COUNT (s) % (s->s_blocksize * 8)) + blocks = SB_BLOCK_COUNT (s) % (s->s_blocksize * 8); + print_bmap_block (fp, i, SB_AP_BITMAP(s)[i], blocks, silent); + } + + /* check unused part of last bitmap */ + { + int bad_unused_bitmap = 0; + int ones; + + ones = s->s_blocksize * 8 - SB_BLOCK_COUNT (s) % (s->s_blocksize * 8); + if (ones == s->s_blocksize * 8) + ones = 0; + + for (i = s->s_blocksize * 8; --i >= blocks; ) + if (!test_bit (i, SB_AP_BITMAP (s)[bmapnr - 1]->b_data)) + bad_unused_bitmap ++; + + if (bad_unused_bitmap) { + reiserfs_warning (fp, "Unused part of bitmap is wrong: should be %d ones, found %d zeros\n", + ones, bad_unused_bitmap); + } + } + +} + + + +void print_objectid_map (FILE * fp, reiserfs_filsys_t fs) +{ + int i; + struct reiserfs_super_block * rs; + __u32 * omap; + + rs = fs->s_rs; + if (fs->s_version == REISERFS_VERSION_2) + omap = (__u32 *)(rs + 1); + else if (fs->s_version == REISERFS_VERSION_1) + omap = (__u32 *)((struct reiserfs_super_block_v1 *)rs + 1); + else { + reiserfs_warning (fp, "print_objectid_map: proper signature is not found\n"); + return; + } + + reiserfs_warning (fp, "Map of objectids (super block size %d)\n", (char *)omap - (char *)rs); + + for (i = 0; i < SB_OBJECTID_MAP_SIZE (fs); i ++) { + if (i % 2 == 0) + reiserfs_warning (fp, "busy(%u-%u) ", omap[i], omap[i+1] - 1); + else + reiserfs_warning (fp, "free(%u-%u) ", + omap[i], ((i+1) == SB_OBJECTID_MAP_SIZE (fs)) ? -1 : omap[i+1] - 1); + } + + reiserfs_warning (fp, "\nObject id array has size %d (max %d):", SB_OBJECTID_MAP_SIZE (fs), + SB_OBJECTID_MAP_MAXSIZE (fs)); + + for (i = 0; i < SB_OBJECTID_MAP_SIZE (fs); i ++) + reiserfs_warning (fp, "%s%u ", i % 2 ? "" : "*", omap[i]); + reiserfs_warning (fp, "\n"); + +} + +#if 0 +/* the below is from fileutils-4.0-66 (shortened) */ + +/* Look at read, write, and execute bits in BITS and set + flags in CHARS accordingly. */ + +static void +rwx (short unsigned int bits, char *chars) +{ + chars[0] = (bits & S_IRUSR) ? 'r' : '-'; + chars[1] = (bits & S_IWUSR) ? 'w' : '-'; + chars[2] = (bits & S_IXUSR) ? 'x' : '-'; +} + +/* snip */ + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for regular files + '?' for any other file type. */ + +static char +ftypelet (long int bits) +{ +#ifdef S_ISBLK + if (S_ISBLK (bits)) + return 'b'; +#endif + if (S_ISCHR (bits)) + return 'c'; + if (S_ISDIR (bits)) + return 'd'; + if (S_ISREG (bits)) + return '-'; +#ifdef S_ISFIFO + if (S_ISFIFO (bits)) + return 'p'; +#endif +#ifdef S_ISLNK + if (S_ISLNK (bits)) + return 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK (bits)) + return 's'; +#endif + + return '?'; +} + +/* Like filemodestring, but only the relevant part of the `struct stat' + is given as an argument. */ + +static void +mode_string (short unsigned int mode, char *str) +{ + str[0] = ftypelet ((long) mode); + rwx ((mode & 0700) << 0, &str[1]); + rwx ((mode & 0070) << 3, &str[4]); + rwx ((mode & 0007) << 6, &str[7]); +} + + +char * st_mode2string (short unsigned int mode, char * buf) +{ + mode_string (mode, buf); + buf[10] = 0; + return buf; +} + + +#endif diff --git a/reiserfscore/reiserfslib.c b/reiserfscore/reiserfslib.c new file mode 100644 index 0000000..b024ac8 --- /dev/null +++ b/reiserfscore/reiserfslib.c @@ -0,0 +1,766 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include "includes.h" + + +/* fixme: this assumes that journal start and journal size are set + correctly */ +static void check_first_bitmap (reiserfs_filsys_t fs, char * bitmap) +{ + int i; + int bad; + + bad = 0; + for (i = 0; i < rs_journal_start (fs->s_rs) + + rs_journal_size (fs->s_rs) + 1; i ++) { + if (!test_bit (i, bitmap)) { + bad = 1; + /*reiserfs_warning ("block %d is marked free in the first bitmap, fixed\n", i);*/ + /*set_bit (i, bitmap);*/ + } + } + if (bad) + reiserfs_warning (stderr, "reiserfs_open: first bitmap looks corrupted\n"); +} + + +/* read bitmap blocks */ +void reiserfs_read_bitmap_blocks (reiserfs_filsys_t fs) +{ + struct reiserfs_super_block * rs = fs->s_rs; + struct buffer_head * bh = SB_BUFFER_WITH_SB(fs); + int fd = fs->s_dev; + unsigned long block; + int i; + + /* read bitmaps, and correct a bit if necessary */ + SB_AP_BITMAP (fs) = getmem (sizeof (void *) * rs_bmap_nr (rs)); + for (i = 0, block = bh->b_blocknr + 1; + i < rs_bmap_nr (rs); i ++) { + SB_AP_BITMAP (fs)[i] = bread (fd, block, fs->s_blocksize); + if (!SB_AP_BITMAP (fs)[i]) { + reiserfs_warning (stderr, "reiserfs_open: bread failed reading bitmap #%d (%lu)\n", i, block); + SB_AP_BITMAP (fs)[i] = getblk (fd, block, fs->s_blocksize); + memset (SB_AP_BITMAP (fs)[i]->b_data, 0xff, fs->s_blocksize); + set_bit (BH_Uptodate, &SB_AP_BITMAP (fs)[i]->b_state); + } + + /* all bitmaps have to have itself marked used on it */ + if (bh->b_blocknr == 16) { + if (!test_bit (block % (fs->s_blocksize * 8), SB_AP_BITMAP (fs)[i]->b_data)) { + reiserfs_warning (stderr, "reiserfs_open: bitmap %d was marked free\n", i); + /*set_bit (block % (fs->s_blocksize * 8), SB_AP_BITMAP (fs)[i]->b_data);*/ + } + } else { + /* bitmap not spread over partition: fixme: does not + work when number of bitmaps => 32768 */ + if (!test_bit (block, SB_AP_BITMAP (fs)[0]->b_data)) { + reiserfs_warning (stderr, "reiserfs_open: bitmap %d was marked free\n", i); + /*set_bit (block, SB_AP_BITMAP (fs)[0]->b_data);*/ + } + } + + if (i == 0) { + /* first bitmap has to have marked used super block + and journal areas */ + check_first_bitmap (fs, SB_AP_BITMAP (fs)[i]->b_data); + } + + block = (bh->b_blocknr == 16 ? ((i + 1) * fs->s_blocksize * 8) : (block + 1)); + } +} + + +void reiserfs_free_bitmap_blocks (reiserfs_filsys_t fs) +{ + int i; + + /* release bitmaps if they were read */ + if (SB_AP_BITMAP (fs)) { + for (i = 0; i < SB_BMAP_NR (fs); i ++) + brelse (SB_AP_BITMAP (fs) [i]); + freemem (SB_AP_BITMAP (fs)); + } + +} + +/* read super block and bitmaps. fixme: only 4k blocks, pre-journaled format + is refused */ +reiserfs_filsys_t reiserfs_open (char * filename, int flags, int *error, void * vp) +{ + reiserfs_filsys_t fs; + struct buffer_head * bh; + struct reiserfs_super_block * rs; + int fd, i; + + fd = open (filename, flags | O_LARGEFILE); + if (fd == -1) { + if (error) + *error = errno; + return 0; + } + + fs = getmem (sizeof (*fs)); + fs->s_dev = fd; + fs->s_vp = vp; + asprintf (&fs->file_name, "%s", filename); + + /* reiserfs super block is either in 16-th or in 2-nd 4k block of the + device */ + for (i = 16; i > 0; i -= 14) { + bh = bread (fd, i, 4096); + if (!bh) { + reiserfs_warning (stderr, "reiserfs_open: bread failed reading block %d\n", i); + } else { + rs = (struct reiserfs_super_block *)bh->b_data; + + if (is_reiser2fs_magic_string (rs) || is_reiserfs_magic_string (rs)) + goto found; + + /* reiserfs signature is not found at the i-th 4k block */ + brelse (bh); + } + } + + reiserfs_warning (stderr, "reiserfs_open: neither new nor old reiserfs format " + "found on %s\n", filename); + if (error) + *error = 0; + return fs; + + found: + + /* fixme: we could make some check to make sure that super block looks + correctly */ + fs->s_version = is_reiser2fs_magic_string (rs) ? REISERFS_VERSION_2 : + REISERFS_VERSION_1; + fs->s_blocksize = rs_blocksize (rs); + fs->s_hash_function = code2func (rs_hash (rs)); + SB_BUFFER_WITH_SB (fs) = bh; + fs->s_rs = rs; + fs->s_flags = flags; /* O_RDONLY or O_RDWR */ + fs->s_vp = vp; + + + reiserfs_read_bitmap_blocks(fs); + + return fs; + +} + + +int no_reiserfs_found (reiserfs_filsys_t fs) +{ + return (fs->s_blocksize == 0) ? 1 : 0; +} + + +int new_format (reiserfs_filsys_t fs) +{ + return fs->s_sbh->b_blocknr != 2; +} + + +int spread_bitmaps (reiserfs_filsys_t fs) +{ + return fs->s_sbh->b_blocknr != 2; +} + + +void reiserfs_reopen (reiserfs_filsys_t fs, int flag) +{ + close (fs->s_dev); + fs->s_dev = open (fs->file_name, flag | O_LARGEFILE); + if (fs->s_dev == -1) + die ("reiserfs_reopen: could not reopen device: %m"); +} + + +int filesystem_dirty (reiserfs_filsys_t fs) +{ + return fs->s_dirt; +} + + +void mark_filesystem_dirty (reiserfs_filsys_t fs) +{ + fs->s_dirt = 1; +} + + +/* flush all changes made on a filesystem */ +void reiserfs_flush (reiserfs_filsys_t fs) +{ + flush_buffers (); +} + + +/* free all memory involved into manipulating with filesystem */ +void reiserfs_free (reiserfs_filsys_t fs) +{ + reiserfs_free_bitmap_blocks(fs); + + /* release super block and memory used by filesystem handler */ + brelse (SB_BUFFER_WITH_SB (fs)); + + free_buffers (); + + free (fs->file_name); + freemem (fs); +} + + +void reiserfs_close (reiserfs_filsys_t fs) +{ + reiserfs_flush (fs); + reiserfs_free (fs); +} + + +int reiserfs_new_blocknrs (reiserfs_filsys_t fs, + unsigned long * free_blocknrs, unsigned long start, int amount_needed) +{ + if (fs->block_allocator) + return fs->block_allocator (fs, free_blocknrs, start, amount_needed); + die ("block allocator is not defined\n"); + return 0; +} + + +int reiserfs_free_block (reiserfs_filsys_t fs, unsigned long block) +{ + if (fs->block_deallocator) + return fs->block_deallocator (fs, block); + die ("block allocator is not defined\n"); + return 0; +} + + +typedef int (comp_function_t) (void * key1, void * key2); + +inline int _bin_search (void * key, void * base, int num, int width, __u32 *ppos, comp_function_t comp_func) +{ + int rbound, lbound, j; + + if (num == 0) { + /* objectid map may be 0 elements long */ + *ppos = 0; + return ITEM_NOT_FOUND; + } + + lbound = 0; + rbound = num - 1; + + for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { + switch (comp_func ((void *)((char *)base + j * width), key ) ) { + case -1:/* second is greater */ + lbound = j + 1; + continue; + + case 1: /* first is greater */ + if (j == 0) { + *ppos = lbound; + return ITEM_NOT_FOUND; + } + rbound = j - 1; + continue; + + case 0: + *ppos = j; + return ITEM_FOUND; + } + } + + *ppos = lbound; + return ITEM_NOT_FOUND; +} + +#if 0 +static inline int _bin_search (void * key, void * base, int num, int width, __u32 *ppos) +{ + __u32 rbound, lbound, j; + + lbound = 0; + rbound = num - 1; + for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { + switch (comp_keys ((void *)((char *)base + j * width), key)) { + case -1:/* second is greater */ + lbound = j + 1; + continue; + + case 1: /* first is greater */ + if (j == 0) { + *ppos = lbound; + return ITEM_NOT_FOUND; + } + rbound = j - 1; + continue; + + case 0: + *ppos = j; + return ITEM_FOUND; + } + } + + *ppos = lbound; + return ITEM_NOT_FOUND; +} +#endif + +static int _search_by_key (reiserfs_filsys_t fs, struct key * key, struct path * path) +{ + struct buffer_head * bh; + unsigned long block = SB_ROOT_BLOCK (fs); + struct path_element * curr; + int retval; + + path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET; + while (1) { + curr = PATH_OFFSET_PELEMENT (path, ++ path->path_length); + bh = curr->pe_buffer = bread (fs->s_dev, block, fs->s_blocksize); + if (bh == 0) { + path->path_length --; + pathrelse (path); + return ITEM_NOT_FOUND; + } + retval = _bin_search (key, B_N_PKEY (bh, 0), B_NR_ITEMS (bh), + is_leaf_node (bh) ? IH_SIZE : KEY_SIZE, &(curr->pe_position), comp_keys); + if (retval == ITEM_FOUND) { + /* key found, return if this is leaf level */ + if (is_leaf_node (bh)) { + path->pos_in_item = 0; + return ITEM_FOUND; + } + curr->pe_position ++; + } else { + /* key not found in the node */ + if (is_leaf_node (bh)) + return ITEM_NOT_FOUND; + } + block = B_N_CHILD_NUM (bh, curr->pe_position); + } + printf ("search_by_key: you can not get here\n"); + return ITEM_NOT_FOUND; +} + + +static int comp_dir_entries (void * p1, void * p2) +{ + __u32 deh_offset; + __u32 * off1, * off2; + + off1 = p1; + off2 = p2; + deh_offset = le32_to_cpu (*off1); + + if (deh_offset < *off2) + return -1; + if (deh_offset > *off2) + return 1; + return 0; +} + + +static struct key * _get_rkey (struct path * path) +{ + int pos, offset = path->path_length; + struct buffer_head * bh; + + if (offset < FIRST_PATH_ELEMENT_OFFSET) + die ("_get_rkey: illegal offset in the path (%d)", offset); + + while (offset-- > FIRST_PATH_ELEMENT_OFFSET) { + if (! buffer_uptodate (PATH_OFFSET_PBUFFER (path, offset))) + die ("_get_rkey: parent is not uptodate"); + + /* Parent at the path is not in the tree now. */ + if (! B_IS_IN_TREE (bh = PATH_OFFSET_PBUFFER (path, offset))) + die ("_get_rkey: buffer on the path is not in tree"); + + /* Check whether position in the parrent is correct. */ + if ((pos = PATH_OFFSET_POSITION (path, offset)) > B_NR_ITEMS (bh)) + die ("_get_rkey: invalid position (%d) in the path", pos); + + /* Check whether parent at the path really points to the child. */ + if (B_N_CHILD_NUM (bh, pos) != PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr) + die ("_get_rkey: invalid block number (%d). Must be %d", + B_N_CHILD_NUM (bh, pos), PATH_OFFSET_PBUFFER (path, offset + 1)->b_blocknr); + + /* Return delimiting key if position in the parent is not the last one. */ + if (pos != B_NR_ITEMS (bh)) + return B_N_PDELIM_KEY (bh, pos); + } + + /* there is no right delimiting key */ + return 0; +} + + +/* NOTE: this only should be used to look for keys who exists */ +int _search_by_entry_key (reiserfs_filsys_t fs, struct key * key, + struct path * path) +{ + struct buffer_head * bh; + int item_pos; + struct item_head * ih; + struct key tmpkey; + + if (_search_by_key (fs, key, path) == ITEM_FOUND) { + path->pos_in_item = 0; + return POSITION_FOUND; + } + + bh = get_bh (path); + item_pos = get_item_pos (path); + ih = get_ih (path); + + if (item_pos == 0) { + /* key is less than the smallest key in the tree */ + if (not_of_one_file (&(ih->ih_key), key)) + /* there are no items of that directory */ + return DIRECTORY_NOT_FOUND; + + if (!is_direntry_ih (ih)) + reiserfs_panic ("_search_by_entry_key: found item is not of directory type %H", + ih); + + /* key we looked for should be here */ + path->pos_in_item = 0; + return POSITION_NOT_FOUND; + } + + /* take previous item */ + item_pos --; + ih --; + PATH_LAST_POSITION (path) --; + + if (not_of_one_file (&(ih->ih_key), key) || !is_direntry_ih (ih)) { + /* previous item belongs to another object or is stat data, check next + item */ + + item_pos ++; + PATH_LAST_POSITION (path) ++; + + if (item_pos < B_NR_ITEMS (bh)) { + /* next item is in the same node */ + ih ++; + if (not_of_one_file (&(ih->ih_key), key)) { + /* there are no items of that directory */ + path->pos_in_item = 0; + return DIRECTORY_NOT_FOUND; + } + + if (!is_direntry_ih (ih)) + reiserfs_panic ("_search_by_entry_key: %k is not a directory", + key); + } else { + /* next item is in right neighboring node */ + struct key * next_key = _get_rkey (path); + + if (next_key == 0 || not_of_one_file (next_key, key)) { + /* there are no items of that directory */ + path->pos_in_item = 0; + return DIRECTORY_NOT_FOUND; + } + + if (!is_direntry_key (next_key)) + reiserfs_panic ("_search_by_entry_key: %k is not a directory", + key); + + /* we got right delimiting key - search for it - the entry will be + pasted in position 0 */ + copy_key (&tmpkey, next_key); + pathrelse (path); + if (_search_by_key (fs, &tmpkey, path) != ITEM_FOUND || PATH_LAST_POSITION (path) != 0) + reiserfs_panic ("_search_by_entry_key: item corresponding to delimiting key %k not found", + &tmpkey); + } + + /* next item is the part of this directory */ + path->pos_in_item = 0; + return POSITION_NOT_FOUND; + } + + + /* previous item is part of desired directory */ + if (_bin_search (&(key->u.k_offset_v1.k_offset), B_I_DEH (bh, ih), ih_entry_count (ih), + DEH_SIZE, &(path->pos_in_item), comp_dir_entries) == ITEM_FOUND) + return POSITION_FOUND; + + return POSITION_NOT_FOUND; +} + + +static void _init_tb_struct (struct tree_balance * tb, reiserfs_filsys_t fs, + struct path * path, int size) +{ + memset (tb, '\0', sizeof(struct tree_balance)); + tb->tb_sb = fs; + tb->tb_path = path; + + PATH_OFFSET_PBUFFER(path, ILLEGAL_PATH_ELEMENT_OFFSET) = NULL; + PATH_OFFSET_POSITION(path, ILLEGAL_PATH_ELEMENT_OFFSET) = 0; + tb->insert_size[0] = size; +} + + +int reiserfs_remove_entry (reiserfs_filsys_t fs, struct key * key) +{ + struct path path; + struct tree_balance tb; + struct item_head * ih; + struct reiserfs_de_head * deh; + + if (_search_by_entry_key (fs, key, &path) != POSITION_FOUND) { + pathrelse (&path); + return 1; + } + + ih = get_ih (&path); + if (ih_entry_count (ih) == 1) { + _init_tb_struct (&tb, fs, &path, -(IH_SIZE + ih_item_len (ih))); + if (fix_nodes (M_DELETE, &tb, 0) != CARRY_ON) { + unfix_nodes (&tb); + return 1; + } + do_balance (&tb, 0, 0, M_DELETE, 0); + return 0; + } + + deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item; + _init_tb_struct (&tb, fs, &path, -(DEH_SIZE + entry_length (ih, deh, path.pos_in_item))); + if (fix_nodes (M_CUT, &tb, 0) != CARRY_ON) { + unfix_nodes (&tb); + return 1; + } + do_balance (&tb, 0, 0, M_CUT, 0); + return 0; +} + + + +void reiserfs_paste_into_item (reiserfs_filsys_t fs, struct path * path, + const void * body, int size) +{ + struct tree_balance tb; + + _init_tb_struct (&tb, fs, path, size); + + if (fix_nodes (M_PASTE, &tb, 0/*ih*/) != CARRY_ON) + reiserfs_panic ("reiserfs_paste_into_item: fix_nodes failed"); + + do_balance (&tb, 0, body, M_PASTE, 0/*zero num*/); +} + + +void reiserfs_insert_item (reiserfs_filsys_t fs, struct path * path, + struct item_head * ih, const void * body) +{ + struct tree_balance tb; + + _init_tb_struct (&tb, fs, path, IH_SIZE + ih_item_len(ih)); + if (fix_nodes (M_INSERT, &tb, ih) != CARRY_ON) + die ("reiserfs_insert_item: fix_nodes failed"); + + do_balance (&tb, ih, body, M_INSERT, 0/*zero num*/); +} + + +/*===========================================================================*/ + +static __u32 hash_value (reiserfs_filsys_t fs, char * name) +{ + __u32 res; + + if (!strcmp (name, ".")) + return DOT_OFFSET; + if (!strcmp (name, "..")) + return DOT_DOT_OFFSET; + + res = reiserfs_hash (fs) (name, strlen (name)); + res = GET_HASH_VALUE(res); + if (res == 0) + res = 128; + + return res; +} + + + +/* returns 0 if name is not found in a directory and objectid of + pointed object otherwise and returns minimal not used generation + counter. dies if found object is not a directory. */ +int reiserfs_find_entry (reiserfs_filsys_t fs, struct key * dir, char * name, + int * min_gen_counter) +{ + struct key entry_key; + int retval; + int i; + INITIALIZE_PATH (path); + struct item_head * ih; + struct reiserfs_de_head * deh; + struct key * rdkey; + __u32 hash; + + entry_key.k_dir_id = dir->k_dir_id; + entry_key.k_objectid = dir->k_objectid; + hash = hash_value (fs, name); + set_type_and_offset (KEY_FORMAT_1, &entry_key, hash, TYPE_DIRENTRY); + *min_gen_counter = 0; + + if (_search_by_entry_key (fs, &entry_key, &path) == DIRECTORY_NOT_FOUND) { + pathrelse (&path); + return 0; + } + + do { + ih = get_ih (&path); + deh = B_I_DEH (get_bh (&path), ih) + path.pos_in_item; + for (i = path.pos_in_item; i < ih_entry_count (ih); i ++, deh ++) { + if (GET_HASH_VALUE (deh_offset (deh)) != GET_HASH_VALUE (hash)) { + /* all entries having the same hash were scanned */ + pathrelse (&path); + return 0; + } + + if (GET_GENERATION_NUMBER (deh_offset (deh)) == *min_gen_counter) + (*min_gen_counter) ++; + + if (!memcmp (name_in_entry (deh, i), name, strlen (name))) { + pathrelse (&path); + return deh_objectid (deh) ? deh_objectid (deh) : 1; + } + } + + rdkey = _get_rkey (&path); + if (!rdkey || not_of_one_file (rdkey, dir)) { + pathrelse (&path); + return 0; + } + + if (!is_direntry_key (rdkey)) + reiserfs_panic ("reiserfs_find_entry: can not find name in broken directory yet"); + + /* next item is the item of the directory we are looking name in */ + if (GET_HASH_VALUE (get_offset (rdkey)) != hash) { + /* but there is no names with given hash */ + pathrelse (&path); + return 0; + } + + /* first name of that item may be a name we are looking for */ + entry_key = *rdkey; + pathrelse (&path); + retval = _search_by_entry_key (fs, &entry_key, &path); + if (retval != POSITION_FOUND) + reiserfs_panic ("reiserfs_find_entry: wrong delimiting key in the tree"); + + } while (1); + + return 0; +} + + +/* compose directory entry: dir entry head and name itself */ +char * make_entry (char * entry, char * name, struct key * key, __u32 offset) +{ + struct reiserfs_de_head * deh; + + if (!entry) + entry = getmem (DEH_SIZE + ROUND_UP (strlen (name))); + + memset (entry, 0, DEH_SIZE + ROUND_UP (strlen (name))); + deh = (struct reiserfs_de_head *)entry; + deh->deh_location = 0; + deh->deh_offset = cpu_to_le32 (offset); + deh->deh_state = 0; + mark_de_visible (deh); + + /* key of object entry will point to */ + deh->deh_dir_id = cpu_to_le32 (key->k_dir_id); + deh->deh_objectid = cpu_to_le32 (key->k_objectid); + + memcpy ((char *)(deh + 1), name, strlen (name)); + return entry; +} + + +/* add new name into a directory. If it exists in a directory - do + nothing */ +int reiserfs_add_entry (reiserfs_filsys_t fs, struct key * dir, char * name, + struct key * key, int fsck_need) +{ + struct item_head entry_ih = {{0,}, }; + char * entry; + int retval; + INITIALIZE_PATH(path); + int gen_counter; + int item_len; + __u32 hash; + + if (reiserfs_find_entry (fs, dir, name, &gen_counter)) + return 0; + + /* compose entry key to look for its place in the tree */ + entry_ih.ih_key.k_dir_id = cpu_to_le32 (dir->k_dir_id); + entry_ih.ih_key.k_objectid = cpu_to_le32 (dir->k_objectid); + hash = hash_value (fs, name) + gen_counter; + if (!strcmp (name, ".")) + hash = DOT_OFFSET; + if (!strcmp (name, "..")) + hash = DOT_DOT_OFFSET; + set_type_and_offset (KEY_FORMAT_1, &(entry_ih.ih_key), + hash, TYPE_DIRENTRY); + set_key_format (&entry_ih, KEY_FORMAT_1); + set_entry_count (&entry_ih, 1); + if (SB_VERSION (fs) == REISERFS_VERSION_2) + item_len = DEH_SIZE + ROUND_UP (strlen (name)); + else + item_len = DEH_SIZE + strlen (name); + set_ih_item_len (&entry_ih, item_len); + + /* fsck may need to insert item which was not reached yet */ + entry_ih.ih_format.fsck_need = fsck_need; + + entry = make_entry (0, name, key, get_offset (&(entry_ih.ih_key))); + + retval = _search_by_entry_key (fs, &(entry_ih.ih_key), &path); + switch (retval) { + case POSITION_NOT_FOUND: + reiserfs_paste_into_item (fs, &path, entry, item_len); + break; + + case DIRECTORY_NOT_FOUND: + ((struct reiserfs_de_head *)entry)->deh_location = cpu_to_le16 (DEH_SIZE); + reiserfs_insert_item (fs, &path, &entry_ih, entry); + break; + + default: + reiserfs_panic ("reiserfs_add_entry: looking for %k (inserting name \"%s\") " + "search_by_entry_key returned %d", + &(entry_ih.ih_key), name, retval); + } + + freemem (entry); + return item_len; +} + + +void copy_key (void * to, void * from) +{ + memcpy (to, from, KEY_SIZE); +} + + +void copy_short_key (void * to, void * from) +{ + memcpy (to, from, SHORT_KEY_SIZE); +} + + +void copy_item_head(void * p_v_to, void * p_v_from) +{ + memcpy (p_v_to, p_v_from, IH_SIZE); +} diff --git a/reiserfscore/stree.c b/reiserfscore/stree.c new file mode 100644 index 0000000..aa81791 --- /dev/null +++ b/reiserfscore/stree.c @@ -0,0 +1,475 @@ +/* + * Copyright 1996, 1997, 1998 Hans Reiser, see reiserfs/README for licensing and copyright details + */ + +/* + * Written by Anatoly P. Pinchuk pap@namesys.botik.ru + * Programm System Institute + * Pereslavl-Zalessky Russia + */ + +/* + * This file contains functions dealing with S+tree + * + * comp_keys + * comp_short_keys + * bin_search + * get_lkey + * get_rkey + * key_in_buffer + * decrement_bcount + * decrement_counters_in_path + * pathrelse + * search_by_key + * search_for_position_by_key + * comp_items + * prepare_for_delete_or_cut + * calc_deleted_bytes_number + * init_tb_struct + * reiserfs_delete_item + * indirect_to_direct + * maybe_indirect_to_direct + * reiserfs_cut_from_item + * reiserfs_cut_dir_entry + * reiserfs_paste_into_item + * reiserfs_insert_item + */ +#include "includes.h" + + +/* Does the buffer contain a disk block which is in the tree. */ +inline int B_IS_IN_TREE (struct buffer_head * p_s_bh) +{ + +#ifdef CONFIG_REISERFS_CHECK + if ( node_level (p_s_bh) > MAX_HEIGHT ) { + reiserfs_panic(0, "PAP-1010: B_IS_IN_TREE: block (%b) has too big level (%z)", + p_s_bh, p_s_bh); + } +#endif + + return ( node_level (p_s_bh) != FREE_LEVEL ); +} + + +/* + Compare keys using REISERFS_SHORT_KEY_LEN fields. + Returns: -1 if key1 < key2 + 0 if key1 = key2 + 1 if key1 > key2 +*/ +int comp_short_keys (void * k1, void * k2) +{ + __u32 * p_s_key1, * p_s_key2; + int n_key_length = REISERFS_SHORT_KEY_LEN; + + p_s_key1 = (__u32 *)k1; + p_s_key2 = (__u32 *)k2; + + for( ; n_key_length--; ++p_s_key1, ++p_s_key2 ) { + if ( *p_s_key1 < *p_s_key2 ) + return -1; + if ( *p_s_key1 > *p_s_key2 ) + return 1; + } + + return 0; +} + + +/* + Compare keys using all 4 key fields. + Returns: -1 if key1 < key2 + 0 if key1 = key2 + 1 if key1 > key2 +*/ +int comp_keys (void * p1, void * p2) +{ + int retval; + struct key * k1, * k2; + + k1 = p1; + k2 = p2; + + retval = comp_short_keys (k1, k2); + if (retval) + return retval; + + if (get_offset (k1) < get_offset (k2)) + return -1; + + if (get_offset (k1) > get_offset (k2)) + return 1; + + /* this part is needed only when tail conversion is in progress */ + if (get_type (k1) < get_type (k2)) + return -1; + + if (get_type (k1) > get_type (k2)) + return 1; + + return 0; +} + + +/************************************************************************** + * Binary search toolkit function * + * Search for an item in the array by the item key * + * Returns: 1 if found, 0 if not found; * + * *p_n_pos = number of the searched element if found, else the * + * number of the first element that is larger than p_v_key. * + **************************************************************************/ +/* For those not familiar with binary search: n_lbound is the leftmost item that it + could be, n_rbound the rightmost item that it could be. We examine the item + halfway between n_lbound and n_rbound, and that tells us either that we can increase + n_lbound, or decrease n_rbound, or that we have found it, or if n_lbound <= n_rbound that + there are no possible items, and we have not found it. With each examination we + cut the number of possible items it could be by one more than half rounded down, + or we find it. */ +inline int bin_search ( + void * p_v_key, /* Key to search for. */ + void * p_v_base, /* First item in the array. */ + int p_n_num, /* Number of items in the array. */ + int p_n_width, /* Item size in the array. + searched. Lest the reader be + confused, note that this is crafted + as a general function, and when it + is applied specifically to the array + of item headers in a node, p_n_width + is actually the item header size not + the item size. */ + int * p_n_pos /* Number of the searched for element. */ + ) { + int n_rbound, n_lbound, n_j; + + for ( n_j = ((n_rbound = p_n_num - 1) + (n_lbound = 0))/2; n_lbound <= n_rbound; n_j = (n_rbound + n_lbound)/2 ) + switch( COMP_KEYS((struct key *)((char * )p_v_base + n_j * p_n_width), p_v_key) ) { + case -1: n_lbound = n_j + 1; continue; + case 1: n_rbound = n_j - 1; continue; + case 0: *p_n_pos = n_j; return ITEM_FOUND; /* Key found in the array. */ + } + + /* bin_search did not find given key, it returns position of key, + that is minimal and greater than the given one. */ + *p_n_pos = n_lbound; + return ITEM_NOT_FOUND; +} + +#ifdef CONFIG_REISERFS_CHECK +extern struct tree_balance * cur_tb; +extern struct tree_balance init_tb; +extern int init_item_pos, init_pos_in_item, init_mode; +#endif + + + +/* Minimal possible key. It is never in the tree. */ +struct key MIN_KEY = {0, 0, {{0, 0},}}; + +/* Maximal possible key. It is never in the tree. */ +struct key MAX_KEY = {0xffffffff, 0xffffffff, {{0xffffffff, 0xffffffff},}}; + + +/* Get delimiting key of the buffer by looking for it in the buffers in the + path, starting from the bottom of the path, and going upwards. We must + check the path's validity at each step. If the key is not in the path, + there is no delimiting key in the tree (buffer is first or last buffer in + tree), and in this case we return a special key, either MIN_KEY or + MAX_KEY. */ +inline struct key * get_lkey ( + struct path * p_s_chk_path, + struct super_block * p_s_sb + ) { + int n_position, n_path_offset = p_s_chk_path->path_length; + struct buffer_head * p_s_parent; + +#ifdef CONFIG_REISERFS_CHECK + if ( n_path_offset < FIRST_PATH_ELEMENT_OFFSET ) + reiserfs_panic(p_s_sb,"PAP-5010: get_lkey: illegal offset in the path"); +#endif + + /* While not higher in path than first element. */ + while ( n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! buffer_uptodate(PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) ) + reiserfs_panic(p_s_sb, "PAP-5020: get_lkey: parent is not uptodate"); +#endif + + /* Parent at the path is not in the tree now. */ + if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) ) + return &MAX_KEY; + /* Check whether position in the parent is correct. */ + if ( (n_position = PATH_OFFSET_POSITION(p_s_chk_path, n_path_offset)) > B_NR_ITEMS(p_s_parent) ) + return &MAX_KEY; + /* Check whether parent at the path really points to the child. */ + if ( B_N_CHILD_NUM(p_s_parent, n_position) != + PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset + 1)->b_blocknr ) + return &MAX_KEY; + /* Return delimiting key if position in the parent is not equal to zero. */ + if ( n_position ) + return B_N_PDELIM_KEY(p_s_parent, n_position - 1); + } + /* Return MIN_KEY if we are in the root of the buffer tree. */ + if ( PATH_OFFSET_PBUFFER(p_s_chk_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr == + SB_ROOT_BLOCK (p_s_sb) ) + return &MIN_KEY; + return &MAX_KEY; +} + + +/* Get delimiting key of the buffer at the path and its right neighbor. */ +inline struct key * get_rkey ( + struct path * p_s_chk_path, + struct super_block * p_s_sb + ) { + int n_position, + n_path_offset = p_s_chk_path->path_length; + struct buffer_head * p_s_parent; + +#ifdef CONFIG_REISERFS_CHECK + if ( n_path_offset < FIRST_PATH_ELEMENT_OFFSET ) + reiserfs_panic(p_s_sb,"PAP-5030: get_rkey: illegal offset in the path"); +#endif + + while ( n_path_offset-- > FIRST_PATH_ELEMENT_OFFSET ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! buffer_uptodate(PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) ) + reiserfs_panic(p_s_sb, "PAP-5040: get_rkey: parent is not uptodate"); +#endif + + /* Parent at the path is not in the tree now. */ + if ( ! B_IS_IN_TREE(p_s_parent = PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset)) ) + return &MIN_KEY; + /* Check whether position in the parrent is correct. */ + if ( (n_position = PATH_OFFSET_POSITION(p_s_chk_path, n_path_offset)) > B_NR_ITEMS(p_s_parent) ) + return &MIN_KEY; + /* Check whether parent at the path really points to the child. */ + if ( B_N_CHILD_NUM(p_s_parent, n_position) != + PATH_OFFSET_PBUFFER(p_s_chk_path, n_path_offset + 1)->b_blocknr ) + return &MIN_KEY; + /* Return delimiting key if position in the parent is not the last one. */ + if ( n_position != B_NR_ITEMS(p_s_parent) ) + return B_N_PDELIM_KEY(p_s_parent, n_position); + } + /* Return MAX_KEY if we are in the root of the buffer tree. */ + if ( PATH_OFFSET_PBUFFER(p_s_chk_path, FIRST_PATH_ELEMENT_OFFSET)->b_blocknr == + SB_ROOT_BLOCK (p_s_sb) ) + return &MAX_KEY; + return &MIN_KEY; +} + + +/* Check whether a key is contained in the tree rooted from a buffer at a + path. This works by looking at the left and right delimiting keys for the + buffer in the last path_element in the path. These delimiting keys are + stored at least one level above that buffer in the tree. If the buffer is + the first or last node in the tree order then one of the delimiting keys + may be absent, and in this case get_lkey and get_rkey return a special key + which is MIN_KEY or MAX_KEY. */ +static inline int key_in_buffer ( + struct path * p_s_chk_path, /* Path which should be checked. */ + struct key * p_s_key, /* Key which should be checked. */ + struct super_block * p_s_sb /* Super block pointer. */ + ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( ! p_s_key || p_s_chk_path->path_length < FIRST_PATH_ELEMENT_OFFSET || + p_s_chk_path->path_length > MAX_HEIGHT ) + reiserfs_panic(p_s_sb, "PAP-5050: key_in_buffer: pointer to the key(%p) is NULL or illegal path length(%d)", + p_s_key, p_s_chk_path->path_length); + + if ( PATH_PLAST_BUFFER(p_s_chk_path)->b_dev == NODEV ) + reiserfs_panic(p_s_sb, "PAP-5060: key_in_buffer: device must not be NODEV"); +#endif + + if ( COMP_KEYS(get_lkey(p_s_chk_path, p_s_sb), p_s_key) == 1 ) + return 0; + if ( COMP_KEYS(p_s_key, get_rkey(p_s_chk_path, p_s_sb)) != -1 ) + return 0; + return 1; +} + + +/* Release all buffers in the path. */ +void pathrelse (struct path * p_s_search_path) +{ + int n_path_offset = p_s_search_path->path_length; + +#ifdef CONFIG_REISERFS_CHECK + if ( n_path_offset < ILLEGAL_PATH_ELEMENT_OFFSET ) + reiserfs_panic(NULL, "PAP-5090: pathrelse: illegal path offset"); +#endif + + while ( n_path_offset > ILLEGAL_PATH_ELEMENT_OFFSET ) + brelse(PATH_OFFSET_PBUFFER(p_s_search_path, n_path_offset--)); + + p_s_search_path->path_length = ILLEGAL_PATH_ELEMENT_OFFSET; +} + + +/************************************************************************** + * Algorithm SearchByKey * + * look for item in the Disk S+Tree by its key * + * Input: p_s_sb - super block * + * p_s_key - pointer to the key to search * + * Output: true value - 1 - found, 0 - not found * + * p_s_search_path - path from the root to the needed leaf * + **************************************************************************/ + +/* This function fills up the path from the root to the leaf as it + descends the tree looking for the key. It uses reiserfs_bread to + try to find buffers in the cache given their block number. If it + does not find them in the cache it reads them from disk. For each + node search_by_key finds using reiserfs_bread it then uses + bin_search to look through that node. bin_search will find the + position of the block_number of the next node if it is looking + through an internal node. If it is looking through a leaf node + bin_search will find the position of the item which has key either + equal to given key, or which is the maximal key less than the given + key. search_by_key returns a path that must be checked for the + correctness of the top of the path but need not be checked for the + correctness of the bottom of the path */ +int search_by_key( + struct super_block * p_s_sb, /* Super block. */ + struct key * p_s_key, /* Key to search. */ + struct path * p_s_search_path,/* This structure was allocated and initialized by + the calling function. It is filled up by this + function. */ + int * p_n_repeat, /* Whether schedule occured. */ + int n_stop_level /* How far down the tree to search.*/ + ) { + dev_t n_dev = p_s_sb->s_dev; + int n_repeat, + n_block_number = SB_ROOT_BLOCK (p_s_sb), + expected_level = SB_TREE_HEIGHT (p_s_sb), + n_block_size = p_s_sb->s_blocksize; + struct buffer_head * p_s_bh; + struct path_element * p_s_last_element; + int n_retval; + int right_neighbor_of_leaf_node; + +#ifdef CONFIG_REISERFS_CHECK + int n_repeat_counter = 0; +#endif + + /* As we add each node to a path we increase its count. This + means that we must be careful to release all nodes in a path + before we either discard the path struct or re-use the path + struct, as we do here. */ + pathrelse (p_s_search_path); + + *p_n_repeat = CARRY_ON; + + /* With each iteration of this loop we search through the items in + the current node, and calculate the next current node(next path + element) for the next iteration of this loop.. */ + while ( 1 ) { + +#ifdef CONFIG_REISERFS_CHECK + if ( !(++n_repeat_counter % 50000) ) + printk ("PAP-5100: search_by_key(pid %u): there were %d searches from the tree_root lokking for key %p\n", + current->pid, n_repeat_counter, p_s_key); +#endif + + /* prep path to have another element added to it. */ + p_s_last_element = PATH_OFFSET_PELEMENT(p_s_search_path, ++p_s_search_path->path_length); + expected_level --; + n_repeat = CARRY_ON; + + /* Read the next tree node, and set the last element in the + path to have a pointer to it. */ + if ( ! (p_s_bh = p_s_last_element->pe_buffer = + reiserfs_bread(n_dev, n_block_number, n_block_size, &n_repeat)) ) { + p_s_search_path->path_length --; + pathrelse(p_s_search_path); + *p_n_repeat |= n_repeat; + return IO_ERROR; + } + + *p_n_repeat |= n_repeat; + + /* It is possible that schedule occured. We must check whether + the key to search is still in the tree rooted from the + current buffer. If not then repeat search from the root. */ + if ( n_repeat != CARRY_ON && + (!B_IS_IN_TREE (p_s_bh) || (! key_in_buffer(p_s_search_path, p_s_key, p_s_sb))) ) { + pathrelse (p_s_search_path); + + /* Get the root block number so that we can repeat the + search starting from the root. */ + n_block_number = SB_ROOT_BLOCK (p_s_sb); + expected_level = SB_TREE_HEIGHT (p_s_sb); + right_neighbor_of_leaf_node = 0; + + /* repeat search from the root */ + continue; + } + +#ifdef CONFIG_REISERFS_CHECK + + if ( ! key_in_buffer(p_s_search_path, p_s_key, p_s_sb) ) + reiserfs_panic(p_s_sb, "PAP-5130: search_by_key: key is not in the buffer"); + if ( cur_tb ) { +/* print_tb (init_mode, init_item_pos, init_pos_in_item, &init_tb, "5140");*/ + reiserfs_panic(p_s_sb, "PAP-5140: search_by_key: schedule occurred in do_balance!"); + } + +#endif + + // make sure, that the node contents look like a nod of + // certain level + if (!is_tree_node (p_s_bh, expected_level)) { + print_block (stderr, 0, p_s_bh, 3, -1, -1); + reiserfs_panic ("vs-5150: search_by_key: expeced level %d", expected_level); + pathrelse (p_s_search_path); + return IO_ERROR; + } + + /* ok, we have acquired next formatted node in the tree */ + n_retval = bin_search (p_s_key, B_N_PITEM_HEAD(p_s_bh, 0), B_NR_ITEMS(p_s_bh), + is_leaf_node (p_s_bh) ? IH_SIZE : KEY_SIZE, &(p_s_last_element->pe_position)); + if (node_level (p_s_bh) == n_stop_level) + return n_retval; + + /* we are not in the stop level */ + if (n_retval == ITEM_FOUND) + /* item has been found, so we choose the pointer which is to the right of the found one */ + p_s_last_element->pe_position++; + /* if item was not found we choose the position which is to the left of the found item. This + requires no code, bin_search did it already.*/ + + + /* So we have chosen a position in the current node which is an + internal node. Now we calculate child block number by position in the node. */ + n_block_number = B_N_CHILD_NUM(p_s_bh, p_s_last_element->pe_position); + } +} + + +int bin_search_in_dir_item (struct item_head * ih, struct reiserfs_de_head * deh, + struct key * key, int * pos_in_item) +{ + int rbound, lbound, j; + + lbound = 0; + rbound = ih_entry_count (ih) - 1; + + for (j = (rbound + lbound) / 2; lbound <= rbound; j = (rbound + lbound) / 2) { + if (get_offset (key) < deh_offset (deh + j)) { + rbound = j - 1; + continue; + } + if (get_offset (key) > deh_offset (deh + j)) { + lbound = j + 1; + continue; + } + /* key found */ + *pos_in_item = j; + return POSITION_FOUND; + } + + *pos_in_item = lbound; + return POSITION_NOT_FOUND; +} diff --git a/reiserfsprogs.spec b/reiserfsprogs.spec new file mode 100644 index 0000000..ad24125 --- /dev/null +++ b/reiserfsprogs.spec @@ -0,0 +1,62 @@ +Vendor: Hans Reiser +Distribution: Hans Reiser +Name: reiserfsprogs +Release: 1 +Copyright: 2001 Hans Reiser +Group: Unsorted + +Packager: anthon@mnt.org + +Version: 3.x.0j +Summary: utilities belonging to the Reiser filesystem +Source: reiserfsprogs-%{version}.tar.gz +BuildRoot: /var/tmp/rpm-reiserfsprogs +%description + +The reiserfsprogs package contains programs for creating (mkreiserfs), +checking and correcting any inconsistencies (reiserfsck) and resizing +(resize_reiserfs) of a reiserfs filesystem. + +Authors: +-------- +Hans Reiser <reiser@namesys.com> +Vitaly Fertman <vetalf@inbox.ru> +Alexander Zarochentcev <zam@namesys.com> +Vladimir Saveliev <vs@namesys.botik.ru> + +%prep +%setup -q +# %patch +%build + MANDIR=$(dirname $(dirname $(man -w fsck | cut -d ' ' -f 1))) + ./configure --prefix="" --mandir=$MANDIR + make all +%install + mkdir -p $RPM_BUILD_ROOT/sbin + make DESTDIR=$RPM_BUILD_ROOT install +# do we need this? + cd $RPM_BUILD_ROOT/sbin + ln -sf reiserfsck fsck.reiserfs + +# __os_install_post is normally executed after %install disable it +%define ___build_post %{nil} +# explicitly call it now, so manpages get compressed, exec's stripped etc. +%{?__os_install_post} +%define __os_install_post %{nil} +# now we have all the files execpt for docs, but their owner is unimportant +cd $RPM_BUILD_ROOT + +rm -f rpm-filelist +# we do not have special directories to make +#find . -type d \ +# | sed '1,2d;s,^\.,\%attr(-\,root\,root) \%dir ,' >> rpm-filelist +find . -type f \ + | sed 's,^\.,\%attr(-\,root\,root) ,' | fgrep -v rpm-filelist >> rpm-filelist +find . -type l \ + | sed 's,^\.,\%attr(-\,root\,root) ,' >> rpm-filelist + +%clean +# in case some overrides buildroot with / don't remove the whole tree + rm -rf /var/tmp/rpm-reiserfsprogs +%files -f /var/tmp/rpm-reiserfsprogs/rpm-filelist +%doc README diff --git a/resize_reiserfs/Makefile.am b/resize_reiserfs/Makefile.am new file mode 100644 index 0000000..0af4bea --- /dev/null +++ b/resize_reiserfs/Makefile.am @@ -0,0 +1,9 @@ +sbin_PROGRAMS = resize_reiserfs + +resize_reiserfs_SOURCES = fe.c resize_reiserfs.c do_shrink.c resize.h +man_MANS = resize_reiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include diff --git a/resize_reiserfs/Makefile.in b/resize_reiserfs/Makefile.in new file mode 100644 index 0000000..d03f545 --- /dev/null +++ b/resize_reiserfs/Makefile.in @@ -0,0 +1,331 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +CC = @CC@ +MAKEINFO = @MAKEINFO@ +PACKAGE = @PACKAGE@ +RANLIB = @RANLIB@ +VERSION = @VERSION@ + +sbin_PROGRAMS = resize_reiserfs + +resize_reiserfs_SOURCES = fe.c resize_reiserfs.c do_shrink.c resize.h +man_MANS = resize_reiserfs.8 +EXTRA_DIST = $(man_MANS) + +LDADD = ../lib/libmisc.a ../reiserfscore/libcore.a + +INCLUDES = -I../include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_CLEAN_FILES = +PROGRAMS = $(sbin_PROGRAMS) + + +DEFS = @DEFS@ -I. -I$(srcdir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +resize_reiserfs_OBJECTS = fe.o resize_reiserfs.o do_shrink.o +resize_reiserfs_LDADD = $(LDADD) +resize_reiserfs_DEPENDENCIES = ../lib/libmisc.a \ +../reiserfscore/libcore.a +resize_reiserfs_LDFLAGS = +CFLAGS = @CFLAGS@ +COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ +man8dir = $(mandir)/man8 +MANS = $(man_MANS) + +NROFF = nroff +DIST_COMMON = Makefile.am Makefile.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +SOURCES = $(resize_reiserfs_SOURCES) +OBJECTS = $(resize_reiserfs_OBJECTS) + +all: all-redirect +.SUFFIXES: +.SUFFIXES: .S .c .o .s +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnu --include-deps resize_reiserfs/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + + +mostlyclean-sbinPROGRAMS: + +clean-sbinPROGRAMS: + -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS) + +distclean-sbinPROGRAMS: + +maintainer-clean-sbinPROGRAMS: + +install-sbinPROGRAMS: $(sbin_PROGRAMS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(sbindir) + @list='$(sbin_PROGRAMS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \ + $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + else :; fi; \ + done + +uninstall-sbinPROGRAMS: + @$(NORMAL_UNINSTALL) + list='$(sbin_PROGRAMS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \ + done + +.c.o: + $(COMPILE) -c $< + +.s.o: + $(COMPILE) -c $< + +.S.o: + $(COMPILE) -c $< + +mostlyclean-compile: + -rm -f *.o core *.core + +clean-compile: + +distclean-compile: + -rm -f *.tab.c + +maintainer-clean-compile: + +resize_reiserfs: $(resize_reiserfs_OBJECTS) $(resize_reiserfs_DEPENDENCIES) + @rm -f resize_reiserfs + $(LINK) $(resize_reiserfs_LDFLAGS) $(resize_reiserfs_OBJECTS) $(resize_reiserfs_LDADD) $(LIBS) + +install-man8: + $(mkinstalldirs) $(DESTDIR)$(man8dir) + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ + else file=$$i; fi; \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \ + $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \ + done + +uninstall-man8: + @list='$(man8_MANS)'; \ + l2='$(man_MANS)'; for i in $$l2; do \ + case "$$i" in \ + *.8*) list="$$list $$i" ;; \ + esac; \ + done; \ + for i in $$list; do \ + ext=`echo $$i | sed -e 's/^.*\\.//'`; \ + inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ + inst=`echo $$inst | sed '$(transform)'`.$$ext; \ + echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \ + rm -f $(DESTDIR)$(man8dir)/$$inst; \ + done +install-man: $(MANS) + @$(NORMAL_INSTALL) + $(MAKE) $(AM_MAKEFLAGS) install-man8 +uninstall-man: + @$(NORMAL_UNINSTALL) + $(MAKE) $(AM_MAKEFLAGS) uninstall-man8 + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + here=`pwd` && cd $(srcdir) \ + && mkid -f$$here/ID $$unique $(LISP) + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS)'; \ + unique=`for i in $$list; do echo $$i; done | \ + awk ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \ + || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS) + +mostlyclean-tags: + +clean-tags: + +distclean-tags: + -rm -f TAGS ID + +maintainer-clean-tags: + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = resize_reiserfs + +distdir: $(DISTFILES) + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +do_shrink.o: do_shrink.c resize.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h +fe.o: fe.c resize.h ../include/io.h ../include/misc.h \ + ../include/reiserfs_lib.h ../include/reiserfs_fs.h ../version.h +resize_reiserfs.o: resize_reiserfs.c resize.h ../include/io.h \ + ../include/misc.h ../include/reiserfs_lib.h \ + ../include/reiserfs_fs.h ../version.h + +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-sbinPROGRAMS +install-exec: install-exec-am + +install-data-am: install-man +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-sbinPROGRAMS uninstall-man +uninstall: uninstall-am +all-am: Makefile $(PROGRAMS) $(MANS) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(sbindir) $(DESTDIR)$(mandir)/man8 + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-sbinPROGRAMS mostlyclean-compile \ + mostlyclean-tags mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-sbinPROGRAMS clean-compile clean-tags clean-generic \ + mostlyclean-am + +clean: clean-am + +distclean-am: distclean-sbinPROGRAMS distclean-compile distclean-tags \ + distclean-generic clean-am + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-sbinPROGRAMS \ + maintainer-clean-compile maintainer-clean-tags \ + maintainer-clean-generic distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS \ +clean-sbinPROGRAMS maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \ +install-sbinPROGRAMS mostlyclean-compile distclean-compile \ +clean-compile maintainer-clean-compile install-man8 uninstall-man8 \ +install-man uninstall-man tags mostlyclean-tags distclean-tags \ +clean-tags maintainer-clean-tags distdir info-am info dvi-am dvi check \ +check-am installcheck-am installcheck install-exec-am install-exec \ +install-data-am install-data install-am install uninstall-am uninstall \ +all-redirect all-am all installdirs mostlyclean-generic \ +distclean-generic clean-generic maintainer-clean-generic clean \ +mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/resize_reiserfs/do_shrink.c b/resize_reiserfs/do_shrink.c new file mode 100644 index 0000000..62b3026 --- /dev/null +++ b/resize_reiserfs/do_shrink.c @@ -0,0 +1,269 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ +#include <time.h> +#include "resize.h" + + +static unsigned long int_node_cnt = 0, int_moved_cnt = 0; +static unsigned long leaf_node_cnt = 0, leaf_moved_cnt = 0; +static unsigned long unfm_node_cnt = 0, unfm_moved_cnt = 0; +static unsigned long total_node_cnt = 0; +static unsigned long total_moved_cnt = 0; + +static unsigned long unused_block; +static unsigned long blocks_used; +static int block_count_mismatch = 0; + +static reiserfs_bitmap_t bmp; +static reiserfs_filsys_t fs; +static struct reiserfs_super_block * rs; + +/* abnornal exit from block reallocation process */ +static void quit_resizer() +{ + /* save changes to bitmap blocks */ + reiserfs_flush_bitmap (bmp, fs); + reiserfs_close (fs); + /* leave fs in ERROR state */ + die ("resize_reiserfs: fs shrinking was not completed successfully, run reiserfsck.\n"); +} + +/* block moving */ +static unsigned long move_generic_block(unsigned long block, unsigned long bnd, int h) +{ + struct buffer_head * bh, * bh2; + + /* primitive fsck */ + if (block > rs_block_count(rs)) { + fprintf(stderr, "resize_reiserfs: invalid block number (%lu) found.\n", block); + quit_resizer(); + } + /* progress bar, 3D style :) */ + if (opt_verbose) + print_how_far(&total_node_cnt, blocks_used, 1, 0); + else + total_node_cnt ++; + + /* infinite loop check */ + if( total_node_cnt > blocks_used && !block_count_mismatch) { + fputs("resize_reiserfs: warning: block count exeeded\n",stderr); + block_count_mismatch = 1; + } + + if (block < bnd) /* block will not be moved */ + return 0; + + /* move wrong block */ + bh = bread(fs->s_dev, block, fs->s_blocksize); + + reiserfs_bitmap_find_zero_bit(bmp, &unused_block); + if (unused_block == 0 || unused_block >= bnd) { + fputs ("resize_reiserfs: can\'t find free block\n", stderr); + quit_resizer(); + } + + /* blocknr changing */ + bh2 = getblk(fs->s_dev, unused_block, fs->s_blocksize); + memcpy(bh2->b_data, bh->b_data, bh2->b_size); + reiserfs_bitmap_clear_bit(bmp, block); + reiserfs_bitmap_set_bit(bmp, unused_block); + + brelse(bh); + mark_buffer_uptodate(bh2,1); + mark_buffer_dirty(bh2); + bwrite(bh2); + brelse(bh2); + + total_moved_cnt++; + return unused_block; +} + +static unsigned long move_unformatted_block(unsigned long block, unsigned long bnd, int h) +{ + unsigned long b; + unfm_node_cnt++; + b = move_generic_block(block, bnd, h); + if (b) + unfm_moved_cnt++; + return b; +} + + +/* recursive function processing all tree nodes */ +static unsigned long move_formatted_block(unsigned long block, unsigned long bnd, int h) +{ + struct buffer_head * bh; + struct item_head *ih; + unsigned long new_blocknr = 0; + int node_is_internal = 0; + int i, j; + + bh = bread(fs->s_dev, block, fs->s_blocksize); + + if (is_leaf_node (bh)) { + + leaf_node_cnt++; + + for (i=0; i < B_NR_ITEMS(bh); i++) { + ih = B_N_PITEM_HEAD(bh, i); + if (is_indirect_ih(ih)) { + __u32 * indirect; + + indirect = (__u32 *)B_I_PITEM (bh, ih); + for (j = 0; j < I_UNFM_NUM(ih); j++) { + unsigned long unfm_block; + + if (indirect [j] == 0) /* hole */ + continue; + unfm_block = move_unformatted_block(le32_to_cpu (indirect [j]), bnd, h + 1); + if (unfm_block) { + indirect [j] = cpu_to_le32 (unfm_block); + mark_buffer_dirty(bh); + } + } + } + } + } else if (is_internal_node (bh)) { /* internal node */ + + int_node_cnt++; + node_is_internal = 1; + + for (i=0; i <= B_NR_ITEMS(bh); i++) { + unsigned long moved_block; + moved_block = move_formatted_block(B_N_CHILD_NUM(bh, i), bnd, h+1); + if (moved_block) { + set_dc_block_number (bh, i, moved_block); + mark_buffer_dirty(bh); + } + } + } else { + die ("resize_reiserfs: block (%lu) has invalid format\n", block); + } + + if (buffer_dirty(bh)) { + mark_buffer_uptodate(bh,1); + bwrite(bh); + } + + brelse(bh); + + new_blocknr = move_generic_block(block, bnd, h); + if (new_blocknr) { + if (node_is_internal) + int_moved_cnt++; + else + leaf_moved_cnt++; + } + + return new_blocknr; +} + +int shrink_fs(reiserfs_filsys_t reiserfs, unsigned long blocks) +{ + unsigned long n_root_block; + unsigned int bmap_nr_new; + unsigned long int i; + + fs = reiserfs; + rs = fs->s_rs; + + /* warn about alpha version */ + { + int c; + + printf( + "You are running BETA version of reiserfs shrinker.\n" + "This version is only for testing or VERY CAREFUL use.\n" + "Backup of you data is recommended.\n\n" + "Do you want to continue? [y/N]:" + ); + c = getchar(); + if (c != 'y' && c != 'Y') + exit(1); + } + + bmap_nr_new = (blocks - 1) / (8 * fs->s_blocksize) + 1; + + /* is shrinking possible ? */ + if (rs_block_count(rs) - blocks > rs_free_blocks(rs) + rs_bmap_nr(rs) - bmap_nr_new) { + fprintf(stderr, "resize_reiserfs: can\'t shrink fs; too many blocks already allocated\n"); + return -1; + } + + reiserfs_reopen(fs, O_RDWR); + set_state (fs->s_rs, REISERFS_ERROR_FS); + mark_buffer_uptodate(SB_BUFFER_WITH_SB(fs), 1); + mark_buffer_dirty(SB_BUFFER_WITH_SB(fs)); + bwrite(SB_BUFFER_WITH_SB(fs)); + + /* calculate number of data blocks */ + blocks_used = + SB_BLOCK_COUNT(fs) + - SB_FREE_BLOCKS(fs) + - SB_BMAP_NR(fs) + - SB_JOURNAL_SIZE(fs) + - REISERFS_DISK_OFFSET_IN_BYTES / fs->s_blocksize + - 2; /* superblock itself and 1 descriptor after the journal */ + + bmp = reiserfs_create_bitmap(rs_block_count(rs)); + reiserfs_fetch_disk_bitmap(bmp, fs); + + unused_block = 1; + + if (opt_verbose) { + printf("Processing the tree: "); + fflush(stdout); + } + + n_root_block = move_formatted_block(rs_root_block(rs), blocks, 0); + if (n_root_block) { + set_root_block (rs, n_root_block); + } + + if (opt_verbose) + printf ("\n\nnodes processed (moved):\n" + "int %lu (%lu),\n" + "leaves %lu (%lu),\n" + "unfm %lu (%lu),\n" + "total %lu (%lu).\n\n", + int_node_cnt, int_moved_cnt, + leaf_node_cnt, leaf_moved_cnt, + unfm_node_cnt, unfm_moved_cnt, + (unsigned long)total_node_cnt, total_moved_cnt); + + if (block_count_mismatch) { + fprintf(stderr, "resize_reiserfs: data block count %lu" + " doesn\'t match data block count %lu from super block\n", + (unsigned long)total_node_cnt, blocks_used); + } +#if 0 + printf("check for used blocks in truncated region\n"); + { + unsigned long l; + for (l = blocks; l < rs_block_count(rs); l++) + if (is_block_used(bmp, l)) + printf("<%lu>", l); + printf("\n"); + } +#endif /* 0 */ + + reiserfs_free_bitmap_blocks(fs); + + set_free_blocks (rs, rs_free_blocks(rs) - (rs_block_count(rs) - blocks) + (rs_bmap_nr(rs) - bmap_nr_new)); + set_block_count (rs, blocks); + set_bmap_nr (rs, bmap_nr_new); + + reiserfs_read_bitmap_blocks(fs); + + for (i = blocks; i < bmap_nr_new * fs->s_blocksize; i++) + reiserfs_bitmap_set_bit(bmp, i); +#if 0 + PUT_SB_FREE_BLOCKS(s, SB_FREE_BLOCKS(s) - (SB_BLOCK_COUNT(s) - blocks) + (SB_BMAP_NR(s) - bmap_nr_new)); + PUT_SB_BLOCK_COUNT(s, blocks); + PUT_SB_BMAP_NR(s, bmap_nr_new); +#endif + reiserfs_flush_bitmap(bmp, fs); + + return 0; +} diff --git a/resize_reiserfs/fe.c b/resize_reiserfs/fe.c new file mode 100644 index 0000000..c14c2c5 --- /dev/null +++ b/resize_reiserfs/fe.c @@ -0,0 +1,37 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +#include "resize.h" + + +/* the front-end for kernel on-line resizer */ +int resize_fs_online(char * devname, unsigned long blocks) +{ + static char buf[40]; + FILE * f; + struct mntent * mnt; + + if ((f = setmntent (MOUNTED, "r")) == NULL) + goto fail; + + while ((mnt = getmntent (f)) != NULL) + if(strcmp(devname, mnt->mnt_fsname) == 0) { + + if (strcmp(mnt->mnt_type,"reiserfs")) + die ("resize_reiserfs: can\'t resize fs other than reiserfs\n"); + + sprintf(buf,"resize=%lu", blocks); + + if (mount(mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, + (unsigned long)(MS_MGC_VAL | MS_REMOUNT), buf)) + die ("resize_reiserfs: remount failed: %s\n", strerror(errno)); + + endmntent(f); + return 0; + } +fail: + die ("resize_reiserfs: can't find mount entry\n"); + return 1; +} + diff --git a/resize_reiserfs/resize.h b/resize_reiserfs/resize.h new file mode 100644 index 0000000..937b500 --- /dev/null +++ b/resize_reiserfs/resize.h @@ -0,0 +1,48 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ +#define _GNU_SOURCE + +#include <sys/types.h> +#include <sys/stat.h> +#include <asm/types.h> +#include <errno.h> +#include <stdio.h> +#include <mntent.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> + +#if __GLIBC__ >= 2 +#include <sys/mount.h> +#else +#include <linux/fs.h> +#endif + + +#include "io.h" +#include "misc.h" +#include "reiserfs_lib.h" +#include "../version.h" + + +#define print_usage_and_exit()\ + die ("Usage: %s [-s[+|-]#[G|M|K]] [-fqv] device", argv[0]) + + +/* reiserfs_resize.c */ +extern struct buffer_head * g_sb_bh; + +extern int opt_force; +extern int opt_verbose; +extern int opt_nowrite; +extern int opt_safe; + +int expand_fs(struct super_block * s, unsigned long block_count_new); + +/* fe.c */ +int resize_fs_online(char * devname, unsigned long blocks); + +/* do_shrink.c */ +int shrink_fs(struct super_block * s, unsigned long blocks); diff --git a/resize_reiserfs/resize_reiserfs.8 b/resize_reiserfs/resize_reiserfs.8 new file mode 100644 index 0000000..66a3f25 --- /dev/null +++ b/resize_reiserfs/resize_reiserfs.8 @@ -0,0 +1,116 @@ +.\" -*- nroff -*- +.\" Copyright 1996-2001 Hans Reiser. +.\" +.TH RESIZE_REISERFS 8 "March 2001" "Reiserfsprogs-3.x.0j" +.SH NAME +resize_reiserfs \- Reiserfs filesystem resizer +.SH SYNOPSIS +.BR resize_reiserfs +[ +.B \-s +.IR \fR[\fB+\fR|\fB\- ]\fIsize [\fBK\fR|\fBM\fR|\fBG\fR] +] [ +.B \-fqv +] +.I device +.SH DESCRIPTION +The +.B resize_reiserfs +tool resizes an unmounted reiserfs file system. It enlarges or shrinks an +reiserfs file system located on a +.I device +so that it will have +.I size +bytes or size=old_size +(\-) +.I size +bytes if the + or \- prefix is used. +If the +.B \-s +option is not specified, the filesystem will be resized to fill the +given device. +The +.I size +parameter may have one of the optional modifiers +.BR K ", " M ", " G , +which means the +.I size +parameter is given in kilo\-, mega\-, gigabytes respectively. +.PP +The +.B resize_reiserfs +program does not manipulate the size of the device. If you wish to +enlarge a filesystem, you must make sure you expand the underlying +device first. This can be done using +.BR cfdisk (8) +for partitions, by deleting the partition and recreating it with a +larger size (assuming there is free space +.I after +the partition in question). Make sure you re\-create it with the +same starting disk cylinder as before! Otherwise, the resize operation +will certainly not work, and you may lose your entire filesystem. +.PP +The +.B resize_reiserfs +program allows to grow a reiserfs on-line if there is a free +space on block +.I device. + +.PP +If you wish to shrink an reiserfs partition, first use +.B resize_reiserfs +to shrink the file system. You may then use +.BR cfdisk (8) +to shrink the device. When shrinking the size of the device, make sure +you do not make it smaller than the reduced size of the reiserfs filesystem. + +.SH OPTIONS +.TP +.BR \-s\ [+|\-]\fIsize +Set the new size in bytes. +.TP +.BR \-f +Force, do not perform checks. +.TP +.BR \-q +Do not print anything but error messages. +.TP +.BR \-v +Turn on extra progress status messages (default). + +.SH RETURN VALUES +0 Resizing successful. +.TP +\-1 In another case. + +.SH EXAMPLES +The following example shows how to test +.B resize_reiserfs\fR. +Suppose 2Gb reiserfs filesystem is created on the device /dev/hda8 +and is mounted on /mnt. +For shrinking the device we need to unmount it first, then run +.B resize_reiserfs +with a +.I size \fR parameter (in this case -1Gb): +.PP +\ df +.br +\ umount /mnt +.br +\ resize_reiserfs -s -1G /dev/hda8 +.br +\ mount /dev/hda8 /mnt +.br +\ df /mnt + +.SH WARNING +Note that this is a BETA program and may corrupt filesystems. +.SH AUTHOR +This version of +.B resize_reiserfs +has been written by Alexander Zarochentcev <zam@namesys.com>. +.SH BUGS +Please, report about the bugs to Alexander Zarochentcev <zam@namesys.com> or +to Hans Reiser <reiser@namesys.com>. +.SH SEE ALSO +.BR cfsck (8), +.BR debugreiserfs (8) diff --git a/resize_reiserfs/resize_reiserfs.c b/resize_reiserfs/resize_reiserfs.c new file mode 100644 index 0000000..3db3d21 --- /dev/null +++ b/resize_reiserfs/resize_reiserfs.c @@ -0,0 +1,257 @@ +/* + * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README + */ + +/* + * Written by Alexander Zarochentcev. + * + * FS resize utility + * + */ + +#include "resize.h" + +int opt_force = 0; +int opt_verbose = 1; /* now "verbose" option is default */ +int opt_nowrite = 0; +int opt_safe = 0; + +#if 0 +/* Given a file descriptor and an offset, check whether the offset is + a valid offset for the file - return 0 if it isn't valid or 1 if it + is */ +int valid_offset( int fd, loff_t offset ) +{ + char ch; + + if (lseek64 (fd, offset, 0) < 0) + return 0; + + if (read (fd, &ch, 1) < 1) + return 0; + + return 1; +} +#endif + +/* calculate the new fs size (in blocks) from old fs size and the string + representation of new size */ +static unsigned long calc_new_fs_size(unsigned long count, int bs, + char *bytes_str) +{ + long long int bytes; + unsigned long blocks; + int c; + + bytes = atoll(bytes_str); + c = bytes_str[strlen(bytes_str) - 1]; + + switch (c) { + case 'G': + case 'g': + bytes *= 1024; + case 'M': + case 'm': + bytes *= 1024; + case 'K': + case 'k': + bytes *= 1024; + } + + blocks = bytes / bs; + + if (bytes_str[0] == '+' || bytes_str[0] == '-') + return (count + blocks); + + return blocks; +} + +/* print some fs parameters */ +static void sb_report(struct reiserfs_super_block * sb1, + struct reiserfs_super_block * sb2) +{ + printf( + "ReiserFS report:\n" + "blocksize %d\n" + "block count %d (%d)\n" + "free blocks %d (%d)\n" + "bitmap block count %d (%d)\n", + rs_blocksize(sb1), + rs_block_count(sb1), rs_block_count(sb2), + rs_free_blocks(sb1), rs_free_blocks(sb2), + rs_bmap_nr(sb1), rs_bmap_nr(sb2)); +}; + +/* conditional bwrite */ +static int bwrite_cond (struct buffer_head * bh) +{ + if(!opt_nowrite) { + mark_buffer_uptodate(bh,1); + mark_buffer_dirty(bh); + bwrite(bh); + } + return 0; +} + + +/* the first one of the mainest functions */ +int expand_fs (reiserfs_filsys_t fs, unsigned long block_count_new) { + int block_r, block_r_new; + unsigned int bmap_nr_new, bmap_nr_old; + int i; + + reiserfs_bitmap_t bmp; + struct reiserfs_super_block * rs = fs->s_rs; + + reiserfs_reopen(fs, O_RDWR); + set_state (fs->s_rs, REISERFS_ERROR_FS); + bwrite_cond(SB_BUFFER_WITH_SB(fs)); + + bmp = reiserfs_create_bitmap(rs_block_count(rs)); + if (!bmp) + die ("cannot create bitmap\n"); + reiserfs_fetch_disk_bitmap(bmp, fs); + reiserfs_free_bitmap_blocks(fs); + if (reiserfs_expand_bitmap(bmp, block_count_new)) + die ("cannot expand bitmap\n"); + + /* clean bits in old bitmap tail */ + for (i = rs_block_count(rs); + i < rs_bmap_nr(rs) * rs_blocksize(rs) * 8 && i < block_count_new; + i++) { + reiserfs_bitmap_clear_bit(bmp, i); + } + + /* count used bits in last bitmap block */ + block_r = rs_block_count(rs) - ((rs_bmap_nr(rs) - 1) * rs_blocksize(rs) * 8); + + /* count bitmap blocks in new fs */ + bmap_nr_new = (block_count_new - 1) / (rs_blocksize(rs) * 8) + 1; + block_r_new = block_count_new - (bmap_nr_new - 1) * rs_blocksize(rs) * 8; + + bmap_nr_old = rs_bmap_nr(rs); + + /* update super block buffer*/ + set_free_blocks (rs, rs_free_blocks(rs) + block_count_new + - rs_block_count(rs) - (bmap_nr_new - rs_bmap_nr(rs))); + set_block_count (rs, block_count_new); + set_bmap_nr (rs, bmap_nr_new); + + reiserfs_read_bitmap_blocks(fs); + for (i = bmap_nr_old; i < bmap_nr_new; i++) /* fix new bitmap blocks */ + reiserfs_bitmap_set_bit(bmp, SB_AP_BITMAP(fs)[i]->b_blocknr); + reiserfs_flush_bitmap(bmp, fs); + + return 0; +} + +int main(int argc, char *argv[]) { + char * bytes_count_str = NULL; + char * devname; + reiserfs_filsys_t fs; + struct reiserfs_super_block * rs; + + int c; + int error; + + struct reiserfs_super_block *sb_old; + + unsigned long block_count_new; + + print_banner ("resize_reiserfs"); + + while ((c = getopt(argc, argv, "fvcqs:")) != EOF) { + switch (c) { + case 's' : + if (!optarg) + die("%s: Missing argument to -s option", argv[0]); + bytes_count_str = optarg; + break; + case 'f': + opt_force = 1; + break; + case 'v': + opt_verbose++; + break; + case 'n': + /* no nowrite option at this moment */ + /* opt_nowrite = 1; */ + break; + case 'c': + opt_safe = 1; + break; + case 'q': + opt_verbose = 0; + break; + default: + print_usage_and_exit (); + } + } + + if (optind == argc ) + print_usage_and_exit(); + devname = argv[optind]; + + fs = reiserfs_open(devname, O_RDONLY, &error, 0); + if (!fs) + die ("%s: can not open '%s': %s", argv[0], devname, strerror(error)); + + if (no_reiserfs_found (fs)) { + die ("resize_reiserfs: no reiserfs found on the device"); + } + if (!spread_bitmaps (fs)) { + die ("resize_reiserfs: cannot resize reiserfs in old (not spread bitmap) format.\n"); + } + + rs = fs->s_rs; + + if(bytes_count_str) { /* new fs size is specified by user */ + block_count_new = calc_new_fs_size(rs_block_count(rs), fs->s_blocksize, bytes_count_str); + } else { /* use whole device */ + block_count_new = count_blocks(devname, fs->s_blocksize, -1); + } + + if (is_mounted (devname)) { + reiserfs_close(fs); + return resize_fs_online(devname, block_count_new); + } + + if (rs_state(rs) != REISERFS_VALID_FS) + die ("%s: the file system isn't in valid state\n", argv[0]); + + if(!valid_offset(fs->s_dev, (loff_t) block_count_new * fs->s_blocksize - 1)) + die ("%s: %s too small", argv[0], devname); + + sb_old = 0; /* Needed to keep idiot compiler from issuing false warning */ + /* save SB for reporting */ + if(opt_verbose) { + sb_old = getmem(SB_SIZE); + memcpy(sb_old, SB_DISK_SUPER_BLOCK(fs), SB_SIZE); + } + + if (block_count_new == SB_BLOCK_COUNT(fs)) + die ("%s: Calculated fs size is the same as the previous one.", argv[0]); + + if (block_count_new > SB_BLOCK_COUNT(fs)) + expand_fs(fs, block_count_new); + else + shrink_fs(fs, block_count_new); + + if(opt_verbose) { + sb_report(rs, sb_old); + freemem(sb_old); + } + + set_state (rs, REISERFS_VALID_FS); + bwrite_cond(SB_BUFFER_WITH_SB(fs)); + + if (opt_verbose) { + printf("\nSyncing.."); + fflush(stdout); + } + reiserfs_close (fs); + if (opt_verbose) + printf("done\n"); + + return 0; +} diff --git a/version.h b/version.h new file mode 100644 index 0000000..542da44 --- /dev/null +++ b/version.h @@ -0,0 +1,9 @@ +/* + * Copyright 2000 Hans Reiser + */ + +#define REISERFSPROGS_VERSION "3.x.0j" + +#define print_banner(prog) \ +fprintf (stderr, "\n<-------------%s, 2001------------->\nreiserfsprogs %s\n", \ +prog, REISERFSPROGS_VERSION) |